hide_table_of_contents: false title: Best Practices For Webhooks sidebar_label: Best Practices custom_edit_url: null
Best Practices For Webhooks
1. Secure your webhook endpoint
- Use HTTPS: We strongly recommend that your webhook endpoint registered with the Fynd Platform uses HTTPS. Encrypting communication with HTTPS ensures that data transmitted between the Fynd Platform provider and your server is secure. This prevents man-in-the-middle attacks where an attacker intercepts and potentially alters the data.
- Implement Integrity Checks: We strongly recommend you validate the incoming request's HMAC (Hash-based Message Authentication Code) signature before processing the payload. This ensures that the payload is indeed from the Fynd Platform and not has been tampered or altered with. Each webhook request contains the
x-fp-signature
HTTP request header, which is calculated by the Fynd Platform using the following code:
<CodeBlock language="javascript" code={const { createHmac } = require('crypto'); const hmac = createHmac("sha256", "<secret-value>"); // this secret is obtained from your extension details page present in partner panel hmac.update(JSON.stringify(payload)); const hmacHex = hmac.digest("hex");
}>
Before processing the webhook request, please calculate the HMAC using the same logic/algorithm and ensure that the resultant signature matches the value of `x-fp-signature` header.
<Admonition>
The secret used for creating the HMAC can be obtained from going into you **[Partner panel](https://partners.fynd.com/organizations) → Extensions → your-extension → API Secret**.
</Admonition>
<img src="https://cdn.pixelbin.io/v2/falling-surf-7c8bb8/original/image_(16).png" alt="QG1" style={{border: "1px solid #ccc;", borderRadius: "5px"}}/>
2. Ensure idempotency and handle retries
Fynd Platform's webhook system follows an "at least once delivery" semantics. This means that it is quite possible that the same webhook request may be sent to your webhook endpoint multiple times. Thus, it is strongly recommended that your webhook handler be written in an idempotent manner and be able to gracefully handle retries.
Idempotency
Design your webhook handlers to be idempotent. This means that receiving the same webhook request multiple times should have the same effect (on your side) as receiving it once. There are multiple ways to make this happen:
-
Most accurate (but cumbersome): The most accurate way to ensure that the webhook handler is idempotent is to identify a "marker" or a "trace" in your system that can help determine whether the webhook payload has already been processed or not. This will likely vary for each webhook payload and depend on how your system handles it. We usually do not recommend this method because, if not implemented carefully, it can easily introduce subtle bugs. For example:
-
If you are listening to the
order.created
event, you might check whether the incomingorder_id
is present in your database (DB), to determine if this payload has already been handled by your system. -
On the other hand, if you are listening to the
order.updated
event, you may want to compare the incomingupdated_at
timestamp with theexternal_updated_at
timestamp in your orders DB to determine whether this particular event has already been handled by your system.
-
-
Easy to implement (but not completely accurate): All webhook requests sent by the Fynd Platform include an
x-fp-event-id
header, which is unique for each request and it is preserved across retries. This header can be used to implement a straightforward (and centralized/common) logic to check if you have already handled a webhook request, or not. Our recommendation would be to implement this as HTTP middleware in your technology stack:-
Maintain a cache (say in Redis, or even in your DB) of all the
x-fp-event-id
s that you have processed. -
Before processing a webhook request, check if the incoming
x-fp-event-id
is present in the cache. -
If it is present, emit a
202 Accepted
HTTP response to indicate that the webhook was accepted, but not immediately acted upon. -
If it is not present, process the webhook, add the
x-fp-event-id
to the cache, and emit a200 OK
HTTP response. Be careful to make an entry in this cache only after a webhook event is successfully processed and a 200 OK response is sent back, not before. In most HTTP-based middleware this is achieved by implementing anafter_filter
(orafter_hook
). -
For the cache, please use a TTL of 50 hours, because currently, a webhook request can be retried up to approx 49 hours only (explained in the next section). Remember to delete items from this cache after 50 hours, especially if you are maintaining it in the DB, otherwise you may end-up with a constantly growing dataset.
<CodeBlock language="javascript" code={`async function handleIdempotency(req, res) { // Extract the unique event identifier from the request headers const eventId = req.headers['x-event-id']; try { // Check if the event ID has already been processed const isProcessed = await checkIfProcessed(eventId);
if (isProcessed) { // If the event has already been processed, return 202 Accepted return res.status(202).send('Event already processed'); } // Process the webhook here // Example: console.log('Processing webhook:', req.body); // After successful processing, mark the event as processed await markAsProcessed(eventId); // Send 200 OK after successful processing res.status(200).send('Webhook processed successfully'); } catch (error) { console.error('Error handling webhook:', error); res.status(500).send('Internal Server Error'); }
}`}>
-
Retries
-
Each event is retried a maximum of 10 times using an exponential-backoff algorithm. The last (i.e., tenth) retry occurs approximately 49 hours after the original attempt, after which that particular webhook request is marked as permanently failed and no longer retried.
-
Webhooks are always sent with the most recent data for the given resource; however, in rare circumstances, you might experience delays in receiving webhooks. We send a header with
x-fp-attempt
, which can be used to identify delays. We also send acreated_timestamp
in the payload that can be used to verify if the event was received out of order. -
Upon failure (of any kind), webhooks will be retried in the following manner:
3. Handle payloads efficiently
-
Parsing and Processing: Ensure your webhook endpoint is capable of parsing JSON payloads quickly. This involves using efficient libraries and methods to deserialize the JSON data and process it. Keeping the processing lightweight helps avoid timeouts and maintain quick response times. Be prepared to handle large payloads (up to 5 MB in size).
-
Acknowledgment: After successfully processing a webhook event, your endpoint should respond with a
2XX
status code within a time frame of 20 seconds, or the webhook request will be marked as a failure (and will be retried). A2XX
response indicates that the event has been received and processed correctly, preventing unnecessary retries. -
Lightweight Endpoint: Design your webhook endpoint to be lightweight and responsive. Avoid long-running processes and offload heavy tasks to background jobs or asynchronous processing systems. This ensures your endpoint can respond quickly to incoming webhooks, reducing the risk of timeouts.
-
Appropriate Response Codes: Return appropriate HTTP status codes based on the result of processing the webhook. Use
2XX
codes for successful processing and5XX
codes for server errors.
4. Additional Tips
- Logging: Maintain detailed logs of all webhook events received. This helps with debugging issues and provides an audit trail for monitoring and security purposes.