---
title: "Provision a user"
method: POST
path: "/users"
tags: ["Users"]
---

# Provision a user

`POST /users`

## Request

- Base URL: `https://api.layout.link/v1`
- URL: `https://api.layout.link/v1/users`
- Auth: HTTP bearer

Idempotent on phone number: calling twice returns the same user rather than a duplicate. Mints nothing that can spend.

## Headers

- `Layout-Version` '2026-10-01'
- `Idempotency-Key` string

## Request body

- CreateUserRequest
  - `firstName` string, required: Shown to the person on the consent screen.
  - `lastName` string, required: Shown to the person on the consent screen.
  - `phone` string, required: E.164. The person verifies it themselves.
  - `email` string, email: Optional and unverified. Kept with the provisioning link; never written to the person's own Layout account, never shown to another partner.
  - `label` string: Your own name for this person, shown in the console.
  - `connectDelivery` 'developer': For a number that already has a Layout account: return the consent link instead of Layout texting it.

## Response `201`

Created, or the existing user for this phone number.

- User
  - `id` string, required
  - `status` string, required
  - `next` 'verify' | 'connect' | 'awaiting_user_approval' | 'connected', required: The next step for this person. `verify`: a new number, prove it headlessly. `connect`: an existing Layout account and you asked to deliver the link; send its owner to `connect.connectUrl`. `awaiting_user_approval`: an existing account, and Layout texted the person the approval link. `connected`: already connected, start building.
  - `connect` object: Present only when `next` is `connect`.
    - `connectUrl` string, uri, required
    - `expiresInSec` integer, required
  - `awaitingApproval` object: Present only when `next` is `awaiting_user_approval`.
    - `phoneHint` string
    - `expiresInSec` integer, required
  - `session` object, required: Carries no token and authenticates nothing. Mint a build grant with POST /users/{userId}/grant.
    - `scope` 'build', required
    - `expiresIn` integer, required: Seconds the build session lasts.
  - `handoffUrl` string, uri, required: Where the person claims the account and proves their phone.

## Other responses

- `400`: Invalid input. `unsupported_version`: the Layout-Version header names no version this API answers.
- `401`: Secret not recognised, malformed, or rotated.
- `403`: Application not permitted. Usually production on an unapproved application.
- `409`: This number is already provisioned to another application in your account. `idempotency_conflict`: the Idempotency-Key was already used with a different request. `in_progress`: the first request with that key is still running; retry after `Retry-After`.
- `429`: A per-hour or per-day provisioning ceiling.
- `500`: Layout failed to answer. Retry with backoff.
- `503`: Layout or a dependency could not be reached. Retry with backoff.

---

[API](https://skmtc.dev/layout/apis/layout-partner-api.md) · [All operations](https://skmtc.dev/layout/apis/layout-partner-api/llms.txt) · [OpenAPI document](https://skmtc.dev/layout/apis/layout-partner-api/revisions/7ff4ba01bf56?raw)
