Skip to main content
A webhook is a POST call TapiPay makes to an endpoint of yours when something you care about happens: a payment settles, a charge is declined, or a charge is reversed. That way you find out right away, without polling the API.
Webhooks are not emitted by the API, which does not implement them. They are emitted by TapiPay’s charging platforms, and they are configured during onboarding: there is no endpoint to register them or change the URL.

The events

Route on the type field. Every event arrives at the same endpoint of yours, so type is what tells one from another. Do not infer the event from the fields the payload carries.With one caveat: on the payment notification, type describes what was charged, not what happened. What tells you whether that payment settled, failed or was reversed is its status field. On the other five, the type already identifies the fact.
The payment notification comes from the referenced payment platform and has its own contract, different from the rest: its status is lowercase, its type identifies the kind of operation rather than the event, and its configuration is documented on that page. The other five share everything below.

A failure and a chargeback are not the same

The five events in this family split into two groups, and it is worth not confusing them: they say different things about the same charge.

Failure

The charge never went through. These arrive with status: "FAILED".

Chargeback

The charge had gone through and was later reversed. These carry no status, because they do not describe the state of a payment but a later fact about an already processed charge.

Common fields

The five events share this base:
companyCode, companyName and hash are always present as a key, even when they are null. You can read them without checking existence, but you do have to handle the null before using the value.

The verification hash

Your endpoint is public, so anyone can call it. The hash field lets you confirm the request comes from TapiPay and that the payload was not tampered with. It is generated by encrypting with RSA against the public key you hand over during onboarding. If you do not configure a publicKey, the field still arrives, with a null value.
Do not assume hash carries a value. Check that it is not null before using it. If your integration relies on it to authenticate, an unexpected null means the public key was not configured: confirm it with TapiPay before going live.
The hash is one mechanism among several. The others (API key, bearer token, IP allowlist, mTLS) are configured the same way for every webhook and are detailed in Payment notification.

Error detail is optional

Card events can include the error code and message the provider returned, inside an additionalData object. Whether they arrive depends on a flag in your configuration, sendErrorInfo, set during onboarding.
additionalData is dropped entirely when it would be empty. It does not arrive as an empty object or with null fields: the key is simply not there. Check that it exists before reading inside it.
It is dropped in two cases: when sendErrorInfo is off, and when it is on but the provider reported no detail.
Bank debit events work differently: the decline detail (bankErrorCode, bankErrorDescription) always travels, at the root of the payload and without depending on sendErrorInfo.

Idempotency

TapiPay can retry a notification, so your endpoint has to tolerate receiving the same event more than once. The key is operationId.
Store processed operationId values in persistent storage (a table with a unique index, or Redis), never in process memory: if you restart the server between the original attempt and the retry, an in-memory marker is lost and the event is processed twice.
Your endpoint requirements (return 2xx, in under 5 seconds, reachable from TapiPay’s IPs) are the same for every webhook and live in Payment notification.
Every delivery attempt is recorded on TapiPay’s side, along with its result, so a notification you believe was lost can be traced.