# Webhooks

Webhooks allow your system to receive real-time notifications when an event occurs in your Fynd account. Instead of polling the [Orders API](/spec#/Orders/getOrderDownload) to check if a document is ready, Fynd can push a notification to your server as soon as the fulfillment is complete.

## Configuration

You can manage your webhook settings from the [Settings](/api-management) page.

1.  **Select an API Client:** Webhooks are configured per API Client.
2.  **Endpoint URL:** Provide a public HTTPS URL on your server where you want to receive POST requests (e.g., `https://api.yourdomain.com/webhooks/fynd`).
3.  **Enable Webhooks:** Click **Enable Webhooks** to save the URL, turn delivery on, and generate a fresh webhook secret.
4.  **Webhook Secret:** Once webhooks are enabled, you can rotate the secret separately at any time. This secret is used to verify that requests are actually coming from Fynd.

If you disable webhooks later, Fynd keeps the saved endpoint URL and secret so you can re-enable the integration without re-entering them.

> **Security Note:**
> Like API Keys, webhook secrets are shown only once upon generation. Store them securely in your environment variables or secret manager.

## Supported Events

Fynd supports the following events:

### `order.fulfillment.completed`

Sent when an order created via the API has been successfully fulfilled and the document is ready for download.

**Payload Example:**

```json
{
  "eventId": "707e776b-0ee7-4ff7-b90d-8c84070bf12e",
  "occurredAt": "2026-03-18T08:00:00.000Z",
  "eventType": "order.fulfillment.completed",
  "apiClientId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "orderId": "9a3b2c1d-4e5f-6789-abcd-ef0123456789",
  "orderReference": "ORD-2026-00123",
  "downloadPath": "/api/v0/orders/9a3b2c1d-4e5f-6789-abcd-ef0123456789"
}
```

Use `downloadPath` as a relative path against the Fynd API base URL to retrieve the fulfilled document.

### `order.fulfillment.failed`

Sent when an order item fails to fulfil.

**Payload Example:**

```json
{
  "eventId": "707e776b-0ee7-4ff7-b90d-8c84070bf12e",
  "occurredAt": "2026-03-18T08:00:00.000Z",
  "eventType": "order.fulfillment.failed",
  "apiClientId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "orderId": "9a3b2c1d-4e5f-6789-abcd-ef0123456789",
  "orderReference": "ORD-2026-00123",
  "errorMessage": "Order item fulfillment failed. Our support is investigating the issue."
}
```

## Request Headers

Each webhook delivery includes the following HTTP headers:

| Header               | Description                                                                         |
| -------------------- | ----------------------------------------------------------------------------------- |
| `X-Fynd-Signature`   | HMAC-SHA256 hex digest of the raw request body, signed with your Webhook Secret     |
| `X-Fynd-Event`       | Event type, e.g. `order.fulfillment.completed`                                      |
| `X-Fynd-Delivery-Id` | UUID of the webhook delivery (same as `eventId` in the payload)                     |
| `X-Fynd-Timestamp`   | ISO 8601 timestamp of when the event occurred (same as `occurredAt` in the payload) |

## Security & Signature Verification

Fynd signs every webhook payload to ensure authenticity. The signature is sent in the `X-Fynd-Signature` HTTP header.

### Verifying the Signature

The signature is a `HMAC-SHA256` hash of the raw request body using your **Webhook Secret** as the key.

To verify the signature in Node.js:

```javascript
const { createHmac, timingSafeEqual } = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  const sigBuf = Buffer.from(signature);
  const expBuf = Buffer.from(expected);
  if (sigBuf.length !== expBuf.length || !timingSafeEqual(sigBuf, expBuf)) {
    return false;
  }
  return true;
}

// In your route handler:
// const signature = request.headers['x-fynd-signature'];
// const isValid = verifyWebhook(request.rawBody, signature, process.env.FYND_WEBHOOK_SECRET);
```

> **Important:** Always compare buffer lengths before calling `timingSafeEqual`. If the lengths differ, `timingSafeEqual` will throw rather than return `false`.

## Reliability & Retries

- **HTTP Responses:** Your endpoint should return a `2xx` status code quickly to acknowledge receipt.
- **Timeouts:** Fynd considers a request failed if your server does not respond within 10 seconds.
- **Retries:** If your server is down or returns an error, Fynd will automatically retry the delivery — up to 6 attempts in total.
- **Monitoring:** You can view recent delivery attempts and failure reasons in the **Activity** tab of the [Settings](/api-management) screen.

## Best Practices

- **Idempotency:** Webhooks may occasionally be delivered more than once. Ensure your implementation handles duplicate `eventId` values gracefully.
- **Secure Endpoint:** Always use HTTPS for your webhook URL.
- **Immediate Acknowledgment:** Perform heavy processing (like downloading the document) asynchronously after returning a `200 OK` to Fynd.
