SeismicOrchestrationClient
The primary export of seismic-orchestration. One class covers auth, users, orgs, KYB, API keys, quotes, customers, and accounts.
Construction
Three environment factories cover the common path. All three accept the same optional config (everything except baseUrl).
import { } from "seismic-orchestration";
const = .({ : .. });
const = .({ : .. });
const = .(); // reads SEISMIC_ORCHESTRATION_ENVFor private deployments or tests, use the raw constructor:
import { } from "seismic-orchestration";
const = new ({
: "https://orchestration.internal.example",
: "seismic_prod_…",
});Authenticating
The client supports both auth modes from the API auth model:
- API key: pass
apiKey: "seismic_prod_…"(or a getter). Sent asApi-Key: <key>. Recommended for server-to-server. - Access token (dashboard): leave
apiKeyunset. Configure atokenStoreand callapi.auth.login(...). Tokens persist via the store. Refresh-on-401 is automatic.
Setting both is harmless on the client (API key wins, Authorization is never sent), but the server explicitly returns 400 BothCredsProvided for any request that arrives with both, so don't try to send them yourself.
fromEnv() details
SeismicOrchestrationClient.fromEnv() reads process.env.SEISMIC_ORCHESTRATION_ENV. It must be exactly "production" or "sandbox". Anything else throws immediately, loud and early:
Error: SEISMIC_ORCHESTRATION_ENV must be "production" or "sandbox"; got "staging"
This is intentional. A silently-defaulted client is the kind of thing that prints real invoices on sandbox creds, or vice versa.
Config
import type { SeismicOrchestrationClientConfig } from "seismic-orchestration";| Field | Type | Default | Purpose |
|---|---|---|---|
baseUrl | string | (required) | API root. Filled in by the environment factories. |
apiKey | string | (() => string | null) | none | Org-scoped API key. Sent as Api-Key: <key>. When set, tokenStore and refresh are bypassed. |
tokenStore | "memory" | "localStorage" | TokenStore | "memory" | Where access + refresh tokens live (dashboard sessions). Ignored when apiKey is set. See Token stores. |
onRefreshFailed | () => void | none | Fires when refresh 401s (token-auth path). The token store is cleared first. |
fetch | typeof fetch | globalThis.fetch | Custom fetch (tracing, mocks, workers). |
Advanced overrides (replace the tokenStore-derived wiring entirely):
getAccessToken, getRefreshToken, onTokensRefreshed, all () => … callbacks. Skip unless you're doing something exotic (httpOnly cookies, cross-process coordination).
Refresh-on-401
request ─► 401 on protected path?
│ yes
▼
is refresh in flight?
│
no ──────┼──► start /auth/refresh
│ │
│ ├─► success → persist pair, retry original
│ └─► failure → clear tokenStore, onRefreshFailed
yes ─────┴──► await the in-flight refresh, then retry
Login / signup / refresh / logout paths are excluded from this: refreshing against them either loops or is meaningless.
Orgs
| Method | Endpoint |
|---|---|
api.orgs.list() | GET /orgs |
api.orgs.get() | GET /org |
api.orgs.update(req) | PATCH /org |
api.orgs.members.list() | GET /org/members |
api.orgs.members.invite(req) | POST /org/members |
api.orgs.members.updateRole(userId, role) | PATCH /org/members/{user_id} |
api.orgs.members.remove(userId) | DELETE /org/members/{user_id} |
api.orgs.kyb.profile() | GET /org/kyb/profile |
api.orgs.kyb.setProfile(profile) | PUT /org/kyb/profile |
api.orgs.kyb.submit() | POST /org/kyb/submit |
api.orgs.kyb.state() | GET /org/kyb/state |
api.orgs.kyb.refreshLink() | POST /org/kyb/link |
api.orgs.kyb.resync() | POST /org/kyb/resync |
API keys
| Method | Endpoint |
|---|---|
api.apiKeys.mint(req) | POST /auth/api-keys |
api.apiKeys.list() | GET /auth/api-keys |
api.apiKeys.revoke(id) | DELETE /auth/api-keys/{id} |
Auth
| Method | Endpoint |
|---|---|
api.auth.signup(req) | POST /auth/signup |
api.auth.login(email, password) | POST /auth/login |
api.auth.mfa.enroll() | POST /auth/mfa/enroll |
api.auth.mfa.activate(code) | POST /auth/mfa/activate |
api.auth.mfa.verify(challenge_id, code) | POST /auth/mfa/verify |
api.auth.refresh(refresh_token) | POST /auth/refresh |
api.auth.logout(refresh_token?) | POST /auth/logout |
api.auth.me() | GET /auth/me (whoami) |
login, mfa.verify, and refresh persist the returned pair to the configured tokenStore automatically, with no tokens.save(res) boilerplate. logout() uses the refresh token from the store if you don't pass one, then clears local state.
Quotes
| Method | Endpoint |
|---|---|
api.quotes.create(req, { wait?, named? }) | POST /quotes[?wait=1&named=1] |
api.quotes.get(id) | GET /quotes/:id |
api.quotes.acceptBest(id, opts) | POST /quotes/:id/accept (best) |
api.quotes.accept(id, quoteId, opts) | POST /quotes/:id/accept (by id) |
Customers
| Method | Endpoint |
|---|---|
api.customers.create(req) | POST /customers |
api.customers.get(id) | GET /customers/:id |
api.customers.list() | GET /customers |
api.customers.delete(id) | DELETE /customers/:id |
api.customers.setProfile(id, env) | PUT /customers/:id/kyc/profile |
api.customers.submit(id) | POST /customers/:id/kyc/submit |
api.customers.syncApprovals(id) | POST /customers/:id/kyc/sync-approvals |
api.customers.state(id) | GET /customers/:id/kyc/state |
api.customers.resync(id) | POST /customers/:id/kyc/resync |
api.customers.documents.upload(id, req) | POST /customers/:id/kyc/documents |
api.customers.documents.list(id) | GET /customers/:id/kyc/documents |
api.customers.documents.delete(id, docId) | DELETE /customers/:id/kyc/documents/:doc_id |
api.customers.documents.download(id, docId) | GET /customers/:id/kyc/documents/:doc_id |
api.customers.createExternalCryptoAccount(id, req, opts?) | POST /customers/:id/accounts/crypto/external |
api.customers.createVirtualCryptoAccount(id, req, opts?) | POST /customers/:id/accounts/crypto/virtual |
api.customers.createExternalFiatAccount(id, req, opts?) | POST /customers/:id/accounts/fiat/external |
api.customers.createVirtualFiatAccount(id, req, opts?) | POST /customers/:id/accounts/fiat/virtual |
api.customers.listAccounts(customerId, filter?) | GET /customers/:id/accounts |
api.customers.getFiatAccount(customerId, accountId) | GET /customers/:id/accounts/fiat/:account_id |
api.customers.getCryptoAccount(customerId, accountId) | GET /customers/:id/accounts/crypto/:account_id |
api.customers.liquidationDestinations(customerId, { currency }) | GET /customers/:id/liquidation-destinations |
api.customers.linkSuggestions(id, personId) | GET /customers/:id/kyc/associated-persons/:person_id/link-suggestions |
api.customers.order(id) | GET /customers/orders/:id |
api.customers.cancelOrder(id) | POST /customers/orders/:id/cancel |
Accounts (org-wide read)
| Method | Endpoint |
|---|---|
api.accounts.list(filter?) | GET /accounts |
api.accounts.get(id) | GET /accounts/:id |
api.accounts.balance(id) | GET /accounts/:id/balance |
api.accounts.refreshBalance(id) | POST /accounts/:id/balance/refresh |
api.accounts.updateLiquidation(id, input) | PUT /accounts/:id/liquidation |
api.accounts.disableLiquidation(id) | DELETE /accounts/:id/liquidation |
api.accounts.delete(id) | DELETE /accounts/:id |
Liquidations
| Method | Endpoint |
|---|---|
api.liquidations.list(filter?) | GET /liquidations |
Deposits
| Method | Endpoint |
|---|---|
api.deposits.list(filter?) | GET /deposits |
Catalog
| Method | Endpoint |
|---|---|
api.rails() | GET /rails |
api.schemas() | GET /schemas |
api.currencies() | GET /currencies |
api.industries() | GET /industries |
api.accountHolderIndustries() | GET /industries/account-holder |
api.rates(opts?) | GET /rates |
api.addressRequirements({ country }) | GET /customers/address-requirements |
Health
| Method | Endpoint |
|---|---|
api.health() | GET /health |
Errors
Every method rejects with ApiError on non-2xx responses:
import { , } from "seismic-orchestration";
const = .();
try {
await ..("alice@example.com", "wrong");
} catch () {
if ( instanceof ) {
.(.); // 401
.(.); // "invalid credentials"
}
}For the status-code landscape, see API → Errors.