# UserKit > Accounts, organizations and the customer plane — the API and the panel that consumes it. ## Docs - [Authentication](https://docs.userkit.dev/en/api-reference/authentication.md): Which credential opens which endpoint, and what happens when you bring the wrong one. - [Change the password](https://docs.userkit.dev/en/api-reference/endpoints/account/change-the-password.md): Requires the current password — a hijacked session must not be enough to take the account over. Every *other* device is signed out; the caller's own session survives. - [List the account's sessions](https://docs.userkit.dev/en/api-reference/endpoints/account/list-the-accounts-sessions.md): Where the account is signed in. Token hashes are never part of it — this is a list of sessions, not of credentials. - [Read the account](https://docs.userkit.dev/en/api-reference/endpoints/account/read-the-account.md) - [Sign an avatar upload](https://docs.userkit.dev/en/api-reference/endpoints/account/sign-an-avatar-upload.md): Returns a presigned PUT. The browser uploads straight to the bucket — bytes never pass through this server — and then saves the returned `public_url` with `PATCH /v1/account`. Content type and size are part of the signature, so the bucket enforces them. - [Sign out everywhere else](https://docs.userkit.dev/en/api-reference/endpoints/account/sign-out-everywhere-else.md): Keeps the session that asked, so the caller is not logged out of the tab they clicked in. - [Update the profile](https://docs.userkit.dev/en/api-reference/endpoints/account/update-the-profile.md): Absent and empty are different requests: a form that only edits the name must not wipe the avatar, so omit what you are not changing. - [Create an API key](https://docs.userkit.dev/en/api-reference/endpoints/api-keys/create-an-api-key.md): Requires `api_keys:write`. Returns the key in plaintext — **the only time it is ever readable**. A lost key is replaced, never recovered. - [List API keys](https://docs.userkit.dev/en/api-reference/endpoints/api-keys/list-api-keys.md): Requires `api_keys:read`. Only the display prefix comes back — the key itself was shown once, at creation, and only its hash is stored. - [List publishable keys](https://docs.userkit.dev/en/api-reference/endpoints/api-keys/list-publishable-keys.md): Requires `api_keys:read`. One per environment, seeded with it, returned in plaintext — the value is public by definition (it sits in every page's HTML) and the panel is where a developer copies it from. - [Revoke an API key](https://docs.userkit.dev/en/api-reference/endpoints/api-keys/revoke-an-api-key.md): Requires `api_keys:write`. The row survives with `revoked_at` stamped, so the audit trail of which key existed does not disappear with it. - [Set the allowed origins](https://docs.userkit.dev/en/api-reference/endpoints/api-keys/set-the-allowed-origins.md): Requires `api_keys:write`. This list is the actual security boundary of a key that is public by design. - [Complete a two-factor sign-in](https://docs.userkit.dev/en/api-reference/endpoints/authentication/complete-a-two-factor-sign-in.md): Exchanges the challenge token plus a TOTP or recovery code for a real session. A wrong code does not consume the challenge — the rate limit is what bounds guessing. A recovery code is single-use and mails the account's owner. - [Create an account](https://docs.userkit.dev/en/api-reference/endpoints/authentication/create-an-account.md): Bootstraps user, organization and owner membership in one transaction, then signs the user in — the session is minted inside the same transaction, so a sign-up either lands complete or not at all. - [Request a password reset](https://docs.userkit.dev/en/api-reference/endpoints/authentication/request-a-password-reset.md): Always answers 202, whether or not the address has an account — the response must not reveal which. A new request voids any older link. The token is valid for one hour. - [Reset a password with a token](https://docs.userkit.dev/en/api-reference/endpoints/authentication/reset-a-password-with-a-token.md): Sets the new password and signs every session of that account out — whoever reset it gets back in with the new password, everyone else is thrown out. A notice email goes to the address, which is what exposes a reset the owner did not ask for. - [Sign in](https://docs.userkit.dev/en/api-reference/endpoints/authentication/sign-in.md): Answers a session, or — when the account has two-factor on — a short-lived challenge to exchange at `/v1/auth/two-factor`. - [List contacts](https://docs.userkit.dev/en/api-reference/endpoints/contacts-panel/list-contacts.md): Requires `customers:read`. Staff are authorized for the whole organization, so `?environment=` is a legitimate **view** parameter here — the opposite posture from the machine surface, where the credential decides and no parameter may. - [Merge two contacts](https://docs.userkit.dev/en/api-reference/endpoints/contacts-panel/merge-two-contacts.md): Requires `customers:write`. Folds a duplicate into the contact that should survive. - [One contact's authentication history](https://docs.userkit.dev/en/api-reference/endpoints/contacts-panel/one-contacts-authentication-history.md): Requires `customers:read`. What happened to this account: every sign-in, how it happened and where from, newest first (100 most recent). - [Read a contact](https://docs.userkit.dev/en/api-reference/endpoints/contacts-panel/read-a-contact.md): Requires `customers:read`. Returns the contact plus its identity edges. - [Read active-contact usage](https://docs.userkit.dev/en/api-reference/endpoints/contacts-panel/read-active-contact-usage.md): Requires `customers:read`. Active contacts this calendar month, the free-tier ceiling, and when the meter resets. - [Identify a contact](https://docs.userkit.dev/en/api-reference/endpoints/contacts-server/identify-a-contact.md): Server-side identity: create or update a contact from whatever identities the developer's backend knows. At least one of `external_id`, `email` or `anonymous_id` is required. - [Identify the API key](https://docs.userkit.dev/en/api-reference/endpoints/contacts-server/identify-the-api-key.md): The first call an integrator makes to check that their key works. The environment reported here is the one the **key** belongs to — no header or parameter can name a different one, which is what makes test data unreachable with a live key and vice versa. - [List contacts](https://docs.userkit.dev/en/api-reference/endpoints/contacts-server/list-contacts.md): Scoped to the key's environment. There is no environment parameter on this surface, by design. - [Read a contact](https://docs.userkit.dev/en/api-reference/endpoints/contacts-server/read-a-contact.md): The key's environment scopes the read: a live id asked with a test key matches no row, and the 404 does not reveal whether the row exists elsewhere. - [Boot the widget](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/boot-the-widget.md): The widget's first call, from the developer's own page. Two shapes share it. - [Disable two-factor](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/disable-two-factor.md): Requires a code that currently works — a TOTP code or a recovery code. - [Enable two-factor](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/enable-two-factor.md): Turns two-factor on and returns the recovery codes — **the only time they are ever shown**. - [Fetch an environment's public keys](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/fetch-an-environments-public-keys.md): The public half of the verification path: the JWKS document your backend checks session JWTs against. - [List the contact's sessions](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/list-the-contacts-sessions.md): Where this contact is signed in — the same "my devices" screen staff get, one layer down. The token hash is never part of it. - [Mint a session JWT](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/mint-a-session-jwt.md): Exchanges the opaque `uk_ct_…` session for a short-lived, signed JWT that your backend can verify **offline** against the environment's JWKS document (`GET /v1/jwks/{publishableKey}`) — no call back to this API on every request. - [Read the current contact](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/read-the-current-contact.md): The customer plane's identity read: who am I, in this product. A contact session reads its own contact and nothing else — that scope *is* the whole authorization model of `uk_ct_`. - [Redeem a magic link](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/redeem-a-magic-link.md): Turns a one-time token into a contact session. The redeem is atomic in SQL, so the link works exactly once, and clicking it is what proves the email — the contact is marked identified here, and the session it mints is verified. - [Replace the recovery codes](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/replace-the-recovery-codes.md): Mints a fresh set and invalidates every previous one — a partially-known set is worse than a fresh one. Requires a working code, for the same reason disabling does. - [Request a magic link](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/request-a-magic-link.md): Always answers 202: the response never reveals whether the address knows this product. The link is also the only way an email is ever **proven** on the customer plane — in federated mode an email arriving by boot is an attribute, and this flow is what upgrades it. Valid for one hour. - [Request an email sign-in code](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/request-an-email-sign-in-code.md): Mails six digits and hands back the challenge that spends them. The magic link's flow with a different presentation — a code is typed back into the page that asked for it, so it survives a mail client that opens links in its own browser, and it works when the inbox is on a different device from the… - [Revoke one session](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/revoke-one-session.md): Revocation is deletion — immediate, with the 5-minute JWT lifetime as the only tail. - [Sign in with an email code](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/sign-in-with-an-email-code.md): Spends the code and signs the contact in. The code arrived in the inbox, so it proves the address exactly like a magic link does: the contact is marked identified, the address is marked verified, and the session it mints is verified. - [Sign out everywhere else](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/sign-out-everywhere-else.md): Ends every session of this contact except the one that asked, so the device in hand stays signed in. - [Sign the contact out](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/sign-the-contact-out.md): Ends this session. Revocation is deletion — the next lookup simply matches nothing. - [Start two-factor setup](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/start-two-factor-setup.md): Generates a TOTP secret for the signed-in contact and returns what an authenticator app needs. It does **not** turn two-factor on: that happens at `enable`, once a code proves the secret was stored correctly, so an abandoned setup never locks anyone out. - [What this environment supports](https://docs.userkit.dev/en/api-reference/endpoints/customer-plane/what-this-environment-supports.md): What a sign-in screen has to know before it can render itself: whether the environment is hosted or federated, and which social providers it has applications for. - [List social providers](https://docs.userkit.dev/en/api-reference/endpoints/environments/list-social-providers.md): Requires `api_keys:read`. One entry per provider this build speaks to, configured or not — the screen is a list of what is possible, not a list of what exists. - [List the environments](https://docs.userkit.dev/en/api-reference/endpoints/environments/list-the-environments.md): The organization's live and test environments — what the panel's switcher shows. No permission gate beyond the session: environments are structure, not data. - [Read the identity settings](https://docs.userkit.dev/en/api-reference/endpoints/environments/read-the-identity-settings.md): Requires `api_keys:write` — this secret mints *verified* sessions, so reading it is holding it, and a read gate would be the wrong gate. - [Register a social provider](https://docs.userkit.dev/en/api-reference/endpoints/environments/register-a-social-provider.md): Requires `api_keys:write`. The **OAuth application belongs to the organization**: you register it in the provider's console and paste the credentials here, so the consent screen your customer reads carries your name and not ours. - [Remove a social provider](https://docs.userkit.dev/en/api-reference/endpoints/environments/remove-a-social-provider.md): Requires `api_keys:write`. Existing links survive: they are history, and a developer who registers again tomorrow should not find their customers locked out. - [Rotate the identity secret](https://docs.userkit.dev/en/api-reference/endpoints/environments/rotate-the-identity-secret.md): Requires `api_keys:write`. The previous secret keeps verifying for a 24-hour grace window — long enough for a fleet to redeploy, short enough that a stolen old secret has a deadline. - [Rotate the signing key](https://docs.userkit.dev/en/api-reference/endpoints/environments/rotate-the-signing-key.md): Requires `api_keys:write` — the same gate as the identity secret, because this key mints the tokens a developer's backend trusts. - [Set the environment's auth mode](https://docs.userkit.dev/en/api-reference/endpoints/environments/set-the-environments-auth-mode.md): Requires `organization:update`. The mode is per environment so a developer can evaluate hosted in test while production stays federated. - [Finish a sign-in with the second factor](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/finish-a-sign-in-with-the-second-factor.md): The second half of any sign-in that stopped at the first factor. Accepts a TOTP code or a single-use recovery code. - [Finish a social sign-in](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/finish-a-social-sign-in.md): Spends the state, exchanges the code with the provider, and resolves the identity onto a contact. The response is the hosted login's, so an SDK has one shape to handle however somebody signed in. - [Request a contact password reset](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/request-a-contact-password-reset.md): Hosted mode only. Unconditional 202 — the staff plane's posture mirrored one layer down. It also works for contacts that never had a password: receiving the link proves the email, so the reset doubles as "set a password" for accounts the developer minted. Valid for one hour. - [Resend the verification email](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/resend-the-verification-email.md): Hosted mode only. 202 always, and nothing goes out for unknown addresses or already-verified ones. - [Reset a contact's password](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/reset-a-contacts-password.md): Redeems the reset token: new password, the email counted as proven (the link arrived in it), and **every** session of that contact revoked immediately — a reset usually means somebody else may hold the credential. - [Sign in a contact](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/sign-in-a-contact.md): Hosted mode only. One uniform 401 for unknown email, password-less contact and wrong password alike, with a dummy bcrypt compare keeping the timing uniform too. Proving the password proves the account, so the session is verified. - [Sign up a contact](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/sign-up-a-contact.md): Hosted mode only. Always answers 202 — the response never reveals whether the address already has an account. What the 202 hides: - [Start a social sign-in](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/start-a-social-sign-in.md): Hosted mode only. Stores the in-flight authorization and answers with the URL to send the browser to. - [Verify a contact's email](https://docs.userkit.dev/en/api-reference/endpoints/hosted-auth/verify-a-contacts-email.md): Redeems the verification token: the email is proven, any password waiting on the token attaches, and a verified session comes back — the person clicked from their inbox, do not make them sign in again. The token is valid for 24 hours. - [Accept an invitation](https://docs.userkit.dev/en/api-reference/endpoints/members/accept-an-invitation.md): Public. Joins the invited address to the organization: creating the account when it is new (`name` and `password` required), or just adding the membership when it already exists. - [Change a member's role](https://docs.userkit.dev/en/api-reference/endpoints/members/change-a-members-role.md): Requires `members:write`. You cannot change your own role — that is how someone grants themselves everything — nor demote the last owner. The permission change takes effect on that member's very next request; they do not have to sign in again. - [Invite someone](https://docs.userkit.dev/en/api-reference/endpoints/members/invite-someone.md): Requires `members:write`. Creates the invitation and emails the link; the invitation exists even if the email is lost, and an admin can revoke and re-invite. Valid for 7 days, one pending invite per address. - [Leave the organization](https://docs.userkit.dev/en/api-reference/endpoints/members/leave-the-organization.md): Not a `members:write` action — this is a user administering their own access, which every member may do. The last-owner guard still applies, and the caller's session is closed with it. - [List members and pending invitations](https://docs.userkit.dev/en/api-reference/endpoints/members/list-members-and-pending-invitations.md): Requires `members:read`. Everyone who has access, plus everyone who is about to. - [Read an invitation](https://docs.userkit.dev/en/api-reference/endpoints/members/read-an-invitation.md): Public. Given a token, returns just enough for the accept screen: which organization, which address, and whether that address already has an account — so the screen knows whether to ask for a password. - [Remove a member](https://docs.userkit.dev/en/api-reference/endpoints/members/remove-a-member.md): Requires `members:write`. Removing yourself is `DELETE /v1/organization/members/me`. Revoking a membership invalidates that user's session for this organization immediately — the membership JOIN *is* the authorization. - [Revoke an invitation](https://docs.userkit.dev/en/api-reference/endpoints/members/revoke-an-invitation.md): Requires `members:write`. - [Close the setup wizard](https://docs.userkit.dev/en/api-reference/endpoints/organizations/close-the-setup-wizard.md): Requires `organization:update`. Idempotent in SQL, so a double click or a late "skip" never moves the timestamp. - [Create an organization](https://docs.userkit.dev/en/api-reference/endpoints/organizations/create-an-organization.md): The caller becomes its owner, and the session switches to it immediately — nobody creates a workspace to then go looking for it. Roles and both environments are seeded in the same transaction. - [Delete the organization](https://docs.userkit.dev/en/api-reference/endpoints/organizations/delete-the-organization.md): Requires `organization:delete` — owner only. Destroys everything hanging off it: members, roles, keys, invitations, and the sessions pinned to it. The exact name must be typed back to confirm. - [List the caller's organizations](https://docs.userkit.dev/en/api-reference/endpoints/organizations/list-the-callers-organizations.md): What the switcher shows. Fetched on demand rather than carried in the session, which only ever describes the active one. - [Read the active organization](https://docs.userkit.dev/en/api-reference/endpoints/organizations/read-the-active-organization.md) - [Sign a logo upload](https://docs.userkit.dev/en/api-reference/endpoints/organizations/sign-a-logo-upload.md): Requires `organization:update`. Same shape as the avatar upload: presign, PUT to the bucket, then save `public_url` with `PATCH /v1/organization`. - [Update the organization](https://docs.userkit.dev/en/api-reference/endpoints/organizations/update-the-organization.md): Requires `organization:update`. Omitted fields are left alone. - [Create a role](https://docs.userkit.dev/en/api-reference/endpoints/roles/create-a-role.md): Requires `roles:manage`. Custom roles are never `is_system`, so they stay renamable and deletable. An unknown permission is refused rather than silently ignored. - [Delete a role](https://docs.userkit.dev/en/api-reference/endpoints/roles/delete-a-role.md): Requires `roles:manage`. A role that still has members is refused rather than cascaded — those people would silently lose every permission. System roles cannot be deleted. - [List roles](https://docs.userkit.dev/en/api-reference/endpoints/roles/list-roles.md): Requires `roles:read`. System roles first, each with its permissions and how many people hold it. - [List the permission catalogue](https://docs.userkit.dev/en/api-reference/endpoints/roles/list-the-permission-catalogue.md): Requires `roles:read`. The catalogue lives in Go, not in the database: the set of things the system can do is a fact of the codebase, while who holds what is data. - [Replace a role's permissions](https://docs.userkit.dev/en/api-reference/endpoints/roles/replace-a-roles-permissions.md): Requires `roles:manage`. The permission set is **replaced**, not merged — the editor sends the full set, and a merge would make removing a permission impossible. - [Read the current session](https://docs.userkit.dev/en/api-reference/endpoints/session/read-the-current-session.md): Who the caller is, where they are, and what they may do there — resolved in a single query, so it is safe as the first call on load. - [Sign out](https://docs.userkit.dev/en/api-reference/endpoints/session/sign-out.md): Deletes the session server-side. Revocation is immediate — the next lookup matches nothing. - [Switch the active organization](https://docs.userkit.dev/en/api-reference/endpoints/session/switch-the-active-organization.md): Membership is always verified: switching into an organization the caller does not belong to answers 404, never a leak that it exists. - [Health check](https://docs.userkit.dev/en/api-reference/endpoints/system/health-check.md): What the platform's health check hits. Only Postgres is a hard dependency: a degraded or absent cache keeps the status at `ok`. - [Run one job](https://docs.userkit.dev/en/api-reference/endpoints/system/run-one-job.md): The queue consumer's callback. Guarded by a dedicated shared secret (`INTERNAL_JOBS_SECRET`); without it the route answers 404 rather than opening — an endpoint that runs arbitrary job kinds must never default to public. - [Disable two-factor](https://docs.userkit.dev/en/api-reference/endpoints/two-factor/disable-two-factor.md): Requires the current password: a stolen session must not be able to strip the second factor off an account. The recovery codes go with it. - [Enable two-factor](https://docs.userkit.dev/en/api-reference/endpoints/two-factor/enable-two-factor.md): Turns two-factor on once the code proves the secret was stored correctly, and hands over the recovery codes — the only time they are ever shown. - [Regenerate recovery codes](https://docs.userkit.dev/en/api-reference/endpoints/two-factor/regenerate-recovery-codes.md): Replaces the whole set — the old codes stop working — and shows the new one once. A partially-known set is worse than a fresh one, which is why this is never partial. - [Start two-factor setup](https://docs.userkit.dev/en/api-reference/endpoints/two-factor/start-two-factor-setup.md): Generates a secret and returns the QR to scan. It does **not** turn two-factor on — that only happens once the user proves they can produce a code, so an abandoned setup never locks anyone out. - [Errors](https://docs.userkit.dev/en/api-reference/errors.md): One envelope, stable codes, and a message meant for a person. - [Introduction](https://docs.userkit.dev/en/api-reference/introduction.md): Base URL, content types, pagination and the shape of every response. - [Rate limits](https://docs.userkit.dev/en/api-reference/rate-limits.md): Where the throttles are, what they count, and which ones fail closed. - [Architecture](https://docs.userkit.dev/en/architecture.md): Where each piece runs, and which of them ever sees a credential. - [Contacts](https://docs.userkit.dev/en/concepts/contacts.md): Your users: visitors, identities, first-touch attribution, and merges you do on purpose. - [Credentials](https://docs.userkit.dev/en/concepts/credentials.md): Four bearer families, four surfaces, and a prefix that tells them apart. - [Environments](https://docs.userkit.dev/en/concepts/environments.md): Live and test: same schema, never the same rows, and the caller never picks. - [Organizations and members](https://docs.userkit.dev/en/concepts/organizations.md): The tenancy boundary, the code that identifies it, and who gets to be inside. - [Roles and permissions](https://docs.userkit.dev/en/concepts/roles-and-permissions.md): A catalogue that lives in code, and role definitions that live in your database. - [Federated identity](https://docs.userkit.dev/en/customer-auth/federated.md): You already have auth. Sign the user's id on your server and UserKit trusts the claim. - [Hosted authentication](https://docs.userkit.dev/en/customer-auth/hosted.md): UserKit owns the account: sign-up, sign-in, verification and recovery, none of them enumerable. - [Choosing an auth mode](https://docs.userkit.dev/en/customer-auth/modes.md): Hosted or federated — the one decision to make before wiring the front end. - [Session tokens](https://docs.userkit.dev/en/customer-auth/session-tokens.md): A short-lived JWT your backend verifies offline, and the opaque session that keeps minting them. - [Social sign-in](https://docs.userkit.dev/en/customer-auth/social.md): Google, on your own consent screen — and the account-linking rules that keep it from becoming a takeover. - [Two-factor](https://docs.userkit.dev/en/customer-auth/two-factor.md): TOTP for your customers, enforced on every way in — including the social button. - [API keys](https://docs.userkit.dev/en/guides/api-keys.md): Minting secret keys, publishing public ones, and the origin list that guards them. - [Domain events](https://docs.userkit.dev/en/guides/events.md): One internal bus, written in the same transaction as the rows it describes. - [Two-factor authentication](https://docs.userkit.dev/en/guides/two-factor.md): TOTP for staff accounts, with recovery codes and a setup that cannot lock anyone out. - [Uploads](https://docs.userkit.dev/en/guides/uploads.md): Presigned PUTs for avatars and logos — the bytes never touch the API. - [Introduction](https://docs.userkit.dev/en/index.md): Accounts for your team, and accounts for the people who use your product — one API, two planes. - [Quickstart](https://docs.userkit.dev/en/quickstart.md): From a new account to your first identified contact, in one sitting. ## OpenAPI Specs - [openapi](https://docs.userkit.dev/api-reference/openapi.json)