Skip to main content

Overview

You can receive payout status change notifications at a URL you provide. When a payout’s status changes, we send an HTTP POST to your endpoint with the event payload. We use five consolidated statuses: PENDING, PROCESSING, SUCCESSFUL, FAILED, and REJECTED.
  • One webhook URL per business — you can register a single HTTPS endpoint; we send all payout status events for your business to that URL.
  • Optional signing — you can provide a secret when registering; we then sign each request so you can verify it came from Yala. We strongly recommend setting a secret and verifying the signature to protect your endpoint against spoofed webhooks.
  • Use the same API key (and base URL) as for the rest of the Payout API when managing your webhook subscription.

Subscribe via API

Use the Payout API with your existing x-api-key to manage your webhook URL. Base path: Same as payouts — https://gateway.staging.useyala.com/v1/payout-api (sandbox) or https://gateway.useyala.com/v1/payout-api (production). Webhook endpoints live under /webhooks.
ActionMethodPath
Register URLPOST/webhooks
List subscriptionGET/webhooks
Get oneGET/webhooks/:id
Update URL or secretPATCH/webhooks/:id
RemoveDELETE/webhooks/:id

Register a webhook URL

POST /v1/payout-api/webhooks HTTP/1.1
Host: gateway.staging.useyala.com
Content-Type: application/json
x-api-key: <YOUR_API_KEY>
Request body:
FieldRequiredDescription
urlYesYour HTTPS endpoint. We POST the event payload to this URL.
secretNo (recommended)Shared secret used to sign requests. If set, we send X-Yala-Signature so you can verify the payload. Recommended to protect against spoofed webhooks.
eventTypesNoArray of event types to receive. Omit or leave empty to receive all payout events (currently payout.status.changed.v1).
Example:
{
  "url": "https://your-server.com/webhooks/payout",
  "secret": "whsec_your_secret_for_signing"
}
Only one active webhook URL is allowed per business. To change it, update the existing subscription (PATCH) or delete and create a new one.

Payload we send

Each notification is an HTTP POST with:
  • Content-Type: application/json
  • Body: JSON object with the shape below.
  • Headers we add:
    • X-Yala-Delivery-Id — unique ID for this delivery (use it to deduplicate if you receive the same event more than once).
    • X-Yala-Signature — only present if you set a secret; format sha256=<hex>. See Verifying the signature.

Payload shape

{
  "eventType": "payout.status.changed.v1",
  "deliveryId": "550e8400-e29b-41d4-a716-446655440000",
  "occurredAt": "2025-02-10T12:00:00.000Z",
  "data": {
    "payoutId": "payout-uuid",
    "businessId": "business-uuid",
    "oldStatus": "PENDING",
    "newStatus": "PROCESSING",
    "changedAt": "2025-02-10T12:00:00.000Z",
    "changedBy": "ops",
    "reason": "Approved for processing"
  }
}
Status values — We send exactly five statuses so you can integrate simply:
StatusMeaning
PENDINGPayout created or awaiting approval; not yet sent for processing.
PROCESSINGPayout is being processed (ops, bank, or treasury).
SUCCESSFULCompleted; funds have been sent.
FAILEDProcessing failed (e.g. bank failure).
REJECTEDRejected (e.g. compliance, initiation checks, or bank).
FieldDescription
eventTypeEvent name; currently payout.status.changed.v1.
deliveryIdUnique ID for this delivery. Use for idempotency (e.g. ignore duplicates with the same deliveryId).
occurredAtWhen the status change occurred (ISO 8601).
data.payoutIdPayout ID (same as the id returned from Initiate Payout).
data.businessIdYour business ID.
data.oldStatusPrevious status (one of the five above).
data.newStatusNew status (one of the five above).
data.changedAtTimestamp of the change (ISO 8601).
data.changedByOptional; who or what triggered the change.
data.reasonOptional; human-readable reason.

Verifying the signature

If you registered a secret, we sign each request with HMAC-SHA256 of the raw request body (the exact JSON string we send). We send the hex-encoded digest in the header:
X-Yala-Signature: sha256=a1b2c3...
How to verify:
  1. Read the raw request body (do not parse and re-serialize; use the exact bytes received).
  2. Compute HMAC-SHA256(rawBody, yourSecret).
  3. Hex-encode the result.
  4. Compare with the value in X-Yala-Signature after the sha256= prefix.
If they match, the request came from Yala and the body was not modified in transit.
Use the raw body. Small differences in JSON formatting (e.g. key order or spacing) will change the signature. Always verify against the exact bytes you receive.

Best practices

  • Respond quickly — Return HTTP 2xx as soon as you have accepted the payload. Process the event asynchronously if needed so we don’t time out.
  • Deduplicate — Store deliveryId and ignore requests you have already processed so duplicate deliveries do not trigger duplicate actions.
  • Set a secret and verify the signature — We strongly recommend setting a secret when registering and always verifying X-Yala-Signature before trusting the payload. Without this, your endpoint could accept spoofed webhooks from third parties.
  • Use HTTPS only — We only send to HTTPS URLs. Keep your endpoint and secret secure.

Delivery retries

If your endpoint does not return HTTP 2xx, we retry delivery automatically.
  • Total attempts: 5
  • Backoff schedule: 1 minute, 5 minutes, 15 minutes, 1 hour, 4 hours
  • Stop condition: Any HTTP 2xx response marks the delivery as successful and stops retries
  • Final state: After the 5th failed attempt, the delivery is marked as failed

Managing your subscription

  • List: GET /v1/payout-api/webhooks returns your current subscription(s). The response does not include the secret.
  • Update: PATCH /v1/payout-api/webhooks/:id with url, secret, eventTypes, or isActive as needed.
  • Remove: DELETE /v1/payout-api/webhooks/:id stops notifications to that URL.
All of these use the same authentication as the rest of the Payout API (x-api-key).
You can also manage your webhook URL from the Yala app under Settings → Payout API Keys (webhook section). The API and the app stay in sync.