Receive Webhook
Inbound endpoint used by the chain-processing service (and other trusted external producers) to push transaction events into the platform. Protected with HTTP Basic authentication rather than JWT — the gateway does not re-wrap this route.
Request
POST /v2/webhooks
Headers
| Name | Description |
|---|---|
Authorization | Required. Basic <base64(username:password)> — credentials are configured per environment on the integration service (WEBHOOK_BASIC_USERNAME / WEBHOOK_BASIC_PASSWORD). |
Content-Type | Required. Must be application/json |
Request Body
Exact schema is dto.Transaction in the integration service:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The originating worker's transaction ID. Used by downstream consumers to look up the matching token_transactions, node_transactions, user_transactions, currency_token_transactions, secondary_market_transactions, or otc_transactions row. Treat this as the deduplication key. |
type | integer | Yes | Domain-specific transaction-type code. The integer maps to one of the Type enums below, depending on which collection the worker is updating. Consumers must therefore look up id first to disambiguate the enum namespace. |
status | integer | Yes | Domain-specific status code (Started, InProgress, Done, Error, PendingReceipt). See Status enum. |
transaction_hash | string | No | On-chain transaction hash (empty until the wallet broadcasts). |
contract_address | string | No | Address of the contract this transaction interacted with (or the newly-deployed contract for deploy types). Empty when not applicable. |
gas_used | integer (uint64) | No | Gas consumed once the receipt is available. 0 while pending. |
error | string | null | No | Set when the wallet pipeline reports a failure. Mirrors status = 4 (Error). |
created_at | string (ISO 8601) | Yes | Timestamp the worker created/queued the transaction. |
updated_at | string (ISO 8601) | Yes | Timestamp of the most recent worker update. |
Unknown fields must be ignored by consumers — additional fields may appear over time without bumping the schema version.
Example body
{
"id": "6913872755654191dede37da",
"type": 1,
"status": 3,
"transaction_hash": "0xc34e006659d532a0c8212a8ec624a08cfab3ffbd102936a681ab81e3cf7e0c6d",
"contract_address": "0x39247A80f1c0ddcEfb4986fA7A59E919E1F2Fa38",
"gas_used": 142037,
"error": null,
"created_at": "2026-02-06T13:04:58.802Z",
"updated_at": "2026-02-06T13:05:09.911Z"
}
Type namespaces
Because the type integer is domain-scoped, you must disambiguate by looking up id in the worker's source collection. The integration service fans the webhook to two consumers — one for admin-level transactions (token / node / currency-token) and one for user-level transactions (user / OTC / secondary-market).
| Collection | Enum reference |
|---|---|
token_transactions | List Token Transactions (22 types: e.g. 1 = CreateTokenization, 2 = Burn, …) |
node_transactions | Get Node Transactions (60 types: e.g. 1 = WithdrawType, 21 = SendKycToken, 33 = ExecuteDiamondCut, …) |
currency_token_transactions | Get Currency Token Transactions by ID (1 = DeployCurrencyToken, 2 = MintCurrencyToken, 3 = SendCurrencyToken) |
user_transactions | Get User Transactions (10 types: 1 = PurchaseOnPrimaryMarket … 10 = Withdraw) |
secondary_market_transactions | Listed under "OpenToSecondaryMarket" in List Token Transactions — the type payload from this collection is 1 (OpenToSecondaryMarket). |
otc_transactions | The type payload from this collection is 1 (OpenToOtc). |
When you receive a webhook, the recommended pattern is:
- Look up the
idin your own mirror (or query the appropriate/transactionsendpoint). - Use the matching record's collection to decide which enum table to apply to
type. - Update your local view based on the new
status/transaction_hash/error.
Status enum
The status integer is consistent across all domains:
| Value | Name | Description |
|---|---|---|
| 1 | Started | Worker has registered the request. |
| 2 | InProgress | Wallet pipeline is preparing / signing. |
| 3 | Done | Confirmed on-chain. Apply state updates. |
| 4 | Error | Failed — see error. |
| 5 | PendingReceipt | Broadcast on-chain (transaction_hash populated); the receipt-confirmation cron is still waiting for inclusion. Treat as not-yet-final. |
user_transactions is the one exception that historically does not emit PendingReceipt (it goes straight to Done/Error). Treat that absence as expected, not a bug.
Response
A successful request returns HTTP 200 with an empty body.
Status Codes
| Status Code | Description |
|---|---|
| 200 | Success - Payload accepted and enqueued |
| 400 | Bad Request - Malformed body or validator rejected the payload |
| 401 | Unauthorized - Invalid Basic credentials |
| 500 | Internal Server Error - Failed to publish to RabbitMQ |
Operational Notes
- The endpoint is public in the gateway configuration so that it can be called directly from the chain service, but still requires Basic credentials at the integration layer.
- The handler validates the payload and immediately publishes it onto two RabbitMQ topics (
webhookTransactionAdminProducer,webhookTransactionUserProducer). Downstream consumers update state, emit realtime events, and release waiting clients. - Retries are expected to be idempotent — send the same transaction with the same
idif unsure whether the previous call succeeded. Consumers dedupe onid. - Order is not guaranteed: a
Done(3) can in rare network conditions arrive before the intermediateInProgress(2). Consumers should monotonically advance status usingcreated_at/updated_atas tiebreakers. - Only the chain-processing service should call this route; the gateway does not re-export it for tenant clients.