Skip to content
Logo

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_ENV

For 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 as Api-Key: <key>. Recommended for server-to-server.
  • Access token (dashboard): leave apiKey unset. Configure a tokenStore and call api.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";
FieldTypeDefaultPurpose
baseUrlstring(required)API root. Filled in by the environment factories.
apiKeystring | (() => string | null)noneOrg-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() => voidnoneFires when refresh 401s (token-auth path). The token store is cleared first.
fetchtypeof fetchglobalThis.fetchCustom 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

MethodEndpoint
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

MethodEndpoint
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

MethodEndpoint
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

MethodEndpoint
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

MethodEndpoint
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)

MethodEndpoint
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

MethodEndpoint
api.liquidations.list(filter?)GET /liquidations

Deposits

MethodEndpoint
api.deposits.list(filter?)GET /deposits

Catalog

MethodEndpoint
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

MethodEndpoint
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.