> For the complete documentation index, see [llms.txt](https://docs.robotmoney.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.robotmoney.com/api-reference/onboarding-api.md).

# Onboarding

Self-serve tenant signup, email verification, owner login, and wallet linking. These routes create the owner session used to configure policies, counterparties, headless limits, and tenant-created agents. Public signup routes are gated behind the `BRAIN_SELF_SERVE_SIGNUP` environment flag. With the flag off, `/signup` and `/auth/verify-email` return `404`.

| Operation              | Endpoint                               | Auth                       |
| ---------------------- | -------------------------------------- | -------------------------- |
| Sign up a new tenant   | `POST /v1/signup`                      | Public (rate-limited)      |
| Verify the owner email | `POST /v1/auth/verify-email`           | Public (rate-limited)      |
| Password login         | `POST /v1/auth/login`                  | Public (rate-limited)      |
| Link a wallet          | `POST /v1/tenants/{tenant_id}/wallets` | Owner JWT + `policy:write` |

For the conceptual walkthrough, see [Sign Up and Onboard](/platform/sign-up-and-onboard.md). For the underlying error codes, see the [self-serve onboarding section](/resources/errors.md#self-serve-onboarding) of the errors reference.

### Sign Up

Provisions a new tenant + owner user and either emails a verification token in production or returns it directly for sandbox deploy targets such as staging.

```http
POST /v1/signup
Content-Type: application/json

{
  "email":    "owner@acme.com",
  "password": "a-strong-passphrase"
}
```

`password` is 12–4096 bytes and is stored as a scrypt hash (`shared/src/auth/password.ts`). The route returns `201 Created`:

```json
{
  "tenant_id": "tnt_01J0000000000000000000000A",
  "user_id": "usr_01J0000000000000000000000B",
  "status": "pending",
  "verification_token": "vtok_..."
}
```

The response carries exactly one of these fields, never both. `verification_token` is included when the deploy target is a sandbox target such as staging. Production sends the token through the configured ESP client (`EMAIL_ENDPOINT`, `EMAIL_API_KEY`, optional `EMAIL_FROM`) and returns `verification_sent: true` in place of the token. If self-serve signup is enabled in production without ESP credentials, API boot fails before the route is served. Errors: `400` (validation), `409` (`signup_email_taken`), `429`.

### Verify Email

```http
POST /v1/auth/verify-email
Content-Type: application/json

{
  "tenant_id": "tnt_01J0000000000000000000000A",
  "token":     "vtok_..."
}
```

```json
{
  "verified": true,
  "user_id": "usr_01J0000000000000000000000B",
  "status": "active"
}
```

Errors: `400` (`signup_token_invalid`. Invalid, expired, or already used), `429`.

### Password Login

Issues a 15-minute owner JWT. The same `401` is returned for an unknown email and a wrong password (no user enumeration); `403` if the owner email is unverified.

```http
POST /v1/auth/login
Content-Type: application/json

{
  "email":    "owner@acme.com",
  "password": "a-strong-passphrase"
}
```

```json
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 900,
  "principal": {
    "id": "usr_01J0000000000000000000000B",
    "type": "user",
    "tenantId": "tnt_01J0000000000000000000000A",
    "scopes": [
      "ledger:read",
      "wiki:read",
      "raw:read",
      "raw:write",
      "policy:read",
      "policy:write",
      "audit:read",
      "execution:read",
      "payment_intent:approve",
      "surfaces:admin"
    ]
  }
}
```

{% hint style="warning" %}
The owner JWT manages the tenant and can approve reviewed actions. Tenant agents use `brain_ak_` credentials exchanged for five-minute JWTs with `payment_intent:propose`. Neither owner sessions nor agent exchange tokens carry `payment_intent:execute`; execution remains behind the section 6 gate.
{% endhint %}

Errors: `401` (`auth_invalid_credentials`), `403` (`auth_email_unverified`), `429`.

### Link a Wallet

Once the owner is logged in, they can link a wallet to the tenant. After linking, the same wallet can sign in over SIWX and receive an owner JWT. Wallets are for human or MCP identity. Headless HTTP agents should use the agent-key exchange path instead.

```http
POST /v1/tenants/{tenant_id}/wallets
Authorization: Bearer <owner JWT>
Content-Type: application/json

{
  "address":        "0xabc...",
  "principal_type": "human"
}
```

The body requires `address` and `principal_type` (`"human"` or `"agent"`); there is no `signature` field. A `human` link defaults to the calling owner; an `agent` link must also name `principal_id`. `tenant_id` in the path must equal the JWT's `tenantId`. Returns `201` with the linked wallet record. Errors: `400`, `401`, `403` (tenant mismatch), `409` (`wallet_already_linked`).

### What Comes Next

After login, the tenant typically:

1. Composes and signs a policy via [`POST /v1/policy/{tenant_id}/compose`](/api-reference/policy-api.md) → [`/sign`](/api-reference/policy-api.md).
2. Connects a financial source (Plaid, ERP, wallet) out-of-band and starts ingesting evidence into the [Raw layer](/platform/sources-api.md).
3. Creates a tenant agent via [`POST /v1/agents`](/api-reference/agents-api.md), exchanges the returned `brain_ak_` key at `/token`, and calls [`POST /v1/authorizations`](/govern/authorizations-api.md).
4. Sets headless decision windows with the [Headless Limits API](/govern/headless-limits-api.md).
5. Watches activity via the [Audit API](/api-reference/audit-api.md), [Usage API](/govern/usage-api.md), and [Proof API](/api-reference/proof-api.md).

### What's Next

<table data-view="cards"><thead><tr><th></th><th></th><th data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>🪪 Authentication</strong></td><td>The fuller auth model (JWT, scopes, SIWX).</td><td><a href="/api-reference/authentication.md">Authentication</a></td><td></td></tr><tr><td><strong>🚀 Sign Up and Onboard</strong></td><td>The narrative quickstart.</td><td><a href="/platform/sign-up-and-onboard.md">Sign Up and Onboard</a></td><td></td></tr></tbody></table>
