Skip to main content
Dispute webhooks notify your endpoint about disputes raised on your payments — when a customer opens a dispute, when you challenge it, and when it is resolved. This lets you react to disputes without polling the Disputes API.
Disputes have no test mode: dispute webhooks are registered with a live secret key and are sent for live payments only (the same as the Disputes API).

How They Work

1

Register an endpoint

Register a dispute webhook for each merchant_code — the merchant code is passed in the X-Merchant-Code header, which is required on every dispute-webhook request:
KSA merchants call https://api.tabby.sa/api/v1/dispute-webhooks. Each merchant_code can have up to 4 dispute webhooks — the limit is separate from payment webhooks. The optional header is added to every notification so you can verify its origin.
Registration is self-service. The same operations as for payment webhooks — register, list, retrieve, update and remove — are available under /api/v1/dispute-webhooks, see the API reference. A dispute webhook receives all dispute events of the merchant; there is no event filter.
2

Receive notifications

Tabby sends a POST request to your URL whenever a dispute is opened on one of your payments or its status changes.
3

Acknowledge with 200

Respond with a 200 HTTP status code to confirm the reception, and check the auth header to verify the request. Any other response (or no response) counts as a delivery error and triggers retries.

Differences From Payment Webhooks

A few rules to keep in mind when managing dispute webhooks:
  • URLs must be publicly reachable. localhost, raw IP addresses and host names that do not resolve are rejected with 400 invalid webhook url. Use HTTPS — the secret in header travels with every notification.
  • URLs are normalised and unique per merchant. The scheme and host are lower-cased, a default port and a trailing slash are dropped before the URL is stored — https://Store.com/hook/ and https://store.com/hook are the same webhook, and registering it twice returns 400 webhook already exists. The path itself is case-sensitive.
  • PUT replaces the whole object. Send both url and header when updating; a request without header removes the header.
  • The header value is a secret. Tabby stores it in full but returns it masked in every response — keep your own copy of the value your endpoint validates against.

Payload

A dispute webhook is a POST request with a JSON body that links the dispute to the affected payment:

Dispute statuses

The status field tells you what happened to the dispute:

Delivery

Dispute webhooks are delivered to the URLs you registered under /api/v1/dispute-webhooks (separate from your payment webhooks), using the same delivery mechanism as payment webhooks — optional authentication header, retry policy, and server IPs. In particular:
  • Acknowledge each delivery with 200 and process it asynchronously — see Best Practices.
  • Delivery order is not guaranteed and a notification may occasionally be delivered twice — deduplicate by dispute_id + status.
  • Failed deliveries are retried — see Retry Attempts.