A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Stytch API is how an app or AI agent adds login to a product: sending a magic link or one-time passcode, checking a password, creating and looking up users, and validating or revoking their sessions. Access is granted through a project ID and secret that authenticate every call, and those credentials are project-wide, so a key that can call one method can call them all. Stytch serves a single v1 surface, split into a Consumer product and a B2B product, and can push a signed event to a webhook when a user changes.
How an app or AI agent connects to Stytch determines what it can reach. There is a route for making calls and a route for receiving events, and each is governed by the project credentials behind it.
The REST API takes JSON request bodies and returns JSON over HTTPS, at https://api.stytch.com for live and https://test.stytch.com for test. A call authenticates with HTTP Basic auth, the project ID as the username and the secret as the password. Most methods are a POST, including reads like Search Users; single-object fetches and the JWKS endpoint are a GET. B2B methods live under the /v1/b2b path prefix.
Stytch delivers webhooks through Svix to an HTTPS endpoint configured in the Dashboard, for events named source.object_type.action such as direct.user.create or dashboard.member.update. The receiver verifies the signature with Svix's libraries before trusting the payload. Delivery is not strictly ordered, so Stytch recommends treating an event as a signal to re-fetch the affected object.
Every server call authenticates with HTTP Basic auth: the project ID as the username and the secret as the password, both from the Project & API keys page. The secret is project-wide and environment-wide, so it can call any method the project's enabled products expose, with no per-endpoint scoping. A secret must stay server-side and is shown in full only in the Dashboard.
A public token identifies the project in browser-initiated redirect flows, like the OAuth start URL, and is safe to expose in front-end code. It cannot make authenticated server calls or read user data on its own; it only kicks off a flow that the server then completes with the secret.
Connected Apps turns a Stytch project into an OAuth 2.0 authorization server so an app can let its own end users grant scoped access to client apps, including MCP clients, via the authorization_code grant. The access tokens are JWTs the app validates by introspection. This authorizes end users of an app built on Stytch; it is not a scope on the project secret that calls the Stytch API.
The Stytch API is split into authentication products an agent can act on, like magic links, one-time passcodes, passwords, sessions, and users. Each product has its own methods, and writes here create users, end sessions, or send login codes to real people.
Methods for sending and authenticating email magic links, plus server-created embeddable links.
Methods for sending and authenticating one-time passcodes over SMS and email.
Methods for creating, authenticating, and resetting password-based logins.
Methods for authenticating, listing, and revoking user sessions, plus the JWKS for verifying JWTs.
Methods for creating, fetching, updating, deleting, and searching users.
The redirect start flow for social and enterprise providers, and the method that completes it.
Methods for setting up and verifying authenticator-app (time-based) one-time passwords.
Methods for registering and authenticating passkeys and hardware authenticators.
The token endpoint that issues access tokens to M2M clients.
B2B / multi-tenant methods for creating and fetching organizations.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
Magic LinksMethods for sending and authenticating email magic links, plus server-created embeddable links.4 | ||||||
| POST | /v1/magic_links/email/send | Send a magic link by email to an existing user. | write | magic_links:send | Current | |
No per-endpoint scope exists; the project secret can call this. Shares a rate-limit counter with send, login_or_create, and invite (1 request/second/email). Acts onmagic_link Permission (capability) magic_links:sendVersionAvailable since the API’s base version Webhook eventNone Rate limit1/second/email (shared with login_or_create and invite) SourceOfficial documentation ↗ | ||||||
| POST | /v1/magic_links/email/login_or_create | Send a magic link by email, creating the user if the email is not already known. | write | magic_links:send | Current | |
Can create a user, firing a user.create event. No per-endpoint scope; project secret calls it. Acts onmagic_link Permission (capability) magic_links:sendVersionAvailable since the API’s base version Webhook event user.createRate limit1/second/email (shared with send and invite) SourceOfficial documentation ↗ | ||||||
| POST | /v1/magic_links | Create an embeddable magic link token server-side, to deliver over a channel of the app's choosing. | write | magic_links:create | Current | |
Returns a sensitive token that logs a user in; default 60-minute expiry, settable 5 to 10080 minutes. Acts onmagic_link Permission (capability) magic_links:createVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/magic_links/authenticate | Authenticate a magic link token and start or continue a session. | write | magic_links:authenticate | Current | |
A used or expired token returns a 401 (unable_to_auth_magic_link). Acts onmagic_link Permission (capability) magic_links:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
One-Time Passcodes (OTP)Methods for sending and authenticating one-time passcodes over SMS and email.3 | ||||||
| POST | /v1/otps/sms/send | Send a one-time passcode to an existing user by SMS. | write | otps:send | Current | |
Sends a real SMS; codes expire after 2 minutes. Test environments cap SMS send at 60/hour/project. Acts onotp Permission (capability) otps:sendVersionAvailable since the API’s base version Webhook eventNone Rate limit60/hour/project (test environment) SourceOfficial documentation ↗ | ||||||
| POST | /v1/otps/email/login_or_create | Send a one-time passcode by email, creating the user if the email is not already known. | write | otps:send | Current | |
Can create a user, firing a user.create event. Codes expire after 2 minutes. Acts onotp Permission (capability) otps:sendVersionAvailable since the API’s base version Webhook event user.createRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/otps/authenticate | Authenticate a one-time passcode and start or continue a session. | write | otps:authenticate | Current | |
A reused or expired code returns a 401 (unable_to_auth_otp_code). Acts onotp Permission (capability) otps:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
PasswordsMethods for creating, authenticating, and resetting password-based logins.4 | ||||||
| POST | /v1/passwords | Create a new user with a password. | write | passwords:create | Current | |
Password must meet strength rules; a duplicate email returns a 400 (duplicate_email). Acts onpassword Permission (capability) passwords:createVersionAvailable since the API’s base version Webhook event user.createRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/passwords/authenticate | Authenticate a user's email and password and start or continue a session. | write | passwords:authenticate | Current | |
Can return a reset_password error if the password appears in a breach database. Acts onpassword Permission (capability) passwords:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/passwords/email/reset/start | Start a password reset by sending a reset link to the user's email. | write | passwords:reset | Current | |
Sends a real email containing a reset link. Acts onpassword Permission (capability) passwords:resetVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/passwords/email/reset | Complete a password reset with the emailed token and set the new password. | write | passwords:reset | Current | |
A successful reset revokes all of the user's active sessions. Acts onpassword Permission (capability) passwords:resetVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
SessionsMethods for authenticating, listing, and revoking user sessions, plus the JWKS for verifying JWTs.4 | ||||||
| POST | /v1/sessions/authenticate | Authenticate a session token or JWT and return the session data. | write | sessions:authenticate | Current | |
Can extend the session lifetime if session_duration_minutes is supplied, so it is a write. Acts onsession Permission (capability) sessions:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/sessions | List all active sessions for a given user (by user_id query parameter). | read | sessions:read | Current | |
Read-only; returns every active session for the user. Acts onsession Permission (capability) sessions:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/sessions/revoke | Revoke a session, immediately invalidating all of its tokens, by session ID, token, or JWT. | write | sessions:revoke | Current | |
Immediately logs the user out for that session; requires exactly one identifier. Acts onsession Permission (capability) sessions:revokeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/sessions/jwks/{project_id} | Retrieve the JSON Web Key Set used to verify session JWTs for a project. | read | sessions:read | Current | |
Read-only; keys rotate roughly every 6 months, with both returned for a month around a rotation. Acts onjwks Permission (capability) sessions:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
UsersMethods for creating, fetching, updating, deleting, and searching users.5 | ||||||
| POST | /v1/users | Create a new user. | write | users:write | Current | |
Creates an account and fires a user.create event. Acts onuser Permission (capability) users:writeVersionAvailable since the API’s base version Webhook event user.createRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/users/{user_id} | Retrieve a single user by ID or external ID. | read | users:read | Current | |
Read-only; returns the user's factors, contact info, and metadata. Acts onuser Permission (capability) users:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /v1/users/{user_id} | Update a user's name, metadata, external ID, or roles. | write | users:write | Current | |
New email or phone factors are added via the relevant /send endpoint, not here. Acts onuser Permission (capability) users:writeVersionAvailable since the API’s base version Webhook event user.updateRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/users/{user_id} | Permanently delete a user. | write | users:write | Current | |
Irreversible; fires a user.delete event. Acts onuser Permission (capability) users:writeVersionAvailable since the API’s base version Webhook event user.deleteRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/users/search | Search users in the project with filters, paginated by cursor. | read | users:read | Current | |
A POST that reads; pages results with next_cursor. Acts onuser Permission (capability) users:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
OAuthThe redirect start flow for social and enterprise providers, and the method that completes it.2 | ||||||
| GET | /v1/public/oauth/{provider}/start | Begin a provider OAuth redirect flow (for example Google), using the project's public token. | read | — | Current | |
A browser redirect started with the public token, not a server call; returns a token to authenticate. Acts onoauth Permission (capability)None required VersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/oauth/authenticate | Complete an OAuth flow by exchanging the redirect token and starting a session. | write | oauth:authenticate | Current | |
Returns provider access and refresh tokens alongside the session. Acts onoauth Permission (capability) oauth:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
TOTPMethods for setting up and verifying authenticator-app (time-based) one-time passwords.2 | ||||||
| POST | /v1/totps | Create a TOTP instance for a user, returning the secret, QR code, and recovery codes. | write | totps:create | Current | |
Issues a shared secret and recovery codes; an active TOTP already present returns active_totp_exists. Acts ontotp Permission (capability) totps:createVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/totps/authenticate | Authenticate a TOTP code from an authenticator app or a recovery code. | write | totps:authenticate | Current | |
Verifies a second-factor code. Acts ontotp Permission (capability) totps:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
WebAuthn / PasskeysMethods for registering and authenticating passkeys and hardware authenticators.2 | ||||||
| POST | /v1/webauthn/register/start | Begin registering a passkey or WebAuthn credential, returning the browser creation options. | write | webauthn:register | Current | |
Requires user_id and domain; returns options for navigator.credentials.create(). Acts onwebauthn Permission (capability) webauthn:registerVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/webauthn/authenticate | Complete a passkey or WebAuthn authentication with the browser's credential response. | write | webauthn:authenticate | Current | |
Takes the result of navigator.credentials.get() and can start a session. Acts onwebauthn Permission (capability) webauthn:authenticateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Machine-to-Machine (M2M)The token endpoint that issues access tokens to M2M clients.1 | ||||||
| POST | /v1/public/{project_id}/oauth2/token | Issue an access token to a machine-to-machine client using the client_credentials grant. | write | m2m:token | Current | |
Takes client_id, client_secret, and grant_type=client_credentials; returns a JWT access token. Acts onm2m_token Permission (capability) m2m:tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Organizations (B2B)B2B / multi-tenant methods for creating and fetching organizations.2 | ||||||
| POST | /v1/b2b/organizations | Create an organization in a B2B (multi-tenant) project. | write | organizations:write | Current | |
B2B projects only; creates a tenant and fires an organization.create event. Acts onorganization Permission (capability) organizations:writeVersionAvailable since the API’s base version Webhook event organization.createRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/b2b/organizations/{organization_id} | Retrieve a single organization by ID, slug, or external ID. | read | organizations:read | Current | |
Read-only; B2B projects only. Acts onorganization Permission (capability) organizations:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Stytch can notify an app when something changes, like a user being created, updated, or deleted. It delivers a signed event describing what happened through its webhook infrastructure, so an integration learns about activity without polling.
| Event | What it signals | Triggered by |
|---|---|---|
{source}.user.create | A user was created in a Consumer project, by API, SDK, or the Dashboard. The source segment names where it came from, like direct or dashboard. | /v1/users/v1/magic_links/email/login_or_create/v1/otps/email/login_or_create |
{source}.user.update | A user's attributes changed, like name, metadata, or factors. | /v1/users/{user_id} |
{source}.user.delete | A user was deleted from the project. | /v1/users/{user_id} |
{source}.organization.create | An organization was created in a B2B (multi-tenant) project. | /v1/b2b/organizations |
Stytch limits how fast an app can call, scoped by a field such as an email address, a phone number, or the project as a whole, and assessed over a fixed interval.
Stytch rate-limits by a field over a fixed interval, not by a per-method point cost. The field is the identifier a limit is counted against, such as a specific email address, a phone number or its prefix, a Stytch user_id, or the project_id as a whole, so abuse against one email does not exhaust the whole project. Several endpoints share one counter: the Email Magic Link send, login_or_create, and invite methods together allow one request per second per email. Intervals are fixed windows aligned to the top of the hour or minute, not rolling. Test environments carry different, lower limits than live, for example SMS OTP send is 60 per hour per project in test. Going over returns HTTP 429 with error_type too_many_requests, and Stytch does not return headers naming which specific limit was hit, so the fix is exponential backoff plus spreading load across distinct fields.
List-style methods that can return many objects, like Search Users, use a results_metadata block with a next_cursor. A request passes that cursor back to fetch the next page, and a null next_cursor means there are no more results. Single-object reads and small fixed lists, like Get Sessions for one user, return the full set without a cursor.
Search Users accepts a limit on results per page (commonly up to a few hundred per page) and pages the rest through the cursor. Tokens carry their own lifetimes rather than size caps: a magic link defaults to 60 minutes and can be set from 5 to 10080 minutes, an embeddable magic link token to the same range, and an OTP code expires after 2 minutes. A user's external_id may be up to 128 characters.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | duplicate_email / bad_request | The request was malformed or conflicts with existing data, for example duplicate_email when a user with that email already exists, or a missing or invalid parameter. | Read error_message and error_url, fix the parameters or resolve the conflict, and resend. The request is not retryable as-is. |
| 401 | unauthorized_credentials | The project ID and secret were invalid, an incorrect password was supplied to a password authenticate, or a session token, magic link, or OTP was invalid or expired. | Confirm the project ID and secret belong to the same project and environment, or send the user a fresh link, code, or login. Rotate the secret if it may be compromised. |
| 403 | forbidden | The credentials are valid but the action is not permitted for this project or environment, for example a product that is not enabled or an SDK-disallowed endpoint. | Enable the relevant product in the Dashboard, or call the method from the server rather than the frontend SDK. |
| 404 | not_found | The requested object does not exist, or is not visible to this project or environment, for example an unknown user_id. | Verify the ID and confirm it belongs to the same project and the same test or live environment. |
| 429 | too_many_requests | A rate limit was hit for a field over its interval, such as too many sends to one email or phone number. Stytch does not return headers naming which specific limit was hit. | Back off with exponential backoff, and spread load across distinct fields rather than hammering one email, phone, or the project. |
| 500 | internal_server_error | A rare error on Stytch's side. | Retry with backoff, and contact Stytch support if it persists. |
Stytch serves a single v1 API and ships changes continuously rather than minting dated versions, splitting its surface into a Consumer product and a B2B (multi-tenant) product.
Stytch serves a single, unversioned v1 API and ships changes continuously through its changelog rather than minting dated versions. The surface is split into a Consumer product and a B2B (multi-tenant) product, each with its own data model, and new authentication products and methods are added over time. The entries below are notable dated additions, newest first.
An Email Risk API launched in beta to score fraud risk from characteristics of an email address.
An SSO Migration Gateway entered beta for moving an existing SSO setup onto Stytch's platform with less disruption.
Support for Client ID Metadata Documents was added, a standard for sharing OAuth client metadata securely.
Connected Apps was expanded to layer onto any existing authentication system, enabling OAuth and AI agent (MCP) authorization flows.
A raw signals feature was added to Device Fingerprinting, exposing low-level device and browser data.
There is one v1 surface; track the changelog for additions and deprecations.
Stytch changelog ↗Bollard AI sits between a team's AI agents and Stytch. Grant each agent exactly the access it needs, read or write, product by product, and every call is checked and logged.