> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nt3.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks Overview

> Receive real-time notifications when translations are updated.

Webhooks let you subscribe to events in Entri and receive an HTTP POST request to your server whenever something important happens — a key is created, a translation is completed, or an import finishes. Use webhooks to trigger CI pipelines, sync external systems, or notify your team.

## Creating a Webhook

<Steps>
  <Step title="Open project settings">
    Navigate to your project, click **Settings**, then select the **Webhooks** tab. Alternatively, create organization-level webhooks under **Organization Settings → Webhooks**.
  </Step>

  <Step title="Add a new endpoint">
    Click **Add Webhook** and enter the URL of your server endpoint. It must be publicly accessible and accept `POST` requests.
  </Step>

  <Step title="Select events">
    Choose which events should trigger this webhook. You can subscribe to individual events or all events.
  </Step>

  <Step title="Save and copy the secret">
    Click **Save**. Entri auto-generates a signing secret and shows it **once** — copy it immediately and store it securely. You will need it to verify webhook signatures.
  </Step>

  <Step title="Test the endpoint">
    Use the **Test** button to send a `webhook.test` event to your endpoint and verify it is reachable.
  </Step>
</Steps>

## Supported Events

| Event                   | When it fires                                            |
| ----------------------- | -------------------------------------------------------- |
| `key.created`           | A new translation key was created in the project         |
| `key.deleted`           | A translation key was deleted from the project           |
| `translation.completed` | A single translation was saved (human or AI)             |
| `language.completed`    | All translations for a language reached completed status |
| `import.completed`      | An import job finished processing                        |
| `export.completed`      | An export job finished and the file is ready             |

## Webhook Payload

Every event is delivered as a JSON object with a consistent envelope:

```json theme={null}
{
  "event": "translation.completed",
  "timestamp": "2026-02-19T14:32:00.000Z",
  "data": {
    "keyId": "key_01hx3k9f2vbmqztjd8ab",
    "key": "welcome.title",
    "language": "fr",
    "value": "Bienvenue sur Entri"
  }
}
```

## Request Headers

Each webhook delivery includes these headers:

| Header                | Description                                   |
| --------------------- | --------------------------------------------- |
| `Content-Type`        | `application/json`                            |
| `X-Webhook-Signature` | HMAC-SHA256 hex signature of the request body |
| `X-Webhook-Event`     | The event type (e.g. `translation.completed`) |

## Security — Verifying Signatures

Entri signs every webhook request so you can confirm the payload came from Entri and was not tampered with. The signature is in the `X-Webhook-Signature` header as a plain hex string.

<Steps>
  <Step title="Retrieve your signing secret">
    Find the signing secret shown when you created the webhook. Keep it private — it cannot be retrieved after creation.
  </Step>

  <Step title="Compute the expected signature">
    Compute an HMAC-SHA256 digest of the raw request body using your signing secret as the key.
  </Step>

  <Step title="Compare signatures">
    Compare your computed digest to the value in `X-Webhook-Signature`. Reject the request if they do not match.
  </Step>
</Steps>

<Tabs>
  <Tab title="Node.js">
    ```js theme={null}
    import crypto from 'crypto'

    function verifySignature(secret, rawBody, signatureHeader) {
      const expected = crypto
        .createHmac('sha256', secret)
        .update(rawBody)
        .digest('hex')
      return crypto.timingSafeEqual(
        Buffer.from(expected),
        Buffer.from(signatureHeader)
      )
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hmac, hashlib

    def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
        expected = hmac.new(
            secret.encode(), raw_body, hashlib.sha256
        ).hexdigest()
        return hmac.compare_digest(expected, signature_header)
    ```
  </Tab>
</Tabs>

## Delivery and Failures

If your endpoint returns a non-2xx status code or does not respond within 10 seconds, the delivery is marked as failed. **Entri does not automatically retry failed webhook deliveries.** Monitor the `failureCount` field on the webhook object and investigate delivery failures promptly.

<Warning>
  Make sure your endpoint responds quickly (ideally under 3 seconds) by acknowledging the request immediately and processing the payload asynchronously. Long-running handlers can cause timeout failures.
</Warning>

For full API reference including creating, updating, and deleting webhooks programmatically, see [Webhooks API Reference](/api-reference/endpoints/webhooks).
