WebhooksImprove this page
Fidel API uses webhooks to notify your application when relevant events happen in your account across multiple resources, namely with event types such as card.failed, card.linked, location.status, marketplace.offer.live, marketplace.offer.updated, mid-request.succeeded, mid-request.failed, missing-transaction-request.succeeded, missing-transaction-request.failed, offer-location.linked, offer-location.unlinked, program.status, transaction.auth.qualified, transaction.auth, transaction.clearing.qualified, transaction.clearing, transaction.refund.qualified, transaction.refund.match.qualified and transaction.refund.
Fidel API will notify your registered webhook URLs as the event happens, via a HTTP POST request with a signature header for verification, which needs to be received and acknowledged in a timely manner. The HTTP request contains the event object as payload.
For example, when a customer makes a payment with a linked Mastercard card, on a participating location, a transaction.auth event is sent in real-time to the specified webhook URL, with a payload that contains the Transaction object.
Management and behavior
There are two ways you can manage your webhooks, i.e., view, create, update, delete, with the Fidel API. You can create them in the Fidel Dashboard, under the "Webhooks" page or make HTTP requests using the Webhooks API.
As requirements for creation, Fidel API only accepts HTTPS URLs for webhook endpoints, thus your server must support HTTPS and have a valid certificate.
Fidel API sends the data via HTTP POST in JSON format. It will send test events if your Dashboard is in test mode or if you are using test API keys when registering the webhook URLs. To receive live events, flip your switch on the Dashboard to go live, or create the webhooks using a live API key.
Fidel API has two types of webhooks (brand and program-related), both of which work similarly, but have slightly different requirements to be registered. For customization, we also allow additional HTTP headers to be added to help integrating with your systems.
Brand-related webhooks
The brand.consent webhook only requires an URL to register. You can use the Hooks endpoint for registering brand webhooks.
Here's an example on how to create one using the Fidel API, with example.com as the URL:
123456789curl -X POST \ https://api.fidel.uk/v1/hooks \ -H 'Content-Type: application/json' \ -H 'Fidel-Key: <KEY>' \ -d '{ "event": "brand.consent", "url": "https://example.com" }'
Program-related webhooks
Program webhooks require a programId to be associated with and a URL to register. You can register up to 10 webhook URLs per event type for each program. You can use the Program Hooks endpoint for registering program webhooks. The events that can be registered for a given program are card.linked, card.failed, program.status, location.status, transaction.auth, transaction.auth.qualified, transaction.clearing, transaction.clearing.qualified, transaction.refund and transaction.refund.qualified.
Here's an example on how to create a webhook on a Program for the transaction.auth event, with example.com as the URL:
123456789curl -X POST \ https://api.fidel.uk/v1/programs/06471dbe-a3c7-429e-8a18-16dc97e5cf35/hooks \ -H 'Content-Type: application/json' \ -H 'Fidel-Key: <KEY>' \ -d '{ "event": "transaction.auth", "url": "https://example.com" }'
Receiving webhook deliveries
Return any 2xx HTTP status code within 20 seconds to acknowledge a delivery. A timeout or any non-2xx response is treated as a failed attempt.
Fidel API makes up to three attempts for each delivery: the first immediately, the second one minute after the first failure, and the third two minutes after the second failure.
Your endpoint might receive the same event more than once. Acknowledge the request before starting long-running work and process events asynchronously. Use fidel-message-id as an idempotency key so repeated attempts do not cause duplicate processing.
Each request includes fidel-message-id, fidel-attempt-number, Fidel-Request-Id, x-fidel-signature and x-fidel-timestamp headers. The message ID identifies the logical event and remains the same across automatic attempts and manual replays. The attempt number starts at 1.
Viewing delivery history
Open Webhooks > Deliveries in the Fidel API Dashboard to inspect webhook deliveries from the last 90 days. Test and live deliveries are separated by the Dashboard mode.
You can filter deliveries by status, event, program or time range; search by fidel-message-id; group related deliveries; and sort by creation time. Delivery statuses are processing, succeeded and failed.
Select a delivery to inspect its payload, metadata and individual attempts. Each attempt includes timing, status code, masked request headers and the response body.
Replaying a delivery
You can replay any completed delivery that is less than 90 days old, whether its original status is succeeded or failed.
- Open Webhooks > Deliveries.
- Select the delivery.
- Select Replay delivery and confirm the action.
A replay creates a new delivery using the stored payload and the webhook subscription's current URL and configuration. It retains the original fidel-message-id, starts again at attempt 1 and appears separately in delivery history. Replays do not delay live webhook traffic.
Custom request headers
Fidel API allows you to define custom HTTP headers when you register a webhook URL. The custom headers are included in the HTTP POST request headers that are sent to your application.
Custom headers can be defined when creating a new webhook in the Dashboard or by using the Webhooks API and setting the optional headers object with a key-value pair (see example below).
123456789101112curl -X POST \ https://api.fidel.uk/v1/programs/06471dbe-a3c7-429e-8a18-16dc97e5cf35/hooks \ -H 'Content-Type: application/json' \ -H 'Fidel-Key: <KEY>' \ -d '{ "event": "transaction.auth", "url": "https://example.com", "headers": { "Custom-Header": "my-custom-header" } }'
To delete custom headers from a registered webhook, use the Update Hooks endpoint and send an empty headers object.
1234567891011curl -X PUT \ https://api.fidel.uk/v1/hooks/3b4be60b-6596-4b40-ae3d-89b9fdaf132a \ -H 'Content-Type: application/json' \ -H 'Fidel-Key: <KEY>' \ -d '{ "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "event": "transaction.auth", "url": "https://example.com", "headers": {} }'
A maximum of 5 custom headers per webhook can be defined, and they need to follow strict character validation patterns. The key name must be between 1 and 64 characters and only accepts the Roman alphabet, numbers, dashes and underscores. The value must be between 1 and 1000 characters. Additionally, the HTTP reserved headers are blocklisted and cannot be used for the key name. The full list of blocklisted key names:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546[ "Accept-Charset", "Accept-Datetime", "Accept-Encoding", "Accept-Language", "Accept", "Access-Control-Request-Headers", "Access-Control-Request-Method", "Cache-Control", "Connection", "Content-Length", "Content-Type", "Cookie", "Date", "Expect", "Fidel-Account", "Fidel-Key", "Fidel-Live", "Fidel-Message-Id", "Fidel-Attempt-Number", "Fidel-Request-Id", "Fidel-User", "Forwarded", "From", "Host", "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards", "Origin", "Pragma", "Proxy-Authorization", "Range", "Referer", "TE", "Transfer-Encoding", "Upgrade", "User-Agent", "Via", "Warning", "X-Fidel-Signature", "X-Fidel-Timestamp" ]
Program
A program.status event is triggered in the live environment when there are updates for each Location in a Program. In the test environment, this webhook doesn't trigger because there is no concept of Location syncing there.
program.status
1234567891011121314151617{ "id": "f67b7b16-0e3d-457a-811a-cc21dc8448f3", "accountId": "77aa1f5f-7152-45dc-8eb1-cb83355e9cf9", "active": true, "activeDate": "2018-10-28T09: 33: 30.139Z", "created": "2018-10-26T17: 10: 02.509Z", "live": true, "name": "Starbucks", "syncStats": { "created": "2020-04-23T11:10:40.054Z", "locations": 2, "estimatedEndDate": "2020-05-07T11:10:40.054Z", "status": "syncing" }, "updated": "2018-10-30T16: 12: 15.604Z" }
Location
A location.status event is triggered when there are updates from a card network for a Location in a Program. In the test environment, this webhook triggers three times for each location upon creating a Location, once for each card scheme, with a location.active event. In the live environment, this would trigger whenever a location has synced successfully for a card scheme, with a location.active event. Or whenever a location has failed to sync for a card scheme, with a location.failed event.
location.status
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556{ "location": { "city": "London", "timezone": "Europe/London", "mastercard": { "estimatedActivationDate": null, "clearingTransactionId": null, "auth": false, "authTransactionId": null, "clearing": false, "status": "inactive" }, "countryCode": "GBR", "activeDate": "2020-08-18T13:40:11.406Z", "currency": "GBP", "id": "298d9c88-cabe-4583-a54b-574e29b57c84", "live": false, "address": "1 Main Street", "created": "2020-08-18T13:40:11.406Z", "postcode": "W1NN3R", "searchBy": { "merchantIds": { "visa": [], "mastercard": [] } }, "accountId": "36081095-2782-4669-8a07-857bbaaeb89b", "amex": { "estimatedActivationDate": null, "clearingTransactionId": null, "auth": false, "authTransactionId": null, "clearing": false, "status": "active" }, "visa": { "estimatedActivationDate": null, "clearingTransactionId": null, "auth": false, "authTransactionId": null, "clearing": false, "status": "inactive" }, "brandId": "9cd32c61-43ca-4bb7-8aca-0cf491112c28", "preonboard": false, "updated": "2020-08-18T13:40:11.406Z", "programId": "f2c9719a-6433-4ef4-8401-19d7ebf60ab9", "geolocation": { "latitude": 51.5138332, "longitude": -0.1318224 } }, "scheme": "amex", "status": "location.active" }
Offer
The offer-location.linked and offer-location.unlinked events are account-scoped and are triggered when a Location is linked to, or unlinked from, an Offer in your account.
offer-location.linked
1234567{ "offerId": "cf22478e-c700-4f31-b75b-38016605e2a3", "locationId": "298d9c88-cabe-4583-a54b-574e29b57c84", "brandId": "9cd32c61-43ca-4bb7-8aca-0cf491112c28", "address": "Lemonades Stand Av Liberdade" }
Card
There are two card-related events available: card.linked and card.failed. They are useful for linking a card if you want to receive the response via your server instead of via the client when using the SDK callbacks.
A card.linked event is triggered when a card is successfully linked.
card.linked
1234567891011121314151617{ "id": "556f8179-bfdb-4274-852e-ba6e43559590", "accountId": "d346d574-d5c2-4a0e-8e02-ffd813fd1a8d", "countryCode": "GBR", "created": "2018-10-29T17:01:05.013Z", "expDate": "2019-10-01T00:00:00.000Z", "expMonth": 10, "expYear": 2020, "firstNumbers": "444400", "lastNumbers": "4735", "live": false, "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "scheme": "visa", "type": "visa", "updated": "2018-10-29T17:01:05.013Z" }
A card.failed event is triggered when card linking fails. This payload includes the event name and error message.
card.failed
123456789101112131415161718192021{ "card": { "id": "c3730d4d-9915-4c51-831f-14b8aeeb56b7", "accountId": "d346c574-d5c2-4a0e-8e02-ffd713fd1a8d", "countryCode": "GBR", "created": "2018-10-29T17:34:54.693Z", "expDate": "2020-10-01T00:00:00.000Z", "expMonth": 10, "expYear": 2020, "firstNumbers": "528245", "lastNumbers": "6015", "live": true, "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "scheme": "mastercard", "type": "mastercard", "updated": "2018-10-29T17:34:56.380Z" }, "event": "card.failed", "message": "Error linking card." }
Transaction
A transaction.auth or authorisation transaction event is triggered when a transaction is carried out on a linked card. For example, when a customer is making a payment in-store in real-time. When a customer makes a payment with a linked debit/credit card in an auth-enabled location, the transaction.auth webhook is triggered and the transaction object sent to your specified URL in real-time.
transaction.auth
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162{ "id": "7fdfd5d8-9589-402f-8477-4a727ad239a2", "accountId": "4ed4b62b-aa4c-43a1-8064-da6d1368e17a", "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "datetime": "2019-03-12T19:12:01", "created": "2019-03-12T19:12:01.744Z", "updated": "2019-03-12T19:12:01.744Z", "offer": null, "approvalCode": "AA00BB", "auth": true, "authCode": "A73H890", "cardPresent": true, "cleared": false, "amount": 100, "currency": "GBP", "descriptor": { "merchantName": "Bob's Cafe London", "storeName": "Bob's Cafe" }, "wallet": null, "card": { "id": "bc538b71-31c5-4699-840a-6d4a08693314", "firstNumbers": "555500", "lastNumbers": "5001", "scheme": "visa", "metadata": { "name": "fancy card", "id": "card-id" } }, "brand": { "id": "9d136f2e-df99-4a08-a0a5-3bc1534b7db9", "name": "Bob's Cafe", "logoURL": null, "metadata": null, }, "location": { "id": "7a916fbd-70a0-462f-8dbc-bd7dbfbea160", "address": "2 Soho Square", "city": "London", "postcode": "W1D3PX", "countryCode": "GBR", "timezone": "Europe/London", "geolocation": { "latitude": 51.5152346, "longitude": -0.1310718 }, "metadata": { "id": "your-user-id", "name": "username" } }, "identifiers": { "MID": "12345678", "mastercardAuthCode": null, "mastercardTransactionSequenceNumber": "0000000000000", "mastercardRefNumber": "AABBCCDDE", "amexApprovalCode": "AA00BB", "visaAuthCode": "A73H890" } }
A transaction.clearing or clearing transaction event is triggered when a transaction is settled, usually happens 48 to 72 hours after a payment is made. The Fidel processes for clearing transactions and triggering the transaction.clearing webhook events run daily at 12:00 UTC for Mastercard and multiple times per day for Visa and American Express. Only one transaction is sent per event.
transaction.clearing
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152{ "approvalCode": "AA00BB", "auth": true, "authCode": "A73H890", "offer": null, "currency": "GBP", "id": "8bbbf56b-3819-473b-877d-cf2175f268f4", "amount": 10, "wallet": null, "created": "2020-07-08T17:05:52.567Z", "accountId": "36081095-2782-4669-8a07-857bbaaeb89b", "cardPresent": false, "cleared": true, "updated": "2020-07-08T17:20:44.134Z", "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "datetime": "2020-07-08T18:05:52", "descriptor": { "merchantName": "Bob's Cafe Bristol", "storeName": "Bob's Cafe" }, "card": { "id": "62744670-f935-4ba3-8e89-be23e31292cf", "firstNumbers": "444400", "lastNumbers": "4305", "scheme": "visa" }, "location": { "address": "5 Main St", "city": "Bristol", "countryCode": "GBR", "id": "0ff99cfd-9e8b-48fe-af55-2519ffe1c0a4", "geolocation": null, "postcode": "BS2 5BL", "timezone": "Europe/London", "metadata": null }, "brand": { "id": "966481e7-dd6f-44d2-83fa-98783aaacf40", "name": "Bob's Cafe", "logoURL": null, "metadata": null }, "identifiers": { "MID": "TEST_MID_d74bc1ca-cd6e-409d-ba8e-549a214dfb0a", "mastercardTransactionSequenceNumber": null, "mastercardRefNumber": null, "mastercardAuthCode": null, "amexApprovalCode": "AA00BB", "visaAuthCode": "A73H890" } }
A transaction.refund or refund transaction event is triggered when a transaction is refunded. Besides the transaction.refund event, a refunded transaction also triggers a transaction.clearing event. On both, their auth property is set to false and the amount is negative. Fidel API tries to identify the initial transaction for which the refund was issued, using cardId, locationId, merchantId, amount and datetime. If an associated initial transaction is identified, the webhook data contains the originalTransactionId. If no initial transaction is identified, the data comes in on both webhooks with a negative amount but no originalTransactionId property.
transaction.refund
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253{ "approvalCode": "AA00BB", "auth": false, "authCode": "A73H890", "originalTransactionId": "8bbbf56b-3819-473b-877d-cf2175f268f4", "currency": "GBP", "descriptor": { "merchantName": "Bob's Cafe Bristol", "storeName": "Bob's Cafe" }, "id": "5ec08ca8-38c6-42e1-9fa5-32c67e4135b2", "amount": -10, "wallet": null, "created": "2020-07-08T17:23:13.972Z", "accountId": "36081095-2782-4669-8a07-857bbaaeb89b", "cardPresent": false, "cleared": true, "updated": "2020-07-08T17:23:13.972Z", "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "datetime": "2020-07-08T18:23:13", "card": { "id": "62744670-f935-4ba3-8e89-be23e31292cf", "firstNumbers": "444400", "lastNumbers": "4305", "scheme": "visa" }, "location": { "address": "5 Main St", "city": "Bristol", "countryCode": "GBR", "id": "0ff99cfd-9e8b-48fe-af55-2519ffe1c0a4", "geolocation": null, "postcode": "BS2 5BL", "timezone": "Europe/London", "metadata": null }, "brand": { "id": "966481e7-dd6f-44d2-83fa-98783aaacf40", "name": "Bob's Cafe", "logoURL": null, "metadata": null }, "identifiers": { "MID": "TEST_MID_d74bc1ca-cd6e-409d-ba8e-549a214dfb0a", "mastercardTransactionSequenceNumber": null, "mastercardRefNumber": null, "amexApprovalCode": null, "mastercardAuthCode": null, "amexApprovalCode": "AA00BB", "visaAuthCode": "A73H890" } }
There are three additional transaction webhook events: transaction.auth.qualified, transaction.clearing.qualified and transaction.refund.qualified. They are triggered when an auth, clearing or a refund transaction is qualified for an Offer.
transaction.auth.qualified and transaction.clearing.qualified events are triggered only if transactions they are inside of the offer's period and pass the set of rules defined in the offer. transaction.refund.qualified events might be emitted even when the offer has expired due to the complex matching logic of the refund to the original transaction.
The payload for these events includes the offer object with the results of the qualification, and you can read more about it in the Offers API documentation.
123456789"offer": { "qualified": true, "id": "eeefb94b-d11c-44db-81d7-d86a9fcc4069", "message": [], "qualificationDate": null, "cashback": 2.5, "performanceFee": 0.3 }
You can filter transactions from a specific offer by setting the offerId optional property when creating a transaction qualification webhook event.
12345678910curl -X POST \ https://api.fidel.uk/v1/programs/06471dbe-a3c7-429e-8a18-16dc97e5cf35/hooks \ -H 'Content-Type: application/json' \ -H 'Fidel-Key: <KEY>' \ -d '{ "event": "transaction.auth.qualified", "url": "https://example.com", "offerId": "cf22478e-c700-4f31-b75b-38016605e2a3" }'
In order to help clients understand which refund they can debit back to their customers, Fidel has created the transaction.refund.match.qualified webhook, which is only triggered if Fidel is able to find a refund for a previously qualified transaction and match it to the originalTransactionId.
This means that partial refunds will not be identified as the cashback amount is one of the triggers for Fidel to match the refund to the original transaction. This webhook provides the cashback amount, which the client can then debit to the cardholder. You can read more about refund matching in the Refunds documentation. Please see the full payload below:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667{ "approvalCode": "AA00BB", "auth": false, "authCode": "A73H890", "currency": "GBP", "descriptor": { "merchantName": "Bob's Cafe Bristol", "storeName": "Bob's Cafe" }, "id": "5ec08ca8-38c6-42e1-9fa5-32c67e4135b2", "amount": -10, "wallet": null, "created": "2020-07-08T17:23:13.972Z", "accountId": "36081095-2782-4669-8a07-857bbaaeb89b", "cardPresent": false, "cleared": true, "updated": "2020-07-08T17:23:13.972Z", "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "datetime": "2020-07-08T18:23:13", "originalTransactionId": "8bbbf56b-3819-473b-877d-cf2175f268f4", "originalTransaction": { "id": "8bbbf56b-3819-473b-877d-cf2175f268f4", "brandId": "966481e7-dd6f-44d2-83fa-98783aaacf40", "programId": "06471dbe-a3c7-429e-8a18-16dc97e5cf35", "amount": 10, "currency": "GBP", "datetime": "2020-07-05T18:23:13", "locationId": "0ff99cfd-9e8b-48fe-af55-2519ffe1c0a4", "cardId": "62744670-f935-4ba3-8e89-be23e31292cf", "offer": { "qualified": true, "id": "eeefb94b-d11c-44db-81d7-d86a9fcc4069", "cashback": 2.5 } }, "card": { "id": "62744670-f935-4ba3-8e89-be23e31292cf", "firstNumbers": "444400", "lastNumbers": "4305", "scheme": "visa" }, "location": { "address": "5 Main St", "city": "Bristol", "countryCode": "GBR", "id": "0ff99cfd-9e8b-48fe-af55-2519ffe1c0a4", "geolocation": null, "postcode": "BS2 5BL", "timezone": "Europe/London", "metadata": null }, "brand": { "id": "966481e7-dd6f-44d2-83fa-98783aaacf40", "name": "Bob's Cafe", "logoURL": null, "metadata": null }, "identifiers": { "MID": "TEST_MID_d74bc1ca-cd6e-409d-ba8e-549a214dfb0a", "mastercardTransactionSequenceNumber": null, "mastercardRefNumber": null, "mastercardAuthCode": null, "amexApprovalCode": "AA00BB", "visaAuthCode": "A73H890" } }
Verifying signatures
If you want to confirm that incoming requests on your webhook URL are coming from the Fidel API, we recommend verifying webhook signatures. We send the x-fidel-signature and x-fidel-timestamp HTTP headers for each request we make to a webhook URL.
Fidel API generates a unique secret key for each webhook you register. The key is returned in the response's secretKey property if you are using the Webhooks API. You can also copy the key from the Fidel Dashboard's Webhooks page by clicking in the Show Key button next to your webhook endpoint. To verify a webhook request, generate a signature using the same key that the Fidel API uses and compare that to the value of the x-fidel-signature header.
Replay attacks are a common MITM attack vector where a valid payload and its signature is intercepted and re-transmitted. If you want to safeguard against them, use the x-fidel-timestamp header and reject requests outside your accepted tolerance. We recommend a five-minute tolerance.
To verify a request:
- Concatenate the raw request body, webhook URL and timestamp value from the
x-fidel-timestampheader. - Double hash the resulting string using the webhook
secretKeywith HMAC-SHA256 and encode it in Base-64. - Compare the signature you generated with the signature provided in the
x-fidel-signatureheader using a constant-time comparison.
Example JavaScript implementation
123456789101112131415161718192021222324252627/** fidelHeaders - x-fidel-signature and x-fidel-timestamp headers rawBody - raw request body secret - webhook secretKey url - webhook URL */ function isSignatureValid(fidelHeaders, rawBody, secret, url) { function base64Digest(s) { return crypto.createHmac("sha256", secret).update(s).digest("base64"); } const timestamp = fidelHeaders["x-fidel-timestamp"]; const timestampAge = Math.abs(Date.now() - Number(timestamp)); if (!Number.isFinite(timestampAge) || timestampAge > 5 * 60 * 1000) { return false; } const content = rawBody + url + timestamp; const signature = base64Digest(base64Digest(content)); const received = Buffer.from(fidelHeaders["x-fidel-signature"]); const expected = Buffer.from(signature); return received.length === expected.length && crypto.timingSafeEqual(received, expected); }
Use the exact raw request body when calculating the signature. Parsing and re-serializing JSON can change the content and cause verification to fail.
Deleting Webhooks
You can delete webhooks in the Fidel API Dashboard, or using the API's Delete Webhook endpoint.
12345curl -X DELETE \ https://api.fidel.uk/v1/hooks/b9ef3795-a38f-4ef2-8d8d-293dd7fbe1a7 \ -H 'content-type: application/json' \ -H 'fidel-key: sk_test_50ea90b6-2a3b-4a56-814d-1bc592ba4d63'
API Reference
If you're looking to find out more about our Webhooks API and how to use it with your application, please visit the Fidel API Reference.