Skip to main content
Webhooks let you receive real-time HTTP notifications when events occur in Entri. Instead of polling the API to check for changes, you register a URL and Entri will POST a JSON payload to it whenever a subscribed event fires. Webhooks are useful for triggering downstream workflows — for example, automatically pulling translations into your codebase when a language reaches 100% completion, or sending a Slack notification when an import finishes.

Supported Events

EventDescription
key.createdA translation key was created.
key.deletedA translation key was deleted.
translation.completedA translation was saved (source: human or AI).
language.completedAll keys in a language have been translated.
import.completedA file import job finished.
export.completedA file export was generated.

Endpoints

Organization-level webhooks
POST   /api/organizations/:orgId/webhooks                   Create a webhook
GET    /api/organizations/:orgId/webhooks                   List webhooks
PATCH  /api/organizations/:orgId/webhooks/:webhookId        Update a webhook
DELETE /api/organizations/:orgId/webhooks/:webhookId        Delete a webhook
POST   /api/organizations/:orgId/webhooks/:webhookId/test   Send a test payload
Project-level webhooks
POST   /api/projects/:projectId/webhooks                    Create a project webhook
GET    /api/projects/:projectId/webhooks                    List project webhooks
PATCH  /api/projects/:projectId/webhooks/:webhookId         Update a project webhook
DELETE /api/projects/:projectId/webhooks/:webhookId         Delete a project webhook

Create a Webhook

When you create a webhook, Entri generates a signing secret for you. The secret is returned only once in the creation response — store it immediately in a secure location.
curl -X POST https://api.nt3.io/api/organizations/org_789xyz/webhooks \
  -H "Cookie: session=..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/entri",
    "events": ["translation.completed", "language.completed", "import.completed"],
    "projectId": "proj_6abc123def456",
    "description": "Notify CI/CD pipeline"
  }'
You do not provide a secret — Entri auto-generates one with crypto.randomBytes(32). The secret field in the response is shown only once.
Response:
{
  "_id": "wh_abc123",
  "url": "https://your-app.example.com/webhooks/entri",
  "events": ["translation.completed", "language.completed", "import.completed"],
  "projectId": "proj_6abc123def456",
  "description": "Notify CI/CD pipeline",
  "active": true,
  "secret": "a1b2c3d4e5f6...",
  "failureCount": 0,
  "created": "2025-03-01T10:00:00.000Z"
}

Webhook Payload Structure

Every webhook POST has a JSON body with consistent structure:
{
  "event": "translation.completed",
  "timestamp": "2025-03-02T14:30:00.000Z",
  "data": {
    "keyId": "key_abc123",
    "key": "nav.home",
    "language": "fr",
    "value": "Accueil"
  }
}

Request Headers

Each webhook delivery includes these headers:
HeaderDescription
Content-Typeapplication/json
X-Webhook-SignatureHMAC-SHA256 hex signature of the request body (see Verification below)
X-Webhook-EventThe event type (e.g. translation.completed)

Payload Verification

Entri signs every webhook request with an HMAC-SHA256 signature using the auto-generated webhook secret. The signature is included in the X-Webhook-Signature header as a plain hex string (no prefix):
X-Webhook-Signature: a1b2c3d4e5f6...
Verify the signature in your receiver before processing the payload:
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyWebhook(rawBody, signature, secret) {
  const expected = createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const expectedBuffer = Buffer.from(expected);
  const signatureBuffer = Buffer.from(signature);

  if (expectedBuffer.length !== signatureBuffer.length) return false;
  return timingSafeEqual(expectedBuffer, signatureBuffer);
}
Always use timingSafeEqual when comparing signatures. String equality (===) is vulnerable to timing attacks.

Test a Webhook

Send a test delivery to verify your endpoint is reachable:
curl -X POST https://api.nt3.io/api/organizations/org_789xyz/webhooks/wh_abc123/test \
  -H "Cookie: session=..."
The test delivery sends an event with type webhook.test:
{
  "event": "webhook.test",
  "timestamp": "2025-03-02T14:30:00.000Z",
  "data": {
    "message": "This is a test webhook delivery"
  }
}

Key Notes

  • Your endpoint must respond with a 2xx status code within 10 seconds to be considered a successful delivery.
  • Failed deliveries are not automatically retried. Monitor failureCount on the webhook object and investigate delivery failures promptly.
  • Webhooks can be scoped to a specific project by setting projectId, or created at the organization level to receive events across all projects.