Skip to content
Seismic Orchestration

Create an account

Pick the endpoint matching the (kind, origin) of the account you're creating:

EndpointWhen to use
POST /customers/:id/accounts/fiat/virtualProvision a new, on-platform bank account for the customer.
POST /customers/:id/accounts/fiat/externalRegister an existing bank account, the customer's own or a third party's.
POST /customers/:id/accounts/crypto/virtualProvision a new, on-platform wallet for the customer.
POST /customers/:id/accounts/crypto/externalRegister a wallet the customer already controls.

Shared semantics

The four endpoints share a common response shape and a common idempotency story.

Idempotency

All four endpoints honor the Idempotency-Key HTTP header. Re-POSTing the same (org, key) returns the existing AccountCreationRequest in its current state. Seismic won't insert a duplicate or retry a previously failed registration.

curl -X POST ".../accounts/crypto/external" \
  -H "Api-Key: $SEISMIC_ORCHESTRATION_API_KEY" \
  -H "Idempotency-Key: my-attempt-001" \
  -H "content-type: application/json" \
  -d '{ ... }'

The header is optional. Omit it and the server mints a key for the underlying request row.

Response shape

Every create endpoint returns the same envelope:

{
  "request": { /* AccountCreationRequest */ },
  "account": { /* Account | null */ }
}
  • request: the AccountCreationRequest. Carries the lifecycle of the creation. Re-POSTing the same Idempotency-Key returns this row in its current state.
  • account: Account | null. When non-null it's the same Account shape you'd get back from GET /accounts/:id. null means the account row hasn't materialized yet.
    • external endpoints populate account synchronously (caller supplied the details).
    • virtual endpoints populate account as soon as Seismic finishes provisioning: inline when provisioning completes during the request, otherwise on a later poll of GET /customers/:id/accounts/requests/:request_id (request.status === "completed" ⇒ request.account_id is set).

Currency / rail references

Where a body field accepts a currency or a rail — currency, the external fiat rails, a crypto account's rail — both forms are accepted:

  • By name (case-insensitive): "USDC", "ethereum", "USD".
  • By id (the stable numeric id from the seed JSON): 5, 7.

Pass whichever is convenient. Unknown names return 404.

Common errors

  • 400 MissingAccountFields / InvalidAccountFields: caller posted a body that doesn't match the chosen endpoint's required fields, or a field failed format validation (EVM address, IBAN structure, routing-number length, …).
  • 403: caller isn't a member of the customer's owning org.
  • 403 FlowNotApprovedForOrg / RailNotApprovedForOrg / CurrencyNotApprovedForOrg: your org isn't approved for this account flow, rail, or currency yet.
  • 404: unknown customer id, unknown currency or rail.
  • 409 CustomerNotVerified: the customer's verification hasn't reached approved. See Verification is required first.
  • 409 CustomerOnHold: the customer's verification carries a negative verdict.

Verification is required first

All four endpoints require the customer's rolled-up verification status to be approved. A customer still in draft, submitted, or under_review is rejected with CustomerNotVerified (409); the error body's status carries that rolled-up value, so you can tell what the customer still owes. Once a negative verdict lands (paused, rejected, terminally_rejected) the code is CustomerOnHold instead — the two are the difference between "not finished" and "declined", and are worth branching on.

Quoting is looser: it refuses only a negative verdict, so a customer still in review can be quoted but cannot yet hold an account.

Authoring

Pick one endpoint per account. Each has its own page with the full body, response, and curl examples: