> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fourdos.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a user

> Add a member to your organization - idempotently, with safe retries

This walkthrough creates a member of your organization, proves the idempotent
retry behavior, and tours the rest of the membership lifecycle. You need your
`org_` API key ([Quickstart](/quickstart)).

```bash theme={null}
export API_BASE_URL="https://api.develop.fourdos.dev"
export API_KEY="org_…"
```

## 1. Create (or attach)

`POST /v1/users` is a **create-or-attach**: if the email has no 4D account, a
user and a membership in your org are created; if the person already exists
on the platform, they're attached to your org as a member instead - never
duplicated. Only `email` is required.

```bash theme={null}
curl -X POST "$API_BASE_URL/v1/users" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: create-ada-001" \
  -d '{
    "email": "ada.lovelace@example.com",
    "external_id": "cust-001",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "display_name": "Ada L.",
    "country": "GB",
    "dob": "1990-12-10"
  }'
```

`201 Created` (or `200 OK` when the user/membership already existed and is
returned unchanged):

```json theme={null}
{
  "id": "0198f2b1-…",
  "email": "ada.lovelace@example.com",
  "display_name": "Ada L.",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "country": "GB",
  "age_band": "adult",
  "dob_verification_status": "unverified",
  "last_active_at": null,
  "status": "active",
  "wallet": { "status": "provisioning", "address": null },
  "membership": {
    "id": "…",
    "external_id": "cust-001",
    "role": "member",
    "status": "active",
    "joined_at": "2026-07-17T00:00:00.000Z"
  }
}
```

Notes on the shape:

* `age_band` is derived from `dob` + `country`; the raw `dob` you submitted is
  not echoed back - partner reads get the redacted profile
  (see [Tenants and members](/concepts/tenants-and-members)).
* `wallet` starts `provisioning` and becomes `active` with an address once the
  background provisioning completes - no action needed from you.
* `country` must be an assigned ISO 3166-1 alpha-2 code (any case in, stored
  uppercase); `dob` must be a real calendar date, in the past, year ≥ 1900.

## 2. Retry it - nothing duplicates

Run the exact same command again. Same `Idempotency-Key`, same body → the
original response is replayed with `200`. A different body under the same key
is rejected with `409 IDEMPOTENCY_CONFLICT`. Reusing an `external_id` already
claimed by another member of your org is also a `409`.

## 3. The rest of the lifecycle

```bash theme={null}
# Fetch (within your org)
curl "$API_BASE_URL/v1/users/$USER_ID" -H "Authorization: Bearer $API_KEY"

# Update profile fields
curl -X PATCH "$API_BASE_URL/v1/users/$USER_ID" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "display_name": "Countess of Lovelace" }'

# Lock the membership (reason required) / unlock it
curl -X POST "$API_BASE_URL/v1/users/$USER_ID/lock" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "reason": "payment dispute" }'
curl -X POST "$API_BASE_URL/v1/users/$USER_ID/unlock" \
  -H "Authorization: Bearer $API_KEY"

# Remove (soft-delete) the membership - the person's 4D account survives
curl -X DELETE "$API_BASE_URL/v1/users/$USER_ID/membership" \
  -H "Authorization: Bearer $API_KEY"
```

Locking and removal act on **your membership**, not the person's global
account: a removed member keeps their 4D identity (and any other
organizations' memberships) intact.

## Next

* [Get user details](/walkthroughs/get-user-details) - reads, updates, and
  typed attributes.
* [Trigger an event](/walkthroughs/trigger-an-event) - start tracking what
  this member does.
* Bringing thousands of existing members?
  [Migrate an existing member base](/walkthroughs/migrate-existing-members)
  runs this same create-or-attach path in bulk.
