Orgs
An org is the tenant. Customers, API keys, KYB submissions, quotes, and orders all hang off an org. A user can belong to one or more orgs. The common case is one. Multi-org users get a "Switch org" UI on the dashboard. Multi-org API callers mint a separate key per org.
Endpoints
Org
GET /orgs: list orgs the caller belongs to (dashboard sessions only).POST /orgs: create a new org. Caller becomesowner.GET /org: read the org bound to the current credential.PATCH /org: update display name and settings.GET /org/approved-currencies: currency codes the org is approved to transact in.POST /org/leave: remove yourself from the current org.GET /orgs/{id}/public-name: resolve an org id to its display name (public, unauthenticated).
Members
GET /org/membersPOST /org/membersPATCH /org/members/{user_id}DELETE /org/members/{user_id}DELETE /org/invitations/{invitation_id}: revoke a pending invitationPOST /org/invitations/{invitation_id}/resend: re-send a pending invitation email
Compliance email
The org's compliance contact address and its verification flow. Required when the org is created; these endpoints change it afterwards. See the compliance-email index for details.
PUT /org/compliance-email: set or change the compliance contact address (owner/admin).POST /org/compliance-email/resend: re-send the pending verification link (owner/admin).POST /orgs/compliance-email/verify: consume a verification token (public).
KYB
The unified KYB profile + state. See the KYB index for the schema and per-endpoint pages.
Auth + scoping
Endpoints accept either an Api-Key: <key> header (org-scoped key, recommended for automation) or Authorization: Bearer <access-token> (dashboard session). Sending both at once returns 400 BothCredsProvided. See Auth for the full model.
The URLs encode the scope:
/orgs(plural): the caller's set of memberships. Listing + creation. With an API key, the listing enumerates the orgs the minting user belongs to./org(singular): the current org. Whichever org the credential is scoped to.- With an API key: the org the key was minted in. Permanent. Keys don't switch context.
- With a dashboard session: see "Picking an org" below.
There's no /orgs/{id}/... form. You don't pass an explicit id in the path.
Picking an org (dashboard sessions)
A bearer token represents a user, not an org. The JWT claim is sub: <user_id>, with no embedded org id. Switching orgs does not require a new token. Pass the org id on the URL:
GET /v0/org?org=0a2a7e3c-...
If ?org= is omitted and you belong to exactly one org, the server picks it automatically. With more than one active membership the request is rejected (400, message "multiple org memberships: pass ?org=<id>"), so multi-org users must always pass ?org=.
If the supplied org id isn't one the caller belongs to, the response is 404 OrgNotFound. If the user has no active memberships at all, same code, message "no active org membership". See POST /orgs to bootstrap.
API keys do not accept ?org=. A key is bound to one org at mint time. The param is silently ignored. Mint a separate key per org instead.
Types
Org
| Field | Type | Notes |
|---|---|---|
id | string (UUID) | Our id. |
name | string | Display name. |
kyb_status | KybStatus | Current KYB outcome. Rich state (link, missing fields, …) lives at GET /org/kyb/state. |
compliance_email | string, nullable | Compliance contact address for the org, if set. May be awaiting verification. See compliance_email_verified. |
compliance_email_verified | boolean | true once the org has proven control of compliance_email. |
slack_channel | string, nullable | Slack channel this org receives changelog updates in. null until one is set. |
expired_quote_tolerance_bps | integer | Default expired-quote tolerance, in basis points (0–500), for accepts that don't set their own. Starts at 1. |
created_at | string (ISO-8601) | |
updated_at | string (ISO-8601) |
OrgMembership
| Field | Type | Notes |
|---|---|---|
org_id | string (UUID) | |
user_id | string (UUID), nullable | null for pending invitations. The invitee doesn't have a Seismic user yet (or hasn't accepted). Becomes a real id once they sign up + accept. |
invitation_id | string (UUID), nullable | Inverse of user_id: set on pending rows, null on active members. Drives DELETE /v0/org/invitations/{id}. Exactly one of (user_id, invitation_id) is non-null. |
email | string | Member's email, denormalized for list views. |
role | OrgRole | See below. |
created_at | string (ISO-8601) |
OrgRole
A string enum: one of "owner", "admin", "member", "viewer". Every org must have at least one owner at all times. A demote or remove that would leave the org with zero owners returns 409 LastOwner.
| Action | Owner | Admin | Member | Viewer |
|---|---|---|---|---|
| Read org, members, KYB, customers, orders | ✓ | ✓ | ✓ | ✓ |
| Mint / list / revoke API keys | ✓ | ✓ | ✓ | |
| Run quotes, accept quotes | ✓ | ✓ | ✓ | |
| Create end-user customers + accounts | ✓ | ✓ | ✓ | |
| Edit KYB profile, submit KYB, refresh state | ✓ | ✓ | ||
| Invite / remove members | ✓ | ✓ | ||
| Billing | ✓ | ✓ | ||
| Change roles (incl. promoting / demoting other owners) | ✓ | |||
| Delete org | ✓ |
Admins can change roles between member ↔ viewer only. Anything that touches admin or owner is owner-only.
KybStatus
A string enum: one of "draft", "submitted", "under_review", "approved", "rejected", "terminally_rejected", "paused", "unknown".
| Value | Meaning |
|---|---|
draft | KYB profile is editable and not yet submitted. |
submitted | Profile sent. Review hasn't begun yet. |
under_review | Verification is in progress. |
approved | Org is cleared for live activity. |
rejected | Verification was rejected: correct the profile and submit again to re-verify. See rejection_reason on the KybState. |
terminally_rejected | Verification was declined in a way that can't be corrected and resubmitted (e.g. a fraud or risk decision). The profile can still be edited and saved, but never resubmitted. Contact support. |
paused | Seismic has placed a compliance hold on the org. It cannot transact, and resubmitting the profile does not lift the hold. Unlike terminally_rejected the hold is reversible: the status moves on once the hold is lifted. |
unknown | Verification backend returned a status we don't recognize. Contact support. |
Rich state (link, missing fields, rejection reason, last sync) lives at GET /org/kyb/state.
