Silicon IAMDocs

Carbons

A Carbon is a human account. Creating one requires a verified email address and a verified phone number, both bound to a single 48-hour signup session.

Signup

Six calls, in order. Every one takes an Idempotency-Key.

CallReturns
POST /api/v1/signup/sessionsA 48-hour session
POST /api/v1/signup/sessions/{session_id}/email already_exists, and a code if false
POST /api/v1/signup/sessions/{session_id}/email/verify verified: true
POST /api/v1/signup/sessions/{session_id}/phone already_exists, and a code if false
POST /api/v1/signup/sessions/{session_id}/phone/verify verified: true
POST /api/v1/signup/sessions/{session_id}/complete The new CarbonSelf

Completion requires both channels verified in that session, and neither may belong to another account. Availability of a candidate handle is a separate public probe: GET /api/v1/carbon-ids/{carbon_id}/availability.

New Carbon IDs accept lowercase a–z, the digits 1–9, _ and -, and are 3–30 characters. Note the absence of 0: immutable legacy IDs containing it remain addressable for login and lookup, but cannot be newly registered. Rejecting it client-side is far kinder than a 422 after somebody has settled on a name.

Completion does not sign the new Carbon in. It returns the profile; the client then runs the ordinary login flow.

Login

Two calls, covered in Authentication. The verification response is the credential pair plus the actor and session ID, and it also sets the iam_session cookie.

The account surface

EndpointNotes
GET/api/v1/me The authenticated Carbon. The ETag is the precondition for the patch.
PATCH/api/v1/me application/merge-patch+json. Requires If-Match.
GET/api/v1/me/sessions Active refresh families
DELETE /api/v1/me/sessions/{session_id} Requires step-up bound to that session
GET/api/v1/me/login-history Retained one year

The profile patch is a JSON Merge Patch. Omitting a key leaves it alone; sending null clears it. That distinction is load-bearing — a client that serialises an absent optional as null will delete the field.

Session revocation and the twelve-hour rule

This exists to stop somebody who has just taken over an account from immediately locking out the real owner. It has a real consequence for interfaces: a freshly signed-in user cannot revoke anything, and the screen should say so up front rather than letting them discover it through a 403.

Revocation also needs a verified-channel step-up token carrying an account.session_revoke assertion bound to the specific session — one prompt per session, by design.

Logout

POST /api/v1/logout revokes the current session by default and propagates to every configured application. mode: "all_sessions" extends that to every device, and then the twelve-hour rule and a account.sessions_revoke_all step-up assertion both apply.

Cookie-authenticated logout additionally requires X-CSRF-Token matching the token bound into the signed session cookie. Bearer-authenticated logout does not.

Finding other Carbons

EndpointNotes
GET/api/v1/carbons/search Fuzzy Carbon-ID suggestions, 0–10 results
POST /api/v1/carbons/resolve/emailExact match on a verified address
POST /api/v1/carbons/resolve/phoneExact match on a verified number

These exist for invitation pickers: you cannot invite a Carbon that does not exist, so resolving first turns a guaranteed 404 into a working autocomplete. Search returns handles only — never contact details.

How contact identities are stored

Normalised email addresses and phone numbers are authenticated-encrypted at the application boundary. Exact lookup and uniqueness use a versioned HMAC blind index, so the server can answer "is this address taken?" without holding a searchable plaintext column.

Raw contact identities, credentials, OTPs and provider records never appear in logs, traces, metrics, error details, audit diffs or webhooks.