Ana içeriğe geç

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

NameDescription
AuthorizationRequired. Basic <base64(username:password)> — credentials are configured per environment on the integration service (WEBHOOK_BASIC_USERNAME / WEBHOOK_BASIC_PASSWORD).
Content-TypeRequired. Must be application/json

Request Body

Exact schema is dto.Transaction in the integration service:

FieldTypeRequiredDescription
idstringYesThe 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.
typeintegerYesDomain-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.
statusintegerYesDomain-specific status code (Started, InProgress, Done, Error, PendingReceipt). See Status enum.
transaction_hashstringNoOn-chain transaction hash (empty until the wallet broadcasts).
contract_addressstringNoAddress of the contract this transaction interacted with (or the newly-deployed contract for deploy types). Empty when not applicable.
gas_usedinteger (uint64)NoGas consumed once the receipt is available. 0 while pending.
errorstring | nullNoSet when the wallet pipeline reports a failure. Mirrors status = 4 (Error).
created_atstring (ISO 8601)YesTimestamp the worker created/queued the transaction.
updated_atstring (ISO 8601)YesTimestamp 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).

CollectionEnum reference
token_transactionsList Token Transactions (22 types: e.g. 1 = CreateTokenization, 2 = Burn, …)
node_transactionsGet Node Transactions (60 types: e.g. 1 = WithdrawType, 21 = SendKycToken, 33 = ExecuteDiamondCut, …)
currency_token_transactionsGet Currency Token Transactions by ID (1 = DeployCurrencyToken, 2 = MintCurrencyToken, 3 = SendCurrencyToken)
user_transactionsGet User Transactions (10 types: 1 = PurchaseOnPrimaryMarket10 = Withdraw)
secondary_market_transactionsListed under "OpenToSecondaryMarket" in List Token Transactions — the type payload from this collection is 1 (OpenToSecondaryMarket).
otc_transactionsThe type payload from this collection is 1 (OpenToOtc).

When you receive a webhook, the recommended pattern is:

  1. Look up the id in your own mirror (or query the appropriate /transactions endpoint).
  2. Use the matching record's collection to decide which enum table to apply to type.
  3. Update your local view based on the new status / transaction_hash / error.

Status enum

The status integer is consistent across all domains:

ValueNameDescription
1StartedWorker has registered the request.
2InProgressWallet pipeline is preparing / signing.
3DoneConfirmed on-chain. Apply state updates.
4ErrorFailed — see error.
5PendingReceiptBroadcast 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 CodeDescription
200Success - Payload accepted and enqueued
400Bad Request - Malformed body or validator rejected the payload
401Unauthorized - Invalid Basic credentials
500Internal 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 id if unsure whether the previous call succeeded. Consumers dedupe on id.
  • Order is not guaranteed: a Done (3) can in rare network conditions arrive before the intermediate InProgress (2). Consumers should monotonically advance status using created_at/updated_at as tiebreakers.
  • Only the chain-processing service should call this route; the gateway does not re-export it for tenant clients.