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.
| Action | Method | Path |
|---|
| Register URL | POST | /webhooks |
| List subscription | GET | /webhooks |
| Get one | GET | /webhooks/:id |
| Update URL or secret | PATCH | /webhooks/:id |
| Remove | DELETE | /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:
| Field | Required | Description |
|---|
url | Yes | Your HTTPS endpoint. We POST the event payload to this URL. |
secret | No (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. |
eventTypes | No | Array 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:
| Status | Meaning |
|---|
PENDING | Payout created or awaiting approval; not yet sent for processing. |
PROCESSING | Payout is being processed (ops, bank, or treasury). |
SUCCESSFUL | Completed; funds have been sent. |
FAILED | Processing failed (e.g. bank failure). |
REJECTED | Rejected (e.g. compliance, initiation checks, or bank). |
| Field | Description |
|---|
eventType | Event name; currently payout.status.changed.v1. |
deliveryId | Unique ID for this delivery. Use for idempotency (e.g. ignore duplicates with the same deliveryId). |
occurredAt | When the status change occurred (ISO 8601). |
data.payoutId | Payout ID (same as the id returned from Initiate Payout). |
data.businessId | Your business ID. |
data.oldStatus | Previous status (one of the five above). |
data.newStatus | New status (one of the five above). |
data.changedAt | Timestamp of the change (ISO 8601). |
data.changedBy | Optional; who or what triggered the change. |
data.reason | Optional; 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:
- Read the raw request body (do not parse and re-serialize; use the exact bytes received).
- Compute
HMAC-SHA256(rawBody, yourSecret).
- Hex-encode the result.
- 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.