Partner webhooks (operationStatusHook)
RaaS can POST JSON events to the URLs configured on the tenant (operationStatusHook), using a shared HMAC so you can verify authenticity.
Both event kinds below use the same URL list and signing headers. They are not alternatives—you may receive both for the same correlationId at different times. Branch on payload shape:
Inbound platform events vs outbound partner webhooks
RaaS handles two different directions of operation-related events:
After RaaS ingests
OperationCreated or OperationUpdated, it persists the new status and may POST an operation status update to your webhook (operationHookWorker). That is separate from request_money_full_outcome, which is only about the async request-money-full API pipeline—not about every platform status change.
Typical timeline for POST /request-money-full on the same correlationId:
- Your API call returns 200 with
status: "Accepted"(work continues in background). - RaaS POSTs
request_money_full_outcomeonce (AcceptedorFailed) when orchestration finishes. - If orchestration succeeded, Numi creates/updates the operation and RaaS receives platform events → one or more operation status updates (no
eventType) as the transfer progresses.
operationHookWorker and requestMoneyFullWebhookWorker share operationStatusHook but answer different questions: platform lifecycle vs request-money-full orchestration outcome. Implement one handler that branches on eventType.Tenant configuration
Configure on the tenant (admin API):Operation status webhooks and
request_money_full_outcome share operationStatusHook, but misconfiguration is handled differently: missing URL/secret for request-money-full skips silently (log only); missing URL/secret for operation status updates fail the job and Bull retries.Request
- Method:
POST - Content-Type:
application/json - Body: Event JSON (shape depends on event kind; see below).
- Timeout (RaaS → your URL): 30 seconds per request.
Headers sent by RaaS
Signature verification
Verifyx-raas-signature using the exact raw request body bytes (UTF-8 string as received) and the tenant sharedSecret.
Algorithm
- rawRequestBody: the raw HTTP body string before
JSON.parse(must match what RaaS signed—same key order asJSON.stringifyfrom the producer). - sharedSecret: tenant shared secret.
- HMAC-SHA256: RFC 2104.
- Base64: standard encoding of the HMAC digest.
Example (Node.js)
req.body as a string only if you use a raw body middleware; otherwise read the raw stream before parsing).
Delivery, retries, and multiple URLs
- All URLs in
operationStatusHookare called in parallel (Promise.allSettled). - Treat handlers as idempotent on
correlationId(retries and duplicate platform events can produce repeated POSTs). - Operation status updates are skipped (no POST) when the persisted status did not change (
NOT_FORWARD_BY_SAME_SATUS).
OperationStatusHookPayload, RequestMoneyFullOutcomePayload (hidden schema routes on the Partner API spec).
Operation status update
Sent when the Numi platform notifies RaaS ofOperationCreated or OperationUpdated (POST /hooks → OperationHookFlow → child status update, then parent webhook).
Implemented by operationHookWorker in src/queues/workers/OperationWorkers.ts.
Prerequisites
All of the following must be true before RaaS POSTs:- Tenant
trustedistrue. - At least one URL in
operationStatusHookand a configuredsharedSecret. rules[operationType].notifyWebHookistruefor the operation type (requestorsend).
notifyWebHook is false, RaaS logs and returns without POST (no error to your URL).
Payload shape (OperationStatusHookPayload)
There is no
eventType field. If you receive both event kinds on one endpoint, treat payloads without eventType as operation status updates.
Example
Flow
Event: request_money_full_outcome
Sent when the background request-money-full flow completes: CIP, beneficiary contact, payment method registration, and platform requestMoneyV2 (see requestMoneyFullWorker in OperationWorkers.ts).
Prerequisites
The outcome webhook is only sent if the tenant has:- At least one URL in
operationStatusHook, and - A configured
sharedSecret.
Payload shape
Example — success
Example — failure
Flow
Synchronous errors vs webhook (request-money-full)
POST /request-money-full can return 4xx/5xx immediately (validation, tenant, duplicate correlationId, etc.). Those responses use a JSON body with reason and code suitable for the HTTP layer.
Failures that happen after the API returned 200 with status: "Accepted" (e.g. CIP not completed, card errors) are reported only via request_money_full_outcome with status: "Failed", not by changing the original HTTP response.
Endpoint error codes (HTTP)
Error responses fromPOST /user/operations/request-money-full use this shape:
code for branching in your integration; values align with RequestMoneyFullErrorCode in the OpenAPI / TypeScript models.
Source
- Operation status webhook:
src/queues/workers/OperationWorkers.ts(operationHookWorker,operationStatusUpdateWorker),src/queues/flows/OperationHookFlow.ts,src/controllers/partner/eventHookController.ts. - Request-money-full outcome:
sendRequestMoneyFullOutcomeWebhook,requestMoneyFullWebhookWorker,requestMoneyFullWorkerin the same workers module. - Queue options:
src/config/queues.ts(QUEUE_NAME_OPERATION_HOOK,QUEUE_NAME_REQUEST_MONEY_FULL_WEBHOOK). - Types:
src/model/operation.ts(OperationStatusHookPayload,RequestMoneyFullOutcomePayload, …). - Repository reference:
docs/partner_webhooks.md.