Partner API

Single sign-on

Two directions: your backend asks for a one-time login link (POST …/sso), or your platform signs a short JWT and sends the browser to /api/sso/brands/{brand_id}/login. Sessions can be listed and ended.

POST/api/external/v1/users/{external_id}/sso

#A one-time login link for a user (your backend asks)

Auth X-Api-Key sso:issue Partner API

Scope sso:issue. Answers a link to the brand's trader or client dashboard that signs the user in once. The link lives login_ttl_seconds of the brand (default 60 s) and works a single time; the client's two-factor login (when switched on) still asks for the code. Open it in the user's browser (redirect) or hand the token to your page, which calls the app's /login?token= itself.

Path parameters

external_id string required

Your client id (1-191 letters, digits, . _ @ : + -). Stored on the client as external_user_id.

Request body (optional) · application/json

target enum optional

Default "trader".

traderdashboard

Responses

201

The link.

403

sso_disabled (brand switch), login_disabled (the user may not sign in), insufficient_scope.

404

not_found / user_not_found / account_not_found / symbol_not_found.

409

no_brand or no_app_domain (the brand has no trader / dashboard domain yet).

422

invalid_request with details.errors = { field: reason }.

curl -X POST "https://api.your-brand.com/api/external/v1/users/cust_10042/sso" \
  -H "X-Api-Key: $OCTOLIT_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "target": "trader"
}'
Response 201
{
  "login_url": "https://trade.your-brand.com/login?token=otp_...",
  "token": "otp_8f3c...",
  "target": "trader",
  "expires_at": "2026-11-11T09:31:00Z",
  "client_id": 1042
}
GET/api/external/v1/users/{external_id}/sessions

#The user's live sessions

Auth X-Api-Key sso:issue Partner API

Scope sso:issue. Newest first (at most 100).

Path parameters

external_id string required

Your client id (1-191 letters, digits, . _ @ : + -). Stored on the client as external_user_id.

Responses

200

The sessions.

curl "https://api.your-brand.com/api/external/v1/users/cust_10042/sessions" \
  -H "X-Api-Key: $OCTOLIT_PARTNER_KEY"
Response 200
{
  "sessions": [
    {
      "id": 1042,
      "started_at": "2026-11-11T08:00:00Z",
      "agent_session": true,
      "expires_at": "2026-11-11T09:31:00Z"
    }
  ]
}
DELETE/api/external/v1/users/{external_id}/sessions

#End every session of the user

Auth X-Api-Key sso:issue Partner API

Scope sso:issue. The user is signed out of the trader and the dashboard everywhere.

Path parameters

external_id string required

Your client id (1-191 letters, digits, . _ @ : + -). Stored on the client as external_user_id.

Responses

200

How many sessions ended.

curl -X DELETE "https://api.your-brand.com/api/external/v1/users/cust_10042/sessions" \
  -H "X-Api-Key: $OCTOLIT_PARTNER_KEY"
Response 200
{
  "ended": 1
}
DELETE/api/external/v1/users/{external_id}/sessions/{session_id}

#End one session

Auth X-Api-Key Partner API

Path parameters

external_id string required

Your client id (1-191 letters, digits, . _ @ : + -). Stored on the client as external_user_id.

session_id integer required

Responses

200

Ended.

404

not_found / user_not_found / account_not_found / symbol_not_found.

curl -X DELETE "https://api.your-brand.com/api/external/v1/users/cust_10042/sessions/9001" \
  -H "X-Api-Key: $OCTOLIT_PARTNER_KEY"
Response 200
{
  "ended": 1
}
GET/api/sso/brands/{brand_id}/login

#Sign a client in with a token your system signed (browser link)

No API key Partner API

No API key: the signed token is the credential. Your system signs a short JWT for the client and sends the browser here; we verify it and redirect to the brand's trader or client dashboard with a one-time login token. On a refusal the browser is sent to the app's /login?sso_error=<code> (or JSON with ?format=json).

Token (JWT, RS256 with the public key / JWKS URL you gave us, or HS256 with the shared secret):

claimmeaning
issexactly the issuer saved for the brand
audexactly the audience saved for the brand
subyour client id (external_id, 1-191 letters, digits, . _ @ : + -)
iatissued at (unix seconds); older than max_age_seconds (default 300) is refused
expexpiry (unix seconds); at most max_age_seconds after iat
jtiunique token id - a token presented twice is refused (token_replayed)
nbf, email, name, given_name, family_nameoptional; the names / e-mail fill a client created by auto-create
header kidthe key id when a JWKS URL is used

Clock skew of clock_skew_seconds (default 60) is tolerated. Unknown client id: a new client is created when the brand's "create unknown clients" is on, else 404 user_not_found.

Path parameters

brand_id integer required

The brand id shown in CRM Settings > API & Webhooks > SSO.

Query parameters

token string required
target enum optional
traderdashboard
format enum optional
json

Responses

302

Redirect to https://<brand app>/login?token=<one-time token> (or /login?sso_error=<code>).

401

token_invalid (not a JWT / bad header or claims), token_signature, token_alg (the algorithm is not the one configured for the brand), token_issuer, token_audience, token_expired, token_age (issued more than the allowed age ago, or not yet valid: iat / nbf outside the window), token_jti (missing jti), token_subject (missing sub), token_key (no key found for the token's kid), token_replayed (the jti was already used).

403

sso_disabled, sso_not_configured, login_disabled, user_other_brand, company_inactive, brand_inactive.

404

brand_not_found, user_not_found.

409

no_app_domain.

422

invalid_target, invalid_subject, create_failed.

curl "https://api.your-brand.com/api/sso/brands/13/login?token=otp_8f3c..."
Response 302
Redirect to `https://<brand app>/login?token=<one-time token>` (or `/login?sso_error=<code>`).
POST/api/sso/brands/{brand_id}/login

#Sign a client in with a token your system signed (server to server)

No API key Partner API

The same check as GET; answers the login link as JSON for your backend or page script.

Path parameters

brand_id integer required

The brand id shown in CRM Settings > API & Webhooks > SSO.

Request body · application/json

token string required
target enum optional

Default "trader".

traderdashboard

Responses

200

The link.

401

No key, an unknown / expired / revoked key, or a refused token.

403

insufficient_scope, ip_not_allowed, api_disabled, company_inactive, brand_disabled, sso_disabled, trending_disabled (trading on behalf also: brand_inactive, client_inactive, trading_inactive, trading_denied).

404

not_found / user_not_found / account_not_found / symbol_not_found.

422

invalid_request with details.errors = { field: reason }.

curl -X POST "https://api.your-brand.com/api/sso/brands/13/login" \
  -H "Content-Type: application/json" \
  -d '{
  "token": "otp_8f3c...",
  "target": "trader"
}'
Response 200
{
  "login_url": "https://trade.your-brand.com/login?token=otp_...",
  "token": "otp_8f3c...",
  "target": "trader",
  "expires_at": "2026-11-11T09:31:00Z",
  "client_id": 1042,
  "created": true
}