Skip to content

Webhooks API

Webhooks let you receive real-time HTTP notifications when events happen in your Comvi projects. Instead of polling the API, you register a URL and Comvi sends a POST request to it whenever a subscribed event fires.


Subscribe to any combination of these events:

Event Description
translation.created A translation value was added
translation.updated A translation value was changed
translation.deleted A translation value was removed
translation.batch_updated Multiple translations were updated at once
key.created A new translation key was created
key.updated A translation key was renamed or modified
key.deleted A translation key was deleted
namespace.created A new namespace was created
namespace.updated A namespace was renamed
namespace.deleted A namespace was deleted
import.completed A translation import finished processing
export.completed A translation export finished processing
comment.created A comment was added to a translation
comment.resolved A comment was resolved
project.locale_added A language was added to the project
project.locale_removed A language was removed from the project
webhook.test Sent when you trigger a test delivery
webhook.disabled The webhook was automatically disabled due to consecutive failures

Returns all webhooks configured for a project.

GET /api/v1/projects/:projectId/webhooks

Path parameters

Parameter Type Required Description
projectId integer Yes Project ID

Example request

Terminal window
curl -X GET \
-H "Authorization: Bearer comvi_your_api_key" \
https://api.comvi.io/api/v1/projects/5/webhooks

Example response

[
{
"id": 1,
"uuid": "wh_abc123",
"organizationId": 1,
"projectId": 5,
"name": "Translation Updates",
"url": "https://example.com/webhooks/comvi",
"events": ["translation.created", "translation.updated"],
"isActive": true,
"consecutiveFailures": 0,
"lastTriggeredAt": "2025-03-25T10:00:00Z",
"lastSuccessAt": "2025-03-25T10:00:00Z",
"createdBy": 42,
"createdAt": "2025-01-15T08:00:00Z",
"updatedAt": "2025-01-15T08:00:00Z"
}
]

Register a new webhook endpoint for a project. The response includes a secret that you use to verify webhook signatures.

POST /api/v1/projects/:projectId/webhooks

Path parameters

Parameter Type Required Description
projectId integer Yes Project ID

Request body

Field Type Required Description
name string Yes Descriptive name (1-255 characters)
url string Yes HTTPS endpoint URL to receive events (up to 2048 characters)
events string[] Yes Event types to subscribe to (at least one)
isActive boolean No Whether the webhook is active (default: true)

Example request

Terminal window
curl -X POST \
-H "Authorization: Bearer comvi_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Translation Sync",
"url": "https://example.com/webhooks/comvi",
"events": ["translation.created", "translation.updated", "translation.deleted"]
}' \
https://api.comvi.io/api/v1/projects/5/webhooks

Example response

{
"id": 2,
"uuid": "wh_def456",
"organizationId": 1,
"projectId": 5,
"name": "Translation Sync",
"url": "https://example.com/webhooks/comvi",
"events": ["translation.created", "translation.updated", "translation.deleted"],
"isActive": true,
"consecutiveFailures": 0,
"lastTriggeredAt": null,
"lastSuccessAt": null,
"createdBy": 42,
"createdAt": "2025-03-25T12:00:00Z",
"updatedAt": "2025-03-25T12:00:00Z",
"secret": "whsec_a1b2c3d4e5f6g7h8i9j0..."
}

Update a webhook’s name, URL, subscribed events, or active status.

PATCH /api/v1/webhooks/:id

Path parameters

Parameter Type Required Description
id integer Yes Webhook ID

Request body

Field Type Required Description
name string No Updated name
url string No Updated endpoint URL
events string[] No Updated event subscriptions
isActive boolean No Enable or disable the webhook

Example request

Terminal window
curl -X PATCH \
-H "Authorization: Bearer comvi_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"events": ["translation.created", "translation.updated", "translation.deleted", "key.created"],
"isActive": true
}' \
https://api.comvi.io/api/v1/webhooks/2

Example response

{
"id": 2,
"uuid": "wh_def456",
"organizationId": 1,
"projectId": 5,
"name": "Translation Sync",
"url": "https://example.com/webhooks/comvi",
"events": ["translation.created", "translation.updated", "translation.deleted", "key.created"],
"isActive": true,
"consecutiveFailures": 0,
"lastTriggeredAt": null,
"lastSuccessAt": null,
"createdBy": 42,
"createdAt": "2025-03-25T12:00:00Z",
"updatedAt": "2025-03-25T12:30:00Z"
}

Permanently remove a webhook. No further events will be delivered to this endpoint.

DELETE /api/v1/webhooks/:id

Path parameters

Parameter Type Required Description
id integer Yes Webhook ID

Example request

Terminal window
curl -X DELETE \
-H "Authorization: Bearer comvi_your_api_key" \
https://api.comvi.io/api/v1/webhooks/2

Response

Returns 204 No Content on success.


Send a test event to your webhook endpoint. This creates a real delivery with the webhook.test event type so you can verify your integration works.

POST /api/v1/webhooks/:id/test

Path parameters

Parameter Type Required Description
id integer Yes Webhook ID

Example request

Terminal window
curl -X POST \
-H "Authorization: Bearer comvi_your_api_key" \
https://api.comvi.io/api/v1/webhooks/1/test

Example response

{
"id": 100,
"uuid": "del_abc123",
"webhookId": 1,
"event": "webhook.test",
"payload": {},
"status": "success",
"httpStatus": 200,
"attemptNumber": 1,
"responseBody": "OK",
"errorMessage": null,
"durationMs": 145,
"sentAt": "2025-03-25T12:00:00Z",
"completedAt": "2025-03-25T12:00:00Z",
"nextRetryAt": null,
"createdAt": "2025-03-25T12:00:00Z"
}

Every webhook delivery includes a signature in the X-Webhook-Signature header. Verify it using your webhook secret to ensure the request came from Comvi.

import crypto from 'crypto';
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/webhooks/comvi', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.COMVI_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the event
const { event, data } = req.body;
console.log(`Received ${event}:`, data);
res.status(200).send('OK');
});

Comvi tracks every delivery attempt. If your endpoint returns a non-2xx status code, Comvi retries the delivery with exponential backoff:

Attempt Delay
1st retry ~1 minute
2nd retry ~5 minutes
3rd retry ~30 minutes

After multiple consecutive failures, the webhook is automatically disabled and a webhook.disabled event is recorded. Re-enable it from the dashboard or via the Update webhook endpoint.


Status Error Code Description
403 FORBIDDEN Missing webhook permission
404 WEBHOOK_NOT_FOUND The specified webhook does not exist
404 WEBHOOK_DELIVERY_NOT_FOUND The specified delivery does not exist