Getting started

Authentication

Every request carries an API key as a bearer token. There are no other authentication methods, and no unauthenticated endpoints.

curl
curl https://xosign.ai/api/v1/account \
  -H "Authorization: Bearer xo_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Issuing a key

Create keys yourself in Settings → API keys. Self-serve issuance is available on the Crew plan and above; the API itself is available to every paid plan.

  • The key is shown once, at the moment you create it. We store only a hash, so we cannot show it to you again or recover it — if you lose it, revoke it and issue another.
  • You choose the key’s scopes when you create it. Scopes cannot be changed afterwards — to widen access, issue a new key.
  • Revoking takes effect immediately, and remains available even if your plan lapses, so you are never locked out of shutting off a leaked key.

Treat a key like a password

A key carries the full authority of its scopes over your entire account. Keep it in a secret store or an environment variable, never in version control, client-side code, or a mobile app.

Live and test keys

Keys come in two modes, distinguished by their prefix. GET /account echoes back the mode of whichever key you used, so you can always confirm which one is in play.

PrefixModeBehaviour
xo_live_LiveEverything happens for real: documents are created and signature requests are delivered.
xo_test_TestDocuments are created and change status exactly as in live mode, but no email or SMS is delivered.

Test mode is not a sandbox

A xo_test_ key reads and writes your real production data, in the same account, alongside everything else. The only thing test mode changes is that outbound email and SMS are suppressed.

A document you create with a test key is a real document in your account. Sending one with a test key really does move it to sent. Test mode makes it safe to exercise the API without bothering your signers — it does not make it safe to experiment against data you care about. There is no separate test dataset today.

Scopes

Each key carries a fixed set of scopes, and each endpoint requires one. Calling an endpoint your key is not scoped for returns 403 with the code insufficient_scope — grant the narrowest set that does the job.

ScopeAllows
documents:readList and retrieve documents.
recipients:readRead a document's recipients, including their email addresses and phone numbers.
documents:writeCreate draft documents.
documents:sendSend documents for signature. This is the scope that causes email and SMS to go out.

Reading recipients is separated from reading documents on purpose: an integration that only needs to know whether something was signed can be given documents:read alone, without ever seeing signers’ email addresses or phone numbers.

EndpointRequires
GET /accountNo scope required
GET /documentsdocuments:read
POST /documentsdocuments:write
GET /documents/{id}documents:read
POST /documents/{id}/senddocuments:send
GET /documents/{id}/recipientsrecipients:read

Keys issued before scopes existed

Older keys were granted read-only access when scopes were introduced, rather than being silently upgraded. If a long-lived key suddenly cannot create or send, that is why — issue a new one with the scopes you need.

Always call xosign.ai

A different host will look like a bad key

Send every request to https://xosign.ai/api/v1. If you call a deployment hostname instead, the request is redirected to the canonical host — and browsers, Node’s built-in fetch, and most HTTP clients drop the Authorization header when a redirect crosses origins.

The retried request then arrives with no credentials, and you get a 401 that looks exactly like an invalid key. If a key works with curl but not from your application, check the host you are calling before you suspect the key.

When authentication fails

Every authentication failure returns the same 401 body, whatever actually went wrong:

json
{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "Invalid API key.",
    "doc_url": "https://xosign.ai/docs/api/errors#authentication_error",
    "request_id": "req_b07b069f0a83492877e3fa31"
  }
}

A missing key is the one case with its own code (missing_authorization). Everything else — malformed, unknown, revoked, expired — is invalid_api_key, so the endpoint cannot be used to discover which keys are real. See authentication errors.

Authentication · XOsign API docs