# auth.md

This file tells AI agents how to sign up for Railcode on behalf of a user and
deploy their first private app.

Railcode is the secure cloud for internal software: an internal Vercel for your
company. Describe the tool you need; your coding agent builds it, and Railcode
provides hosting, company login, storage, AI, and connected services.

- **API server**: `https://api.railcode.app`
- **Account login**: `https://railcode.app`
- **Website**: `https://railcode.dev`
- **Pricing**: `https://railcode.dev/pricing.md` — plans, limits and rates, in this format
- **Credentials**: bearer access token for signup; personal API token for ongoing use

## 1. Discover

Fetch `GET https://api.railcode.app/api/config`.

For cloud signup, expect `is_cloud: true` and `signup_enabled: true`. Registration
availability does not guarantee permission to create an organization; check that
in step 3. This guide covers cloud signup, not self-hosted provisioning.

All HTTP examples below use the API server. Send JSON request bodies with
`Content-Type: application/json`. No browser or cookies are required.

## 2. Pick a Method

Railcode supports email-and-password registration with email verification.

| Agent has | Method |
| --- | --- |
| An inbox it can read | Register → verify email → create or join an organization |
| Existing account credentials | Log in → inspect account → resume incomplete steps |
| No readable inbox or existing credentials | Obtain access to an inbox or account before continuing |

Use the intended account's email and display name. Generate a unique password of
at least eight characters and save it securely before registering. Keep passwords,
verification codes, and tokens out of project files and user-facing output.

This creates an ordinary user account. An account currently belongs to one
organization; creating an organization makes that account its owner.

## 3. Register

### Step 1: Create the account

```http
POST /api/auth/register HTTP/1.1
Host: api.railcode.app
Content-Type: application/json

{
  "email": "<inbox-address>",
  "password": "<generated-password>",
  "name": "<display-name>"
}
```

Returns `201` with `access_token`, `token_type: "bearer"`, and `user`.
Initially, `user.email_verified` is false and `user.organization_uuid` is null.
Registration sends a verification email; it does not create an organization.

Use `Authorization: Bearer <access_token>` for every authenticated request below.

**Existing account or interrupted registration:** call
`POST /api/auth/login` with `{"email":"<email>","password":"<saved-password>"}`.
It returns `200` with the same token/user envelope. Then call
`GET /api/auth/me`: skip verification if `email_verified` is true, and skip
organization setup if `organization_uuid` is already set.

### Step 2: Verify the email

Read the new email with subject `Your verification code` using your inbox tool.
Match the recipient and current signup attempt. Keep the six-digit code as a
string so leading zeros survive.

```http
POST /api/auth/verify-email HTTP/1.1
Host: api.railcode.app
Authorization: Bearer <access_token>
Content-Type: application/json

{"code": "<six-digit-code>"}
```

Returns `200` with the user object and `email_verified: true`. Continue using the
same access token; verification does not return a replacement token.

If needed, call authenticated `POST /api/auth/resend-verification` with no body.
A resend replaces the previous code. Defaults: 15-minute code lifetime,
60-second resend cooldown, five invalid attempts. Honor `429` and `Retry-After`;
use the newest email rather than guessing codes.

### Step 3: Create or join an organization

Fetch authenticated `GET /api/onboarding/options`. It returns:

```json
{
  "invites": [],
  "domain_matches": [],
  "can_create_org": true
}
```

**Create:** choose an organization name and a globally unique slug: 4–63 lowercase
letters, numbers, or internal hyphens. Check
`GET /api/organizations/slug-available?slug=<desired-slug>` with authentication;
it returns the normalized `slug` and `available` flag. Then:

```http
POST /api/organizations HTTP/1.1
Host: api.railcode.app
Authorization: Bearer <access_token>
Content-Type: application/json

{"name": "<organization-name>", "slug": "<available-slug>"}
```

Returns `201` with the organization, including `uuid`, `name`, and `slug`.
If `can_create_org` is false, report that creation requires approval. The account
can still register and verify; do not claim organization setup is complete.

**Join:** if the user intends to join an organization returned under `invites` or
`domain_matches`, call authenticated `POST /api/onboarding/join` with
`{"organization_uuid":"<eligible-organization-uuid>"}`. It returns `200` with the
organization. Each option contains `organization.uuid`, `name`, and `slug`;
invites supply `role`, domain matches supply `default_role`. An invite's role wins
when both match. Resolve an ambiguous organization choice before joining.

**Confirm:** fetch `GET /api/auth/me` again. Signup is complete when
`email_verified` is true and `organization_uuid` matches the intended organization.
Keep the organization UUID and slug for subsequent requests.

## 4. Use the Credential

For terminal-based agents, use the existing setup-token exchange to configure the
CLI without browser approval. First check Node.js 20+ and install:

```sh
node --version
npm install -g railcode@latest
npx --yes skills add Railcode-HQ/railcode-skills
railcode --version
```

Install the skills for the coding-agent tools the user wants to use. Read the
installed `create-railcode-app` skill before building.

Mint a setup token immediately before login:

```http
POST /api/auth/setup-token HTTP/1.1
Host: api.railcode.app
Authorization: Bearer <access_token>
```

Returns `201` with `setup_token`, `token_prefix`, `expires_at`, and
`expires_in_seconds`. The account must already belong to an organization.

```sh
railcode login --api-url https://api.railcode.app --setup-token <setup_token>
```

The token is single-use and normally expires in ten minutes. The CLI exchanges
it for a personal API token and saves credentials and org selection in
`~/.railcode/config.json`. Do not print that file. Preserve another account's CLI
setup before replacing it. If the setup token expires, mint a fresh one through
the API; no dashboard visit is needed.

**API-only alternative:** call authenticated `POST /api/auth/api-token` with
`{"name":"Agent onboarding"}`. The `201` response contains `token`, shown once.
Store it securely and use `Authorization: Bearer <token>` for subsequent API calls.
For example, verify the credential with `GET /api/auth/me`.

On `401` from an access token, log in again with saved credentials and inspect the
account state. Do not restart registration or create another organization.

## 5. Get Rolling

### What to tell the user

Carry the same context as the human onboarding into your handoff:

| Railcode provides | What it means for the first app |
| --- | --- |
| Company login and app access controls | Apps are internal. A URL alone does not grant access. |
| App database and file storage | Store records and uploads without provisioning infrastructure. |
| Managed LLM gateway | Add AI without first supplying a provider key, within available allowance. |
| Transactional email | Send app emails without configuring another mail provider. This is not a signup inbox. |
| Governed connectors and database connections | Use authorized company tools and data, with permissions and audit logs. |

New apps have a frontend and backend worker deployed together. Use
`@railcode/sdk` from the worker and implement business permissions there using
verified identity. Follow the installed skill for implementation details.

### Deploy the welcome app

Check `/api/auth/me` for `app:create` and `app:deploy` capabilities. Viewer signup
ends with membership and access to permitted apps; skip deployment for viewers.

Unless the user requested a specific first app, deploy the default welcome
scaffold. It introduces the live URL, edits with a coding agent, app data, AI,
connectors, email, and file uploads.

In a new project directory, with pnpm installed:

```sh
railcode init <name>-onboarding
cd <name>-onboarding
pnpm install
railcode deploy --private
railcode apps show <name>-onboarding --json
```

If pnpm is missing, install it with `npm install -g pnpm`. Wait for deployment
success and return the actual URL printed by the CLI. Verify the app's deployed
state and private access mode; distinguish deployment from browser testing.

Private access avoids sharing with the entire organization. The app owner,
authorized org administrators, and applicable explicit grants can still allow
access. A different human account needs its own membership and appropriate app
access. CLI login does not sign a fresh browser in.

### Connect data and invite teammates

Discover resources relevant to the user's work:

```sh
railcode connector list
railcode connector catalog --json
railcode connections list
railcode query list
```

Use the skills to link authorized services or reuse existing connectors and saved
queries. External OAuth consent may need the account holder; report pending
connections and continue independent setup. Connections are optional for signup.

When the user has authorized invitations and supplied recipients, use
`POST /api/organizations/<org_uuid>/invites` with
`{"email":"<teammate-email>","role":"viewer"}` and bearer authentication.
It requires `invite:manage`, returns `201`, and attempts to send an invitation
email. Viewer is the human onboarding default. Teammates receive their own
accounts; an invitation does not grant access to a private app.

### Optional survey

Submit known customer answers to authenticated `POST /api/onboarding/survey`.
It returns `204` with no body. Only `path` is required when submitting:

```json
{"path": "create"}
```

Optional fields: `heard_from`, `heard_from_detail`, `job_role`, `company_size`,
and boolean `is_technical`. They describe the customer, not the agent. Omit
unknown answers. For `heard_from`, use `Search engine`, `Friend or colleague`,
`Twitter`, `LinkedIn`, `Blog or article`, `ChatGPT`, `Claude`, or `Other` with a
truthful detail. Agent-assisted signup does not itself establish acquisition source.
For a joiner, use `path: "join"` and only a known `is_technical` answer, matching
the shorter human onboarding. Survey submission saves answers, not browser
walkthrough progress; there is no separate API completion step.

### Credits and next steps

Read `GET /api/organizations/<org_uuid>/onboarding/credits` for current reward
amounts and earned milestones: org creation, first deploy, first connection, and
first invite. Rewards are shared, one-time per organization, and spendable on
managed LLM usage in the UTC month earned. The lifetime earned total is not the
remaining balance; use `GET /api/organizations/<org_uuid>/llm/config` for current
allowance. The survey earns no reward.

Finish with the organization launcher (`https://<org_slug>.railcode.app`), the
private app's URL, its local directory, and any pending setup. Suggest a few
useful next tools grounded in the user's work—for example, a sales CRM that turns
meeting notes into action items and tracks email once those services are linked.

## 6. Errors

| Error | Action |
| --- | --- |
| `400`, email already registered | Log in with saved credentials and resume from `/api/auth/me`. |
| `400`, invalid or expired code | Check the latest email; request a fresh code when needed. |
| `401`, expired access token | Log in again; retain the existing account and organization. |
| `401`, setup token rejected | Mint a new setup token; it may have expired or already been used. |
| `403`, verify email first | Complete email verification before creating or joining an org. |
| `403`, creation requires approval | Report the gate; join only an eligible, intended organization. |
| `403`, plan or capability limit | Report the specific limit; do not repeatedly retry unchanged. |
| `409`, slug taken | Choose an available slug; after a lost response, check membership first. |
| `422` | Correct the field identified in the validation error. |
| `429` | Honor `Retry-After` when present and the response's recovery instructions. |
| Network error or `5xx` | Retry reads with bounded backoff; inspect state before repeating writes. |
| Deploy conflict | Inspect the current app version; do not overwrite it with `--force`. |

## 7. Detailed Skills and Links

- [Railcode skills](https://github.com/Railcode-HQ/railcode-skills): install with
  the command above, then use `create-railcode-app` for building and deployment,
  `create-railcode-agent` for managed agents, and `manage-railcode-org` for org setup.
- [Railcode](https://railcode.dev)
- [Pricing](https://railcode.dev/pricing.md): what each plan includes, what is metered
  and what it costs past the allowance. The page it mirrors is at
  [railcode.dev/pricing](https://railcode.dev/pricing).
- [Account login](https://railcode.app)
