https://{your-domain}.outseta.com/api/v1/
Contents
- Quick start
- Authentication
- Working with collections
- Account billing stages
- Error responses
- Rate limits
- Support
- Webhooks
Quick start
Authenticate every request with either a server-side API key or a user bearer token. For example:limit and offset for pagination, fields to control the response shape, orderBy for sorting, and entity properties for filtering.
General request behavior:
- Use HTTPS. HTTP requests are redirected to HTTPS with
301. GETretrieves,POSTcreates or invokes an action,PUTupdates, andDELETEremoves.- The API returns standard HTTP status codes. Error details are returned as JSON.
- Add
donotlog=1to a request that should not appear in the activity log.
Authentication
All API requests require authentication via one of two methods:Server-Side (API Keys)
Use API keys for server-to-server integrations. Create keys at Settings > Integrations > API Keys. Make sure to record the secret key when you create it.Warning: Never expose API keys in client-side code. The API key and secret combined give full access to all data in your account.
Client-Side (Bearer Token)
Do not use API keys on the client side — they can be easily copied. Instead, obtain a JWT access token by calling thePOST /tokens endpoint from the server side with your credentials, then use it on the client:
PersonUid, AccountUid, and subscription details. Tokens expire after approximately one year.
Verify tokens server-side using Outseta’s JWKS endpoint.
Two-Factor Authentication (2FA) During Login
When a user has enabled two-factor authentication, a username and password are no longer sufficient to obtain a token — the login becomes a two-step exchange. You do not need to call any endpoint up front to discover whether 2FA is on; the response toPOST /tokens tells you.
Step 1 — Attempt login as usual.
200 token response and
you’re done:
202 Accepted with a challenge instead of a token:
When both an authenticator app and email are enrolled, Outseta picks the authenticator app (Step 2 — Submit the code to get the token. Collect the 6-digit code from the user (from their email or authenticator app) and post it along with theTotp) as the defaultmechanismbecause it requires no “check your inbox” step. The user can switch to email — see below.
challenge_token:
Authorization: bearer {access_token} for subsequent calls:
Optional helper endpoints (all take the
challenge_token from Step 1):
- Resend an emailed code —
POST /api/v1/tokens/two-factor/resendwith{ "challenge_token": "..." }. Returns a fresh challenge (same shape as the202above). Only valid forEmailchallenges; aTotpchallenge returns400not_supported. - Switch to a different method —
POST /api/v1/tokens/two-factor/switch-mechanismwith{ "challenge_token": "...", "mechanism": "Email" }. Use this when the user picks one of the otheravailable_mechanisms(e.g. they can’t reach their authenticator). Returns a fresh challenge for the chosen mechanism (and sends a code when switching toEmail). - Use a recovery code —
POST /api/v1/tokens/two-factor/recoverywith{ "challenge_token": "...", "recovery_code": "abcd-efgh-ijkl" }. Returns the final JWT, exactly like Step 2. Each recovery code is single-use.
Forced 2FA Enrollment During Login
Accounts can be configured to require 2FA. When a user who has not yet set it up tries to log in, they must enroll a method before they can get a token — all in the same login flow. Step 1 — Attempt login. Exactly as above. If the account forces 2FA and the user has no method enrolled,POST /api/v1/tokens verifies the password and
returns 202 Accepted with two_factor_enrollment_required (instead of
two_factor_required):
challenge_token is the enrollment token — it proves the user’s
identity for the rest of the flow. Pass it as enrollment_token in every call
below. Let the user pick a method (email or an authenticator app) and run the
matching two-step begin → confirm sequence.
Option A — Enroll an authenticator app (recommended).
-
Begin — exchange the enrollment token for a shared secret:
Have the user add the secret to their authenticator app — scan
qr_code_png_base64(render it as an image), openotpauth_uri, or type thesecretmanually. -
Confirm — submit the first code the app generates:
-
Begin — a code is emailed to the user:
-
Confirm — submit the emailed code:
ShowNotes:recovery_codesto the user once and prompt them to save the codes — they are generated as part of first-time enrollment and are never returned again. Each is single-use atPOST /api/v1/tokens/two-factor/recovery.
- Both
begincalls pass only theenrollment_token; bothconfirmcalls pass theenrollment_token, thechallenge_tokenfrom the matchingbegin, and thecode. 401means the enrollment token is missing/invalid/expired (restart at Step 1);403means forced enrollment does not apply to the user;400(invalid_grant) means a wrong code;410(challenge_expired) means the challenge timed out.- Each token/challenge is valid for 10 minutes (
expires_in= 600). If the user takes too long, restart atPOST /api/v1/tokens. - After enrollment, the user is a normal 2FA user — subsequent logins follow the standard challenge flow described above, not this enrollment flow.
Working with collections
Collection endpoints support field selection, pagination, sorting, and filtering. Unless an endpoint says otherwise, these options can be combined in the same request.Field selection
When you make an API request, you’ll automatically get all the basic information from the main object and its immediate child objects. Referenced objects beyond the first level are returned asnull.
Change this behavior using the fields query parameter:
- Go deeper — Request fields lower down in the object tree:
?fields=CurrentSubscription.Plan.* - Go lighter — Request only the essentials for faster performance:
?fields=Uid,Name - Combination —
?fields=Uid,Name,CurrentSubscription.Plan.Uid - Wildcard — Use
*to get all fields in an object:?fields=*or?fields=CurrentSubscription.Plan.*
Tip: When expanding nested paths, includeExamples:*and intermediate path segments (e.g.,PersonAccount.*) to preserve root-level and intermediate fields.
Unknown paths are ignored, not rejected. AIf your request includes fields from a child object you will be limited to retrieving 25 items per page. The maximum for requests not expanding child object fields is 100 items.fieldspath that does not match a property on the object is dropped from the request. The response is still200, still well-formed, and simply has no such property — the same shape you would get if the value were legitimatelynull. Nothing in the response distinguishes “you asked for a property that does not exist” from “that property is null”. Confirm that what you requested came back before relying on it, and check spelling against the endpoint’s response schema rather than a similarly named property on another object.
Pagination
List endpoints return paginated results with ametadata object and an items array:
Examples:
Sorting
Sort results using theorderBy parameter with a property name and direction:
orderBy generally supports database-backed scalar properties. Support for nested paths is endpoint-specific, especially when a path traverses a collection and does not identify a single ordering value. Important known unsupported paths, including calculated properties, are listed in the endpoint’s x-outseta-sort-denylist; this list is curated rather than exhaustive.
Filtering
Filter results by passing entity properties as query parameters.Basic filtering
Filter on any field using the field name as a query parameter:Wildcard matching
Comparison operators
For advanced filtering, append comparison operators to field names:
Examples:
Sorting and filtering metadata
Collection operations in the OpenAPI document can includex-outseta-sort-denylist and x-outseta-filter-denylist. Each extension is an array of property paths, relative to an item in that operation’s response collection, that the operation does not support for sorting or filtering. Paths can be nested.
Account Billing Stages
Account stages reflect the financial standing of each account and are not directly editable — they change automatically based on subscription activity.
Filter by stage:
GET /crm/accounts?AccountStage=3 returns all actively subscribing accounts.
Error Responses
Validation errors return a JSON body with
ErrorMessage and PropertyName fields:
Rate Limits
Requests authorized by an API Key should not exceed 4 requests/second.Support
For help regarding the Outseta API please email support@outseta.com.Webhooks
Use Activity Notifications to receive real-time callbacks when events occur in Outseta. Configure webhook URLs and their SHA256 signing secrets at Settings > Notifications. Always verify the signature before processing a request. Outseta sends an HTTPPOST to each registered callback URL. The payload is the activity’s entity restricted to a subset of fields, with an ActivityEventData property carrying activity-specific data.
On accounts that bill through Stripe, account payloads carry CurrentStripeProductInstances: the Stripe products the account owns right now, each with the owned Quantity. The AccountProductsChanged event fires when that list changes and names the added and removed product UIDs in ActivityEventData.
An activity that lists more than one entity is rooted on whichever of those entities it fires on, so its payload schema is a
oneOf of them. TaskCreated and TaskUpdated work this way: the webhook is rooted on the deal, person or account the task belongs to, and the task itself is carried in the ActivityEventData property.