Platform ToolsEvent Structure

Event structure

The webhook envelope, every event kind we emit, and what rides inside data.object.

The envelope

{
  "id": "wev_033y3HiVQoqY1Y4lCwUxSz",
  "kind": "endorsement.approved",
  "environment": "sandbox",
  "sequence": 118,
  "created_at": "2026-08-26T10:15:32+00:00",
  "data": {
    "object": {
      "object": "endorsement",
      "customer_id": "cus_033y1FhTOmoW9W2jAuSvQx",
      "name": "endo_usd",
      "status": "approved",
      "requirements": {
        "complete": ["compliance_review", "identity", "proof_of_address", "selfie", "terms_of_service"],
        "uploaded": [],
        "pending": [],
        "missing": [],
        "issues": []
      }
    }
  }
}
FieldMeaning
idThe event id, wev_ prefixed. Stable across retries and replays. Use it as your dedupe key
kindThe event kind, such as deposit.received. The catalog is below
environmentsandbox or live. A cheap assertion that your consumer is wired to the right place
sequenceAn ever-increasing counter with gaps. Order your projection by it, not by arrival
created_atWhen the event was emitted, ISO 8601 with an offset
data.objectThe resource, built by the same serializer as the API, so a model that parses a GET response parses the event too
replayedPresent and true only on a manual replay

Event kinds

Events fire on the lifecycle transitions in Statuses & Lifecycles, using the same status vocabulary and the same objects.

Onboarding

KindFires whendata.object
customer.createdA customer was createdThe customer
tos_acceptance.createdA hosted terms link was acceptedThe tos_acceptance, carrying signed_tos_id
endorsement.approvedThe endorsement approved, and the currency's features openThe endorsement
endorsement.request_for_informationA retryable ask, including one on an approved endorsement. The requirements buckets say what to fixThe endorsement
endorsement.rejectedA final adverse decisionThe endorsement

There are no verification.* events. Verification decisions are announced as endorsement.*, because the endorsement is the only public verification reading. See Verification & Endorsements.

Money movement

KindFires whendata.object
virtual_account.activatedAn account is live with deposit instructionsThe virtual account
deposit.receivedFiat arrived on a virtual accountThe deposit
deposit.returnedA deposit was sent back to the sender. Produced in sandbox today; in live our team resolves a reversed credit with youThe deposit
deposit.on_holdA deposit is held pending compliance review. Produced in sandbox today; in live our team contacts youThe deposit
conversion.createdA conversion opened for a depositThe conversion
conversion.completedDelivery landed on chain. Emitted with payout.completedThe conversion, with final delivered_amount
payout.completedStablecoin delivered on chainThe payout, with tx_hash

Wallets

KindFires whendata.object
wallet.createdA wallet was createdThe wallet
wallet_transaction.createdFunds arrived at a wallet address, including sends you did not initiate (kind: "deposit"), or left it (kind: "withdrawal")The wallet_transaction
wallet_transaction.returnedA wallet deposit was returned. Produced in sandbox todayThe wallet_transaction
wallet_operation.completedAn authorized send landedThe wallet_operation, with tx_hash
wallet_operation.failedAn authorized send did not land. Nothing movedThe wallet_operation

Every kind is <object>.<what happened>, and data.object.object names the same object as the prefix.

wallet_transaction.created with kind: "deposit" is the one to wire up first if you use wallets. A wallet is funded by a plain on-chain send, so this event is how you learn a customer was paid. The alternative is polling GET /api/wallets/{wallet_id}/history.

Subscribing to a subset

Pass event_kinds when you register the endpoint:

curl -s $BASE/api/webhooks \
  -H "Api-Key: $KEY" -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://yourapp.example/rasto/kyc",
       "event_kinds": ["endorsement.approved",
                       "endorsement.request_for_information",
                       "endorsement.rejected"]}'

event_kinds must name at least one kind, and each must be a kind from the catalog above: an unknown one returns 422 invalid_parameter with param: event_kinds. url must be https://, at most 512 characters.

New event kinds are additive and ship without a version bump, so ignore kinds you do not recognize rather than failing on them.

Reading the history

GET /api/webhook_events returns what we emitted, newest first, whether or not it was delivered:

{
  "data": [
    {
      "id": "wev_033y3HiVQoqY1Y4lCwUxSz",
      "object": "webhook_event",
      "kind": "endorsement.approved",
      "sequence": 118,
      "data": {"object": {}},
      "published_at": "2026-08-26T10:15:33+00:00",
      "created_at": "2026-08-26T10:15:32+00:00"
    }
  ],
  "has_more": true,
  "next_cursor": "..."
}

A published_at of null means the event has not gone out yet. To backfill after downtime, see Debugging, replay, and downtime.