Skip to main content

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:
EventEvent originWhat it tells the partnerWorker
Operation status updateNumi platform → RaaS POST /hooks (OperationCreated / OperationUpdated)The operation changed state on the platform (Sent, Funding, Completed, …) plus sender/recipient contextoperationHookWorker (operationsHook queue)
request_money_full_outcomeRaaS internal async job after POST /request-money-full returned HTTP AcceptedWhether RaaS finished orchestrating the request (CIP, contact, payment method, requestMoneyV2): Accepted or FailedrequestMoneyFullWebhookWorker (requestMoneyFullWebhook queue)
EventHow to detect in your handler
Operation status updateNo eventType field (see Operation status update)
request_money_full_outcomeeventType === "request_money_full_outcome"

Inbound platform events vs outbound partner webhooks

RaaS handles two different directions of operation-related events:
DirectionEndpoint / triggerAudienceDocumentation
InboundNumi → RaaS POST /hooksRaaS only (not your integration)Internal: docs/event_system/
OutboundRaaS → your operationStatusHook URLsPartner integrationThis page
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:
  1. Your API call returns 200 with status: "Accepted" (work continues in background).
  2. RaaS POSTs request_money_full_outcome once (Accepted or Failed) when orchestration finishes.
  3. 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):
SettingPurpose
operationStatusHookOne or more HTTPS URLs. RaaS POSTs to all of them in parallel.
sharedSecretHMAC key for x-raas-signature. Required for signed delivery.
trustedMust be true for operation status webhooks. Non-trusted tenants are skipped (TENANT_NOT_TRUSTED).
rules.request.notifyWebHook / rules.send.notifyWebHookPer operation type (request, send). If false, RaaS does not POST for that type (may reassign tenant when reasignTenant is set).
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

HeaderDescription
x-raas-signatureRequired when a body is sent. Base64-encoded HMAC-SHA256 of the raw JSON body using the tenant sharedSecret. See Signature verification.
x-raas-op-countryTenant country id (for example MX). May be empty if not set on the tenant model.

Signature verification

Verify x-raas-signature using the exact raw request body bytes (UTF-8 string as received) and the tenant sharedSecret.

Algorithm

signature = Base64( HMAC-SHA256( rawRequestBody, sharedSecret ) )
  • rawRequestBody: the raw HTTP body string before JSON.parse (must match what RaaS signed—same key order as JSON.stringify from the producer).
  • sharedSecret: tenant shared secret.
  • HMAC-SHA256: RFC 2104.
  • Base64: standard encoding of the HMAC digest.

Example (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(rawBody, signatureHeader, sharedSecret) {
  const expected = crypto
    .createHmac('sha256', sharedSecret)
    .update(rawBody, 'utf8')
    .digest('base64');

  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader, 'base64'),
    Buffer.from(expected, 'base64')
  );
}
Use the raw body from your framework (e.g. Express 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

QueueWorkerAttemptsInitial delayBackoffOn delivery failure
operationsHookoperationHookWorker3500 msfixed 3 sThrows ERROR_FORWARDING_EVENTS if every URL fails
requestMoneyFullWebhookrequestMoneyFullWebhookWorker51 sfixed 5 sThrows WEBHOOK_DELIVERY_FAILED if any URL fails
  • All URLs in operationStatusHook are 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).
OpenAPI types: OperationStatusHookPayload, RequestMoneyFullOutcomePayload (hidden schema routes on the Partner API spec).

Operation status update

Sent when the Numi platform notifies RaaS of OperationCreated or OperationUpdated (POST /hooksOperationHookFlow → 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:
  1. Tenant trusted is true.
  2. At least one URL in operationStatusHook and a configured sharedSecret.
  3. rules[operationType].notifyWebHook is true for the operation type (request or send).
If notifyWebHook is false, RaaS logs and returns without POST (no error to your URL).

Payload shape (OperationStatusHookPayload)

FieldTypeAlways presentDescription
correlationIdstringYesRaaS operation id (partner correlation id when provided at creation).
statusstringYesPlatform status: Sent, Funding, Funded, Completed, Failed, OnHold, InTransit, Cancelled, etc.
statusDetailsstringNoPlatform detail (e.g. RequestSent, WaitingRecipientPaymentInfo).
datestringYesISO 8601 when the payload was built.
firstNamestringYesSender first name (empty string if unavailable).
lastNamestringYesSender last name.
fundingMethodstringYesSource payment method display name.
recipientFirstNamestringYesRecipient first name.
recipientLastNamestringYesRecipient last name.
payoutMethodstringYesDestination payment method display name.
senderPhonestringYesSender phone.
recipientPhonestringYesRecipient phone.
senderEmailstringYesSender email.
recipientEmailstringYesRecipient email.
payoutReplacementLinkstringNoLink when RaaS generated a payout replacement URL.
There is no eventType field. If you receive both event kinds on one endpoint, treat payloads without eventType as operation status updates.

Example

{
  "correlationId": "d4290fcc-ad62-445d-9071-0f68df2d",
  "status": "Completed",
  "statusDetails": "RequestSent",
  "date": "2026-06-02T15:30:00.000Z",
  "firstName": "Juan",
  "lastName": "Perez",
  "fundingMethod": "account-1234",
  "recipientFirstName": "Maria",
  "recipientLastName": "Garcia",
  "payoutMethod": "MobileWallet",
  "senderPhone": "+5213310120067",
  "recipientPhone": "+13058884000",
  "senderEmail": "sender@example.com",
  "recipientEmail": "recipient@example.com",
  "payoutReplacementLink": "https://…"
}

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:
  1. At least one URL in operationStatusHook, and
  2. A configured sharedSecret.
If either is missing, RaaS skips sending the event (it logs a warning; no HTTP call is made). Configure both before relying on async notifications.

Payload shape

FieldTypeAlways presentDescription
eventTypestringYesAlways "request_money_full_outcome".
correlationIdstringYesSame id the partner sent as request.correlationId on POST /request-money-full.
statusstringYes"Accepted" when the pipeline completed successfully, or "Failed" when it stopped with a business/platform error. Casing matches the JSON exactly (not completed / failed).
datestringYesISO 8601 timestamp when the payload was built.
errorCodestringOnly if status === "Failed"Stable machine code from the failing NumiError (e.g. ERROR_CIP_NOT_COMPLETED). Aligns with RequestMoneyFullErrorCode in the API types.
reasonstringOnly if status === "Failed"Human-readable message.
phasestringOnly if status === "Failed"High-level stage: cip, contact, payment_method, request_money, or unknown.

Example — success

{
  "eventType": "request_money_full_outcome",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "Accepted",
  "date": "2026-04-24T12:34:56.789Z"
}

Example — failure

{
  "eventType": "request_money_full_outcome",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "Failed",
  "date": "2026-04-24T12:35:10.000Z",
  "errorCode": "ERROR_CIP_NOT_COMPLETED",
  "reason": "…",
  "phase": "cip"
}

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 from POST /user/operations/request-money-full use this shape:
{ "reason": "string", "code": "string" }
Use 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, requestMoneyFullWorker in 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.