/v1 requires a bearer token in the
Authorization header. The token is a JWT issued by WorkOS, SOLO’s
identity provider, when you sign in to the SOLO dashboard. The API verifies it
cryptographically on every request — there are no API keys and no shared
secrets to manage on your side.
GET /health and GET /version are exempt; everything else returns
401 Unauthorized without a valid token.
Getting a token
Access to the API starts with your organization being provisioned by SOLO. Once that’s done, tokens are minted through authentication with the SOLO dashboard.Get your organization provisioned
SOLO onboards your organization into WorkOS and associates it with your
entity on the network. If you don’t have access yet, contact your SOLO
account manager.
Sign in
Authenticate through the SOLO dashboard sign-in flow. On success, WorkOS
issues an access token — a JWT signed with RS256 — bound to your user and
organization.
What the server verifies
When a request arrives, the API verifies the token before any handler runs:- Signature. The token’s key ID (
kid) is looked up in WorkOS’s published JWKS endpoint (https://api.workos.com/sso/jwks/<client_id>), and the RS256 signature is verified against that public key. Tokens signed by anything other than WorkOS — or minted for a different environment’s client — fail here, because their signing key isn’t in the key set. - Standard claims. The decoded payload is validated, including expiry
(
exp). An expired token is rejected. - Identity resolution. The verified claims (your user ID, organization, and role) are used to resolve your account and entity on the network. If the token is valid but no matching account exists, the request is rejected with a 401.
permissions (or role) claim; a valid token without the required permission
receives a 403 Forbidden.
Verification happens against WorkOS’s public keys — the API never sees or
stores your password, and tokens cannot be forged without WorkOS’s private
key.
Token lifetime and refresh
WorkOS access tokens are short-lived. Don’t cache one and reuse it indefinitely:- Refresh proactively. Re-authenticate (or use your sign-in session’s
refresh mechanism) before the token’s
expclaim passes, rather than waiting for failures. - Handle 401 as a refresh signal. If a previously working request starts
returning
401with"Invalid or expired token", obtain a fresh token and retry the request once. Do not retry in a loop with the same token. - Don’t share tokens across environments. A token is bound to one environment’s WorkOS client. Sandbox tokens fail signature lookup in production and vice versa.
Environments
Use the base URL for the environment your credentials were issued in:| Environment | Base URL |
|---|---|
| Development | https://api.dev.solo.one |
| Sandbox | https://api.sandbox.solo.one |
| Production | https://api.prod.solo.one |
Example request
A complete authenticated call — searching for a consumer by name within a network usingGET /v1/entities/consumer/search:
401 vs 403
The two failure modes mean different things, and you should handle them differently.401 Unauthorized — the API doesn’t know who you are. The token is
missing, malformed, expired, or doesn’t resolve to an account. Fix the token
and retry.
403 Forbidden — the API knows who you are, and the answer is no. The
token verified successfully, but it lacks a required permission, or your
account isn’t allowed to perform this operation on this resource. A retry with
the same credentials will fail the same way; this is a provisioning or
authorization problem, not a token problem.
403, with the full error envelope described in
Errors:
Troubleshooting
| Symptom | Status and body | Cause | Fix |
|---|---|---|---|
No Authorization header sent | 401 — "Requires authentication" | Header missing entirely | Add Authorization: Bearer $SOLO_TOKEN to the request |
Wrong scheme (e.g. Authorization: Token … or a bare token) | 401 — "Requires authentication" | The header isn’t a valid Bearer credential, so it’s treated as absent | Use exactly Bearer <token> — capital B, single space |
| Expired token | 401 — "Invalid or expired token" | The exp claim has passed | Obtain a fresh token and retry once |
| Token from the wrong environment | 401 — "Invalid or expired token" | The signing key isn’t in this environment’s JWKS | Use a token issued for the environment you’re calling |
| Garbled or truncated token | 401 — "Invalid or expired token" | The JWT can’t be decoded or its key can’t be resolved | Re-copy the token; check for whitespace or truncation in your env var |
| Valid token, unknown account | 401 — "Could not validate credentials or find account" | Your user verified but no account/entity exists for it yet | Usually first-time setup — contact SOLO support |
| Valid token, missing permission | 403 — "Not enough permissions" | The permissions/role claim lacks what the endpoint requires | Ask your administrator to update your role |
Persistent 500 — "Authentication service is unavailable" | 500 | Server-side identity configuration problem (not your token) | Report to SOLO support with your request_id |
request_id
when present — and quote it to support. See
Errors for how request IDs are correlated.
Best practices
Keep tokens server-side
Keep tokens server-side
Make API calls from your backend and keep tokens out of code you don’t
control. A bearer token carries your organization’s full standing on the
network for as long as it’s valid.
Store the token in an environment variable
Store the token in an environment variable
The examples on this site use
$SOLO_TOKEN for a reason: keeping the token
in an environment variable (or a secrets manager) keeps it out of shell
history, scripts, and version control.Build refresh in from day one
Build refresh in from day one
Because tokens are short-lived, an integration that fetches one token at
startup will start failing mid-run. Centralize token acquisition behind a
helper that refreshes on expiry, and treat a
401 anywhere as a signal to
refresh once and retry.Use least-privilege roles
Use least-privilege roles
Request only the roles and permissions your integration needs. A
furnish-only service doesn’t need querying permissions — limiting the
token’s reach limits the blast radius if it leaks.