---
title: Build an app with sign-in and AI
description: Build and deploy one complete app with sign-in, private notes, and AI answers using the plan's included allowance and no provider key.
slug: /guides/build-an-app-with-sign-in-and-ai
order: 40
visibility: public
redirects_from: []
---

# Build an app with sign-in and AI

Prompt:

> Build a web app where people sign in, keep their own notes, and ask an AI questions. I don't have an AI provider key.

This worked example uses the complete [web-app source](/cloud/examples/web-app/). The browser signs people in, the backend verifies each session, and the signed-in user can ask an AI a question. Each question stands alone; the example sends no earlier questions to the model.

At a glance:

- **Example:** `web-app`, with all its files on one [source page](/cloud/examples/web-app/).
- **Sign-in:** `/__account/signin`; the Account package verifies the session in `src/session.mjs`.
- **AI route:** `POST /api/ask`; `src/ai.mjs` calls the AI Allowance package after the server verifies the user.
- **Server settings:** `TURNZERO_CLOUD_API`, `TURNZERO_CLOUD_APP_TOKEN`, `TURNZERO_CLOUD_REALM_KEYS`, and `TURNZERO_CLOUD_GATEWAY_URL` where set.
- **Packages:** Account, AI Allowance, and Database. The example includes the database so each user can keep private notes.

## What your AI tool does

Your AI tool starts from the complete example, creates the application, submits its manifest, and deploys it. It checks that an anonymous request cannot reach the AI route and that a signed-in user can get an answer. It reports which checks it completed.

## What you need

- A [connected AI coding tool](/cloud/getting-started/connect-your-tool/).
- A plan with room for the application and an available AI allowance. Your AI tool reads the allowance; the creation call refuses a plan that is full.
- A sign-in method to try, such as an emailed code, Google, or GitHub.

You need no separate AI provider account or key.

## Before your AI starts

Read the `deploy`, `add-sign-in`, and `use-ai-allowance` skills through `read_context`. Keep their approval and verification rules. Read the complete [web-app example](/cloud/examples/web-app/), following the response's continuation fields if it arrives in parts.

With a shell, check that the turnzero command is installed and this computer is authorized, as [Connect your AI coding tool](/cloud/getting-started/connect-your-tool/) describes. Without a usable command, use the file route below.

## Steps

### 1. Create the application

Read `read_plan_quotas` for the plans and their AI allowances. Use the plan the person named, or ask them to choose one. With nobody to ask, use Free and report that choice. Call `create_application` without a separate capacity check, and keep its identifier. If the chosen plan is full, follow [Create the application](/cloud/getting-started/build-an-app-from-a-chat/#2-create-the-application). A new application has production only.

### 2. Take the complete example

With an installed, authorized command, run this from an empty project folder, replacing `<application id>` with the identifier from step 1:

```text
turnzero scaffold --template web-app --application <application id>
```

The command writes the app in `app/`, takes its three library packages, installs dependencies, and runs the tests. Keep the generated files together.

Without a usable command, read every file on the [web-app source page](/cloud/examples/web-app/), using its application paths. Its text downloads end in `.txt`; remove that suffix when naming a file. Keep `package.json`, the client files, server modules, and migration. Read the tests too, even if your tools cannot run them.

For the file route, read `list_library` and put the current names and versions of `account`, `ai_allowance`, and `database` in the manifest's `packages` list. Keep the example's `file:lib/…` dependencies. Leave the three library folders out of `deploy_files`; Cloud fills them from the submitted manifest. [The manifest's package rows](/cloud/guides/use-the-library/#7-the-rows-in-the-manifest-member) explains this route.

### 3. Follow the sign-in and AI request together

The complete app already connects these parts:

| File | What happens |
| --- | --- |
| `client/index.html` | The Sign in link opens the platform's sign-in page on the app's own hostname. |
| `client/app.js` | The page reads `/__account/me`. After sign-in, its question form sends `{ question }` to `/api/ask`; the browser sends its session cookie. |
| `src/session.mjs` | The Account client verifies the cookie or bearer token and the router's session header. It returns the verified user's id, or no user. |
| `src/server.mjs` | The AI route returns 401 with `sign_in_required` without a verified user. Otherwise it passes the question to `src/ai.mjs`. |
| `src/ai.mjs` | `generateText` sends the question through the platform gateway under the app's credential. The route returns the answer or a named fallback. |

Keep the platform credential in the backend. The browser receives the answer, never that credential. The example's AI call uses the plan's allowance, so it needs no stored provider key or external-host declaration.

### 4. Submit the manifest and deploy

Submit the example's complete manifest with `submit_manifest`. It declares the database and accounts services. Use the application identifier from step 1. For the file route, include the current package rows you prepared in step 2; the source template's empty list is a starting point.

Check `read_realm` and keep the sign-in methods the person chose. Follow [Deploy from your computer](/cloud/getting-started/deploy-an-application/) with a usable command, or [Deploy from a chat](/cloud/getting-started/build-an-app-from-a-chat/) with files. Run the required deployment checks before starting the deploy, then read status until it ends.

With a shell, run the app's tests again after any code change. Without one, report that local tests were not run. File checks and a successful health check do not verify sign-in or an AI answer.

### 5. Verify the combined flow

Check the deployed home page and `/health`. An anonymous request to `/api/ask` must return 401 with `sign_in_required`.

Open the app's deployed address, choose Sign in, and complete the selected method. On returning to the app, the page shows the signed-in user and the question form. Ask, “What are three ways to organize my notes?” The page should display an AI answer. Sign out and confirm that the signed-in controls disappear.

For an automated run, use [Test sign-in with test end users](/cloud/guides/test-sign-in-with-test-end-users/), including the approvals required by the chosen environment and tools. Check a signed-in `POST /api/ask` as well as the anonymous refusal. Ask before writing test data to production, and remove temporary users and data after the test.

Use the example name `web-app sign-in and AI` when reporting a test. Record the environment, whether browser sign-in completed, whether the protected route refused an anonymous request, and whether a signed-in question received an answer. State any untested part explicitly.

## Expected result

One deployed app lets a person sign in, keep private notes, and ask an AI a question. Anonymous requests cannot use its AI route. The source and tests stay in the project, and the verification report distinguishes local tests from checks on the deployed app.

## Refusals

These are the example's common failures. The [Account](/cloud/backend-packages/account/), [AI Allowance](/cloud/backend-packages/ai-allowance/), and [platform refusal reference](/cloud/reference/refusals/) cover the rest.

| Code | Cause | Remedy |
| --- | --- | --- |
| `sign_in_required` | The app received no verified session. | Sign in on the deployed app's hostname, or follow the test-user guide for an automated request. This response is expected in the anonymous check. |
| `library_entry_not_served` | A package row names a version the library cannot fill. | Read `list_library` again, correct the row, and resubmit the manifest before retrying the file deployment. |
| `allowance_drawn` | The example caught `allowance_exhausted`: the month's AI allowance is used up. | Keep the fallback and wait for the allowance reset, or choose another approved source of AI answers. Do not retry the exhausted allowance. |
| `allowance_unavailable` | The platform cannot serve the included allowance now. | Keep the fallback and report the failure if it persists. |
| `upstream_quota` | The model provider is out of capacity. | Keep the fallback and try later. |

## Related

- [web-app example](/cloud/examples/web-app/) holds the complete app and its tests.
- [Add sign-in to your app](/cloud/guides/add-sign-in-to-your-app/) explains session verification and the sign-in methods.
- [Call an AI model under the allowance](/cloud/guides/call-an-ai-model-under-the-allowance/) explains the AI request, conversation history, and fallbacks.
- [Test your application locally](/cloud/guides/test-your-application-locally/) explains tests over the package doubles.
