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:

EventDescription
translation.createdA translation value was added
translation.updatedA translation value was changed
translation.deletedA translation value was removed
translation.batch_updatedMultiple translations were updated at once
key.createdA new translation key was created
key.updatedA translation key was renamed or modified
key.deletedA translation key was deleted
namespace.createdA new namespace was created
namespace.updatedA namespace was renamed
namespace.deletedA namespace was deleted
import.completedA translation import finished processing
export.completedA translation export finished processing
comment.createdA comment was added to a translation
comment.resolvedA comment was resolved
project.locale_addedA language was added to the project
project.locale_removedA language was removed from the project
webhook.testSent when you trigger a test delivery
webhook.disabledThe webhook was automatically disabled due to consecutive failures

Returns all webhooks configured for a project.

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

Path parameters

ParameterTypeRequiredDescription
projectIdintegerYesProject ID

Example request

Terminal window
curl -X GET \
-H "X-API-Key: tlk_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

ParameterTypeRequiredDescription
projectIdintegerYesProject ID

Request body

FieldTypeRequiredDescription
namestringYesDescriptive name (1-255 characters)
urlstringYesHTTPS endpoint URL to receive events (up to 2048 characters)
eventsstring[]YesEvent types to subscribe to (at least one)
isActivebooleanNoWhether the webhook is active (default: true)

Example request

Terminal window
curl -X POST \
-H "X-API-Key: tlk_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

ParameterTypeRequiredDescription
idintegerYesWebhook ID

Request body

FieldTypeRequiredDescription
namestringNoUpdated name
urlstringNoUpdated endpoint URL
eventsstring[]NoUpdated event subscriptions
isActivebooleanNoEnable or disable the webhook

Example request

Terminal window
curl -X PATCH \
-H "X-API-Key: tlk_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

ParameterTypeRequiredDescription
idintegerYesWebhook ID

Example request

Terminal window
curl -X DELETE \
-H "X-API-Key: tlk_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

ParameterTypeRequiredDescription
idintegerYesWebhook ID

Example request

Terminal window
curl -X POST \
-H "X-API-Key: tlk_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:

AttemptDelay
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.


StatusError CodeDescription
403FORBIDDENMissing webhook permission
404WEBHOOK_NOT_FOUNDThe specified webhook does not exist
404WEBHOOK_DELIVERY_NOT_FOUNDThe specified delivery does not exist