Account events
Fired when a new account is provisioned, an existing account changes, a deposit into a virtual account changes status, or a deposit is automatically converted and settled to a crypto destination. The lifecycle variants (account.created, account.updated) share one shape. The deposit variants (deposit.status_changed, plus the deprecated account.deposit_received / account.deposit_returned) add a deposit object on top of it. account.deposit_liquidated carries a liquidation object instead. The event_type discriminator tells them apart.
account.created
Fired when a new account is provisioned.
{
"event_type": "account.created",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "7c1d…",
"customer_id": "2f10…",
"account_kind": "fiat",
"account": {
"id": "7c1d…",
"kind": "fiat",
"shape": "us",
"status": "active",
"details": { "...": "…" }
}
}
}account.updated
Fired when an existing account changes: typically a previously-pending address being assigned, or a status flip — including the flip to closed.
{
"event_type": "account.updated",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "7c1d…",
"customer_id": "2f10…",
"account_kind": "crypto",
"account": {
"id": "7c1d…",
"kind": "crypto",
"shape": "evm",
"status": "active",
"details": { "...": "…" }
}
}
}Closure
An account being retired arrives as account.updated with status: "closed" and no details key at all. That absence is the signal: the account can no longer receive deposits, and any instructions you previously showed for it must not be used again. Retire it in your own systems off this event — no follow-up read is needed.
The account is not deleted. It stays readable on GET /accounts/:id and in GET /accounts, and its payments and deposits stay queryable. See Closed accounts.
{
"event_type": "account.updated",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "7c1d…",
"customer_id": "2f10…",
"account_kind": "fiat",
"account": {
"id": "7c1d…",
"kind": "fiat",
"shape": "us",
"status": "closed"
}
}
}deposit.status_changed
Fired every time a registered deposit changes status, carrying the new status in the body. This is the event to build on. One subscription covers the whole deposit lifecycle: switch on data.status rather than subscribing to a separate event per status.
status takes the same values as status on GET /deposits:
pending, reported and in flight.under_review, held for a check before crediting. The funds are neither available nor returned; a resolution follows later.completed, the funds landed.refunded, the funds went back to the sender.failed, the deposit failed and the funds never landed.
Statuses only move forward, and each fires at most once per deposit, so the events for one deposit form a chain with no repeats. A deposit can jump straight to a later status (most fiat deposits arrive already completed in their first report), in which case the skipped statuses never fire.
deposit.id is stable across every event for that deposit, so reconcile and dedupe on it. deposit.status_reason carries the reported reason for the current status when there is one, most usefully on under_review (why it is held) and refunded (why it went back). Registered deposits stay queryable via GET /deposits, so a missed delivery never loses a deposit.
{
"event_type": "deposit.status_changed",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "8e2f…",
"customer_id": "2f10…",
"account_kind": "fiat",
"status": "under_review",
"deposit": {
"id": "4d2e…",
"amount": "1.00",
"currency": "USD",
"status_reason": "compliance: under review before crediting",
"created_at": "2026-08-27T08:04:11.002Z",
"updated_at": "2026-08-27T08:05:05.740Z",
"status_at": "2026-08-27T08:05:04.118Z"
},
"account": {
"id": "8e2f…",
"kind": "fiat",
"shape": "us",
"status": "active",
"details": { "...": "…" }
}
}
}account.deposit_received
Fired when funds land in one of the customer's virtual accounts. This is the signal that money arrived outside of an order: a counterparty wiring into a virtual bank account, or sending stablecoins to a virtual wallet. Deposits that fund an order you created are reported through order.status_changed instead.
Seismic registers each deposit the moment it is first reported and tracks its status as it settles. This event fires exactly once per deposit, on the transition into completed, the moment the funds are actually credited. A deposit that never completes (held in review and then returned) never fires this event. It shows up via deposit.status_changed (above), account.deposit_returned (below), and GET /deposits instead. deposit.id is the stable identifier of the registered deposit. Reconcile and dedupe on it. Registered deposits stay queryable after the fact via GET /deposits, so a missed delivery never loses a deposit.
{
"event_type": "account.deposit_received",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "7c1d…",
"customer_id": "2f10…",
"account_kind": "fiat",
"deposit": {
"id": "9b41…",
"amount": "500.00",
"currency": "USD",
"reference": "ACH Credit 021000024184980 ACME PAYMENTS LLC",
"source": {
"sender_name": "ACME PAYMENTS LLC",
"bank_routing_number": "021000021",
"rail": "ach"
},
"created_at": "2026-08-27T08:04:11.002Z",
"updated_at": "2026-08-27T08:05:05.740Z",
"status_at": "2026-08-27T08:05:04.118Z"
},
"account": {
"id": "7c1d…",
"kind": "fiat",
"shape": "us",
"status": "active",
"details": { "...": "…" }
}
}
}account.deposit_returned
Fired when a registered deposit is returned to its sender: released the wrong way out of a compliance hold, or clawed back after it had already completed. At most one fires per deposit, on the transition into refunded.
deposit.id matches the id from the deposit's other events and GET /deposits, so the return reconciles against the receipt: a deposit that fired account.deposit_received and then this event netted out to zero. deposit.status_reason says why the funds went back to the sender, when a reason was reported — it is omitted otherwise, and the same value stays queryable on GET /deposits. Events delivered before the field existed don't carry it. Re-fetch the deposit if you need the reason for one of those.
{
"event_type": "account.deposit_returned",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "8e2f…",
"customer_id": "2f10…",
"account_kind": "fiat",
"deposit": {
"id": "4d2e…",
"amount": "75.00",
"currency": "GBP",
"status_reason": "compliance: returned to sender",
"created_at": "2026-08-25T14:20:00.310Z",
"updated_at": "2026-08-27T08:05:05.740Z",
"status_at": "2026-08-27T08:05:04.118Z"
},
"account": {
"id": "8e2f…",
"kind": "fiat",
"shape": "uk",
"status": "active",
"details": { "...": "…" }
}
}
}account.deposit_liquidated
Fired when a deposit credited to a virtual account is automatically converted and settled to a crypto destination: a virtual bank account configured to auto-liquidate takes in fiat; the deposit is converted and the proceeds are settled on-chain (often to the customer's own external wallet). This is the outbox counterpart of account.deposit_received: the deposit lands, then leaves as crypto.
The event is keyed on the source account that was drained (account_id / account) and fires once the liquidation has settled. Delivery waits for transaction_hash while recovery is attempted, for up to 72 hours. If the hash is still unavailable, or lookup fails permanently earlier, the completion is queued without it. A hash recovered afterward is available on GET /v0/liquidations, without another completion or separate settlement event. liquidation.deposit_id links back to the funding deposit's account.deposit_received, so the conversion reconciles against the original receipt.
{
"event_type": "account.deposit_liquidated",
"created_at": "2026-08-27T08:05:05.883Z",
"data": {
"account_id": "7c1d…",
"customer_id": "2f10…",
"account_kind": "fiat",
"liquidation": {
"id": "a3f8…",
"deposit_id": "9b41…",
"from_amount": "10",
"from_currency": "GBP",
"to_amount": "12.67",
"to_currency": "USDC",
"exchange_rate": "1.3209",
"fee": "0.41",
"destination_address": "0x0b47…",
"destination_chain": "base",
"transaction_hash": "0x6bed…",
"created_at": "2026-08-27T08:04:52.601Z",
"updated_at": "2026-08-27T08:05:05.740Z"
},
"account": {
"id": "7c1d…",
"kind": "fiat",
"shape": "us",
"status": "active",
"details": { "...": "…" }
}
}
}liquidation fields
| Field | Type | Notes |
|---|---|---|
id | UUID | Seismic's identifier for the liquidation. Stable across its completion and settlement events. Reconcile on it. |
deposit_id | UUID | The funding deposit's id. Optional. Absent when Seismic can't link the source deposit. |
from_amount | string | Amount drained from the source account. Optional. |
from_currency | string | Currency of from_amount. Optional. |
to_amount | string | Amount settled to the crypto destination. Optional. |
to_currency | string | Currency of to_amount. Optional. |
exchange_rate | string | Conversion rate applied. Optional. Absent for same-currency settlements. |
fee | string | Conversion / settlement fee. Optional. |
destination_address | string | On-chain address the proceeds settled to. Optional. |
destination_chain | string | Chain the proceeds settled on. Optional. |
transaction_hash | string | On-chain settlement hash. Optional: omitted when lookup recovery is exhausted. Reconcile on id, since webhook delivery may be retried. |
created_at | string | When the liquidation was first recorded, RFC-3339 UTC with millisecond precision. Not the same as the envelope created_at, which is when this event was recorded. Absent only on events delivered before the field existed. |
updated_at | string | When the liquidation record last changed — for this event, when it settled. Same format and same caveat as created_at. |
liquidation.settlement_recorded
Legacy event retained for compatibility with previously queued deliveries. New liquidations include the hash on account.deposit_liquidated when recovery succeeds and do not emit this event. The hash also appears on GET /v0/liquidations.
{
"event_type": "liquidation.settlement_recorded",
"created_at": "2026-09-04T08:05:05.883Z",
"data": {
"liquidation_id": "a3f8…",
"account_id": "7c1d…",
"customer_id": "2f10…",
"transaction_hash": "0x6bed…",
"updated_at": "2026-09-04T08:05:04.118Z"
}
}| Field | Type | Notes |
|---|---|---|
liquidation_id | UUID | The completed liquidation's identifier. |
account_id | UUID | The virtual account drained by the liquidation. |
customer_id | UUID | The customer that owns the account. |
transaction_hash | string | The on-chain settlement transaction hash. |
updated_at | string | When the liquidation record captured the hash, RFC-3339 UTC with millisecond precision. |
deposit fields
| Field | Type | Notes |
|---|---|---|
id | UUID | Seismic's identifier for the registered deposit. At most one event of each deposit type fires per id. Use it for reconciliation and dedup. |
amount | string | Decimal amount, in the account's currency. Optional. Absent when the deposit report omits it. |
currency | string | Currency of the amount. Optional. Absent when the deposit report omits it. |
transaction_hash | string | On-chain settlement hash. Present only for deposits credited over a blockchain rail. Absent for fiat deposits and crypto credits whose hash was not reported. |
reference | string | Payment reference reported with the deposit: the bank statement line of the credit (e.g. ACH Credit 021000024184980 ACME PAYMENTS LLC). Optional. Absent when none was reported. |
status_reason | string | Reported explanation for the current status (e.g. why a deposit was returned). Optional. Usually absent. |
source | object | Who sent the deposit, when Seismic can determine it. Absent when no originator details are available. See source fields. |
created_at | string | When the deposit was first recorded, RFC-3339 UTC with millisecond precision. Stable across every event the deposit fires, and not the same as the envelope created_at, which is when this event was recorded. Absent only on events delivered before the field existed. |
updated_at | string | When the deposit record last changed. Same format and same caveat as created_at. |
status_at | string | When the deposit reached its current status, as reported by the settlement network. Distinct from updated_at, which is when the record was written: a backfilled or re-delivered report lands well after the funds actually moved. Optional. Absent when no time was reported. |
source fields
Who sent the deposit. Every field is optional, and which ones arrive depends on the rail the deposit travelled and on how much the network reports about the sender — treat each one as present-if-known rather than guaranteed. The whole object is absent when nothing is known about the sender and no rail is known either. A credit whose only known detail is the rail still carries a source, holding rail and nothing else.
A bank deposit identifies the sender's bank account; a blockchain deposit identifies the sending wallet and the chain it settled on:
| Field | Type | Notes |
|---|---|---|
sender_name | string | Name of the sending party (person or business). Reported on both bank and blockchain deposits. |
bank_routing_number | string | Bank deposits only. Routing number of the sender's bank. |
account_number | string | Bank deposits only. The sender's account number, as reported. |
from_address | string | Blockchain deposits only. The wallet the funds were sent from. |
chain | string | Blockchain deposits only. The chain the transfer settled on (e.g. BASE, ETHEREUM). Always present for a deposit into a virtual wallet — a wallet lives on one chain. |
rail | string | How the deposit travelled. For a blockchain deposit this is the chain, and matches chain. For a bank deposit it is the payment scheme (e.g. ach, wire, swift, sepa). Always one of the platform's own rail names, spelled exactly as GET /rails spells it, and not guaranteed to be present. |
A blockchain deposit's source:
{
"sender_name": "ACME TREASURY",
"from_address": "0x37d1…",
"chain": "base",
"rail": "base"
}When rail is present
rail arrives in one of two ways, and is omitted when neither applies:
- The sending network reported it. Some networks name the scheme a credit arrived on; others report only the amount and the sender. A reported rail always wins over what the account would imply.
- The account accepts exactly one rail, in which case the rail follows from the account itself. This covers every virtual wallet (a wallet lives on one chain) and every bank account limited to a single scheme — a EUR account reachable only over SEPA, for example.
An account that accepts several rails and a credit that doesn't say which one carried it leaves rail absent. Seismic omits the field rather than guess, because a wrong rail on a deposit is a wrong entry in your records. Virtual USD accounts used to fall here as a matter of course — one account number receives ACH, wire and SWIFT alike, so nothing about the account tells them apart — but a growing share of those credits now arrive with the scheme reported, and those carry it.
rail is also absent when no external network carried the funds — a book transfer settled within the network holding both sides, the shape a conversion's own funding leg takes. There is no rail to name, so none is reported; the account's own rail is not substituted for one.
If you need the rail on every deposit, don't infer it from the account — treat its absence as "not known" and reconcile from the deposit's amount, reference, and sender_name instead.
Rail names
rail uses the same rail names as GET /rails, spelled the same way — the vocabulary GET /v0/schemas also groups by. It never carries a settlement network's private spelling for a scheme.
Match on the platform name, not on whatever a network happens to call it — a domestic wire reports wire, whatever the network that carried it calls the scheme.
When a credit arrives over a scheme the platform has no rail for, rail is omitted rather than reported under a foreign name. Same-day ACH and RTP are the two you are most likely to meet: both settle, and both leave rail absent, because naming either one would mean publishing a value GET /v0/rails doesn't list.
FedNow credits are the exception: they report fednow, a platform rail name, even while GET /v0/rails does not list it.
Fields
| Field | Type | Notes |
|---|---|---|
account_id | UUID | The account that was created, updated, credited, or debited. |
customer_id | UUID | Owning customer. |
account_kind | "fiat" | "crypto" | Discriminator on the embedded account.details shape. |
deposit | object | Only on the deposit events. See deposit fields. |
status | status | Only on deposit.status_changed, where it is required: the status the deposit moved into. |
liquidation | object | Only on account.deposit_liquidated, where it is required. See liquidation fields. |
account | Account | Full account record at event time. Optional in the schema. See Backfill caveat. |
Backfill caveat
The embedded account field is optional in the published JSON Schema. Freshly-emitted events always include it. Replays of older deliveries may not. Fall back to GET /v0/accounts/:id in that case.