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": {
      "name": "endo_usd",
      "status": "approved",
      "requirements": {
        "complete": ["terms_of_service", "identity", "selfie", "proof_of_address", "aml"],
        "uploaded": [],
        "pending": [],
        "missing": [],
        "issues": []
      }
    }
  }
}
FieldMeaning
idThe event id, wev_ prefixed. Stable across retries and replays. Use it as your dedupe key
kindThe event kind, always resource.verb. The catalog is below
environmentsandbox or live. A cheap assertion that your consumer is wired to the right place
sequenceA monotonically increasing per-partner counter. Order your projection by it, not by arrival
created_atWhen the event was emitted, ISO 8601 with an offset
data.objectThe resource, byte-identical to what the matching GET returns
replayedPresent and true only on a manual replay

data.object is the same serializer as the API. One shape on both paths, so your models never fork, and anything you can parse from a GET you can parse from an event.

Event kinds

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

Onboarding

KindFires whendata.object
tos.acceptedA hosted terms link was acceptedThe terms link, carrying signed_tos_id
endorsement.approvedThe endorsement approved, and the currency's features openThe endorsement
endorsement.request_for_informationA retryable ask. 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 KYC 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 senderThe deposit
deposit.on_holdA deposit is held pending compliance reviewThe deposit
conversion.createdA conversion opened for a depositThe conversion
conversion.completedFX and fees settled, delivery startingThe 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.deposit_receivedFunds arrived at a wallet address, including sends you did not initiateThe wallet activity row
wallet.withdrawal_createdFunds left a walletThe wallet activity row
wallet.deposit_returnedA wallet deposit was returnedThe wallet activity row
wallet.operation_confirmedAn authorized send landedThe operation, with tx_hash
wallet.operation_failedAn authorized send did not land. Nothing movedThe operation

wallet.deposit_received is the one to wire up first if you use wallets. Because a wallet is funded by a plain on-chain send, it is how you learn a customer was paid. There is no API call to observe instead.

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"]}'

Omitting event_kinds subscribes to everything, and the registered endpoint reads back ["*"].

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, 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. Page through anything newer than your last processed sequence to backfill after downtime.