User account (/me)
Self-service endpoints for the signed-in User (the account, across all their orgs — see Connected ID). Base path: /api/v1/users.
Registration (public)
Section titled “Registration (public)”| Method · Route | Purpose |
|---|---|
POST /registration/email | Register with email. Body: email, password, username?, phoneNumber?, countryCode?. 400 with message on failure. |
POST /verification/email | Confirm the emailed token and start a session. Body: userId, token. |
My profile (authenticated)
Section titled “My profile (authenticated)”| Method · Route | Purpose |
|---|---|
GET /me | The signed-in account: name, email, isEmailVerified, firstName, lastName, countryIso, userId, organizations[], currentOrganization, credentialIds[], isCurrentSessionSealed, profile, and signatureSpec (the user’s designed personal seal, rendered across the approval flow). The answer is cached for 5 minutes per user; add any ?_t= query value to read fresh data. |
GET /me/org-count | { count } — how many organizations the account belongs to. |
PUT /me/profile | Update the profile. Body (all optional): firstName, lastName, bio, location, websiteUrl, preferredLanguage, preferredCurrency, timeZone, profileImageUrl, countryIso. For an account that belongs to an organization, the request must come from a sealed device. |
PUT /me/timezone | Set only the time zone. Body { timeZone } (2–64 characters). |
PUT /me/language | Set only the display language. Body { language } (2–10 letters, digits or -). Returns { language, changed }. |
POST /me/profile/photo | Upload an avatar as multipart/form-data, field file. PNG, JPEG, WebP or GIF, at most 5 MB. Returns { url }. For an account that belongs to an organization, the request must come from a sealed device. |
GET /me/phone | The account’s phone number and whether it is verified. |
POST /me/korean-identity/initiate | Start Korean real-name verification. Body { purpose? }. The owner’s own verification (no purpose, or owner-kyc) is free; any other purpose inside an organization needs the Korean Package add-on, otherwise 402 with error = "ADDON_REQUIRED". |
POST /me/korean-identity/finalize | Finish it. Body { identityVerificationId, purpose? }. |
POST /api/v1/identity/kyc/start | Start a Stripe Identity check (document + matching selfie). Body { returnUrl }; answers { url } (send the person there). Starting again clears the previous failure reason. 409 with error = "already_verified" once the account is verified. |
GET /api/v1/identity/kyc/status | The signed-in person’s own identity check: status (None · Pending · Verified · RequiresInput · Canceled), isVerified, reason (Stripe’s reason, only for RequiresInput), verifiedAt, canRestart (true until verified). |
My devices (authenticated)
Section titled “My devices (authenticated)”| Method · Route | Purpose |
|---|---|
GET /my-devices | List the account’s devices. Send the current device in X-Device-Id to have it marked. |
DELETE /my-devices/{deviceId} | Remove a device key (account-wide). 404 if unknown; 409 if removing it would lock the account out. |
POST /my-devices/{deviceId}/claim | Claim a device. |
POST /my-devices/{deviceId}/lock | Lock or unlock a device. Body { locked }. 404 if unknown; 409 if it would lock the account out. |
POST /orgs/{slug}/device-request | Request device access in an org for the device of the current session. |
Removing, claiming and locking a device must be done from a sealed device. These are the account-level view; the org-scoped device view is Devices, and the conceptual model is Devices.
Deleting the account
Section titled “Deleting the account”The signed-in user deletes their own Axowl account. Deleting an account never deletes an organization: delete (or transfer) every organization you own and leave every other organization first. Public explanation for your users: axowl.com/account/delete.
| Method · Route | Purpose |
|---|---|
GET /me/account/deletion | Preconditions: canRequest, status, hasPasskey, deletionRequestedAt, purgeAt, graceDays, blockers[{ code, orgSlug, orgName }], warnings[{ code, count }]. Blocker codes: OWNER_ORG, OWNER_ORG_PENDING_PURGE, MEMBER_ORG, ACCOUNT_STATUS. Warning APP_ENTITLEMENTS: paid app subscriptions linked to the account are not canceled by deletion. |
POST /me/account/deletion/email-code | Send a 6-digit confirmation code to the account’s email (for accounts without a passkey). The code is valid for 10 minutes and allows five attempts. One send per minute, five per ten minutes (429 CODE_THROTTLED); 502 CODE_SEND_FAILED if the email could not be sent. |
POST /me/account/deletion | Request deletion. Body { stepUpAssertion } (passkey seal) or { emailCode }. 409 DELETION_BLOCKED with blockers; 401 VERIFICATION_REQUIRED · STEPUP_FAILED · CODE_INVALID. On success every session and access token ends, and the answer carries requested, deletionRequestedAt, purgeAt and graceDays. |
POST /api/auth/account-recovery/send | Public. Body { email }. Sends a restore code if that account is pending deletion. The answer is { accepted: true } for any email. |
POST /api/auth/account-recovery/confirm | Public. Body { email, code }. Restores the account ({ restored: true }); 401 CODE_INVALID otherwise. |
While deletion is pending, every sign-in answers ACCOUNT_PENDING_DELETION. After 30 days the account is anonymized (the job runs hourly, so it happens within about an hour of the 30-day mark): name, email, phone number, passkeys, linked sign-in providers, trusted devices and identity-verification records are removed. Audit records stay in anonymized form. An email is sent when deletion is requested, when it is restored, and when it is completed.