Skip to main content
Every event Meow sends has the same envelope. The data object is the resource snapshot (or {id, object} in thin mode). The lifecycle stage lives on data.status. Dispatch on that, not the event name.
Envelope
sequence is a per-resource counter for discarding stale deliveries. See Handle out-of-order deliveries. It rides resource events and is absent on webhook.test, message.attempt.exhausted, and partner application.* events. ID shapes.
  • Meow-managed IDs (entity_id, account_id, counterparty_id, virtual_account_id, subscription_id, delivery_id) are UUIDs.
  • Resource IDs (ach_transfer.id, wire_transfer.id, inbound transaction id, check_deposit.id, global_account_transfer.id) are opaque strings whose format varies by the underlying bank:
    • CRB / FirstBank: deposit_inach_11mn5s777xgveb9, withdrawal_txc_15wp3bd309xenf6p, book_11n0b3vs9vy0ypw, interest_txc_15wp2ghq09qm3141.
    • Bridge global accounts: bridge_va_inach_<uuid> (incoming ACH), bridge_va_iw_<uuid> (incoming wire), bridge_wallet_ext_ach_<uuid> (external ACH off-ramp), bridge_wallet_ext_wire_<uuid> (external wire), wallet_transfer_group_<uuid> / wallet_deposit_group_<uuid> (wallet send / receive).
    • Treasury Prime: TP’s own external IDs.
    Treat resource IDs as tokens; don’t parse them.
Onboarding events (application.*, identity_verification.*) describe an application’s lifecycle and go to whoever owns that application’s stream. If a partner created it, they go to that partner’s subscription. If you onboarded the business yourself through the API with a global key, they go to your own entity subscription. You do not need a partner subscription to receive them. So the two catalogs overlap on the onboarding events rather than being disjoint. An entity subscription may name the rail events and the onboarding events; a partner subscription may name only the onboarding events. Listing an event a subscription could never receive returns a 400.

ach_transfer

status values: pending, processing, sent, returned, canceled, error. Optional fields not shown in the sample: operation_request_id.

wire_transfer

Same status values as ach_transfer. Extra wire-only fields: counterparty_bank_name, purpose, imad (Fed reference), instructions.

book_transfer

A transfer between two accounts at the same bank. Emitted only for the debited (source) business, using the same outbound framing as ach_transfer and wire_transfer, so the credited side does not get a copy. Internal legs Meow moves on your behalf (FX funding, insured cash sweep, core migrations) are suppressed. status values: pending, sent, canceled, error. Book transfers use a narrower set than ach_transfer / wire_transfer: there is no processing, returned, or pending_approval leg. Both sides are Meow accounts, so there are no counterparty routing details. to_entity_id is the business that was credited, including your own when you move money between two of your accounts. It is null when Meow cannot resolve the receiving account.

inbound_ach_transfer

status values: pending, posted, scheduled, failed, canceled, returned. Casing matches outbound (lowercase on the wire).

inbound_wire_transfer

Same shape as inbound ACH; object is inbound_wire_transfer.

check_deposit

Same shape as inbound ACH; object is check_deposit.

global_account_transfer

Activity on a global (virtual) account. Each activity is one-shot: funds_scheduled and funds_received for the same deposit share deposit_id but arrive as two separate events with different ids. activity_type values: funds_scheduled, funds_received, payment_submitted, payment_processed, refund_in_flight, refunded, refund_failed. currency is lowercase: usd, usdc, usdt, usdb, usdg, pyusd, dai, cash.

crypto_deposit

A stablecoin arriving at one of the entity’s deposit addresses. Both .created and .updated fire; status is the idempotency key, so one delivery emits per status transition (several vendor states collapse onto a single status value). status values: pending, processing, completed, refunding, returned, canceled, failed. destination values: global_account (funds stay in the Bridge global account) or bank_account (funds are converted to USD and wired to a Meow checking account). The fiat leg of a bank_account deposit is excluded from inbound_wire_transfer.* events — crypto_deposit.* is the only webhook signal for either destination. currency is lowercase: usdc, usdt, usdb, usdg, pyusd, eurc, dai, cash, usdv, path_usd. deposit_tx_hash is the on-chain transaction hash. Use it with the deposit_tx_hash query parameter on GET /accounts/{account_id}/transactions to look up the resulting bank transaction once status reaches completed. No credited-amount field is included: the settlement amount is in the destination’s currency (USD for bank_account, the held stablecoin for global_account), which differs from currency and is not persisted with the deposit record.

card

A corporate card. card.created fires when a card is issued; card.updated fires on every subsequent change. Dispatch on data.status, not the event type — every state change, including freeze and unfreeze, arrives as card.updated. status values: pending (issuer is provisioning the card), active, suspended (frozen or under a fraud hold), closed, failed. last_four reads **** while the card is still provisioning. spending_restriction is set when the card is restricted to or blocked from a specific merchant list. allowed_categories narrows the card to specific MCC categories; null means every category is allowed.

account

A Meow deposit account. account.created fires when the account is live and addressable on GET /accounts. For Treasury Prime that is when the bank confirms the application, later than the POST /accounts call. account.updated fires on status changes, including closes. status values: OPEN, PENDINGOPEN, PENDINGCLOSE, CLOSED, RESTRICTED, DELINQUENT. reference_id is the value you sent on POST /accounts. It is how you match this event back to the request that opened the account, which matters most on the products that open in the background, where the create response carries no account_id. It is null if you did not send one. Balances are not included. They move independently of the account itself. Read them from GET /accounts/{account_id}.

info_request

A reviewer’s request for more documents or answers, raised against a business that has already submitted. info_request.created fires when the request is raised. info_request.updated fires each time the outstanding set changes: a document lands, the questions are answered, and again when the request is satisfied. outstanding is everything the request is still waiting on, documents and questions alike. required_proof_types is the document half of that, as proof_type values the upload endpoints accept. completed_at is what says the request is done. It is null while anything is left. Fetch the request’s form with GET /entities/{entity_id}/info-requests/{request_id}/schema to see exactly what to collect. See the onboarding guide for the full flow.
Expiry emits no event. A request lapses when its expires_at passes, and nothing runs at that moment. Read expires_at and time it out yourself.
The agreements the business has to accept, tracked against the consent record that backs consent_url. onboarding_consent.updated fires when they first become owed at submission, when the representative opens the form, and on each item they accept. remaining_items is what is still owed and consented_items is what has been accepted. status moves pending to viewed to completed; completed_at is set once nothing remains. The event at submission is what tells you the agreements are owed at all. Every later event is driven by the representative acting on the form, so a business whose link is never opened produces no further events. Until remaining_items is empty, POST /accounts returns 403.
This family has no .created. Unlike the other resources, the consent record reports every stage through onboarding_consent.updated, including the first event at submission. Dispatch on status and remaining_items, not on the event name.

webhook.test

Triggered manually via POST /webhooks/subscriptions/{id}/test. Always sent in full (the thin mode falls through because there’s no id to GET).

message.attempt.exhausted

Fires when a delivery hits 10 failed attempts. The subscription is auto-disabled (disabled_reason=retry_exhausted). Use this to page an on-call. last_response_status is null if every attempt failed before getting an HTTP response (timeout, DNS, TLS).

application

Delivered to whoever owns the application’s stream: the partner that created it, or, for an API self-serve onboarding opened with a global key, the onboarded business’s own entity subscription. Each event reports an onboarding application reaching a new status; the event name and data.status always match. application_id is the application identifier (the KYB info public id); entity_id is the onboarded business, present on every event including application.created. previous_status is null only on application.created. status values: pending_document_upload, pending_user_action, under_review, submitted, approved, rejected. Note submitted sits later in the lifecycle than under_review: under_review means KYB review is pending, submitted means KYB approved but the account is not yet active. Unlike the rail events, application.* events carry no sequence and are always delivered as a full snapshot (partner subscriptions don’t support thin mode).

identity_verification

Reports one representative’s identity-verification status on an onboarding application. Delivered to the same owner as application.*: the partner that created the application, or, for an API self-serve onboarding opened with a global key, the onboarded business’s own entity subscription. This is a separate family from the application-level application.* events; a subscriber can take one without the other. status is always action_required, approved, or rejected, mirroring the event name. previous_status may additionally be pending, and is null on the first notification. Only approved is final. action_required means the representative has to verify again (their verification expired or was canceled) and can legitimately repeat across attempts, and a rejected representative stays under reconciliation, so either may be followed by further events if they verify again. The payload carries normalized state only, never the underlying reason: no PEP, watchlist, or vendor failure detail is included. representative_id is the stable identifier; representative_email is the correlation key you submitted. Like application.*, these carry no sequence and are always a full snapshot.