Webhooks
Endpoints for managing outbound webhooks: CRUD, enable/disable, signing-secret rotation, synchronous test ping, delivery log inspection and retry, and sample event generation. On create both `id` and `secret` are generated server-side and returned in the PUT response — capture the secret then, as it is the same value used to sign every subsequent delivery. Every delivery is signed with an HMAC-SHA256 of `{timestamp}.{payload}` using the webhook secret; receivers should verify the `Contit-Signature: t={timestamp},s={hex}` header. Test pings additionally carry `Contit-Test: true`.
Base URL: https://api.contit.cloud
/webhooks
Retrieve all webhooks
Retrieves all webhooks configured for the current workspace.
ReadAccess policy
Responses
(array) |
WebhookModel[] | Array of configured webhooks |
Code examples
curl -X GET "https://api.contit.cloud/webhooks" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var response = await http.GetAsync("https://api.contit.cloud/webhooks");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks",
{
method: "GET",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.get(
"https://api.contit.cloud/webhooks",
headers=headers
)
data = response.json()
GET /webhooks
[
{
"id": "wh_a1b2c3d4",
"endpoint": "https://example.com/hooks/contit",
"secret": "wh_d4f6e9c2a1b8...",
"eventTypes": {
"article": [0, 1, 2]
},
"useCustomPayload": false,
"customPayload": null,
"description": "Production article sync",
"errorEmailTo": "[email protected]",
"enabled": true
}
]
/webhooks/{id}
Retrieve webhook by ID
Retrieves a single webhook by its ID.
ReadAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Responses
(object) |
WebhookModel | The webhook configuration |
Code examples
curl -X GET "https://api.contit.cloud/webhooks/{id}" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var response = await http.GetAsync("https://api.contit.cloud/webhooks/{id}");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}",
{
method: "GET",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.get(
"https://api.contit.cloud/webhooks/{id}",
headers=headers
)
data = response.json()
GET /webhooks/wh_a1b2c3d4
{
"id": "wh_a1b2c3d4",
"endpoint": "https://example.com/hooks/contit",
"secret": "wh_d4f6e9c2a1b8...",
"eventTypes": {
"article": [0, 1, 2]
},
"useCustomPayload": false,
"customPayload": null,
"description": "Production article sync",
"errorEmailTo": "[email protected]",
"enabled": true
}
/webhooks
Create or update webhook
Creates a new webhook or updates an existing one (matched by `id`). The `endpoint` field is required. On **create**, both `id` and `secret` are generated automatically by the API (the secret has the form `wh_` + 32 random chars) and returned in the response — read it from the response body immediately because the same value is reused for HMAC signing on every delivery. Passing a non-empty `secret` in the body overrides the autogenerated one (useful only if you want to import an existing secret). On **update**, omitting `secret` (or sending an empty value) preserves the current one; pass an explicit value to replace it, or use `POST /webhooks/{id}/rotate-secret` to regenerate it server-side. The `eventTypes` map keys are content-type keys and values are arrays of `WebhookEventType` enum values: `0` Published, `1` Unpublished, `2` Deleted, `3` PublishedCreated, `4` PublishedUpdated.
WriteAccess policy
Request Body application/json
| Property | Type | Required | Description |
|---|---|---|---|
id |
string | — | Webhook ID. Omit (or empty) to create a new one. |
endpoint |
string | ✓ | Absolute HTTPS URL the webhook will POST to |
secret |
string | — | Signing secret used to compute the HMAC-SHA256 signature. Auto-generated on create if omitted (returned in the response). On update, omit to preserve the existing one. |
eventTypes |
object | — | Map of `contentTypeKey` -> array of WebhookEventType values that trigger this webhook |
useCustomPayload |
boolean | — | If true, the delivery body is built from `customPayload` instead of the default content envelope |
customPayload |
object | — | JSON template used when `useCustomPayload` is true |
description |
string | — | Free-text description shown in the admin UI |
errorEmailTo |
string | — | Comma-separated list of email addresses notified on repeated delivery failures |
enabled |
boolean | — | Whether the webhook is active. Disabled webhooks are configured but do not fire. |
Responses
(object) |
WebhookModel | The persisted webhook configuration |
Code examples
curl -X PUT "https://api.contit.cloud/webhooks" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "id_value",
"endpoint": "endpoint_value",
"secret": "secret_value",
"eventTypes": "eventTypes_value",
"useCustomPayload": true,
"customPayload": "customPayload_value",
"description": "description_value",
"errorEmailTo": "errorEmailTo_value",
"enabled": true
}'
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{
\"id\": \"id_value\",
\"endpoint\": \"endpoint_value\",
\"secret\": \"secret_value\",
\"eventTypes\": \"eventTypes_value\",
\"useCustomPayload\": true,
\"customPayload\": \"customPayload_value\",
\"description\": \"description_value\",
\"errorEmailTo\": \"errorEmailTo_value\",
\"enabled\": true
}",
Encoding.UTF8, "application/json");
var response = await http.PutAsync(
"https://api.contit.cloud/webhooks", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks",
{
method: "PUT",
headers: {
"X-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
"id": "id_value",
"endpoint": "endpoint_value",
"secret": "secret_value",
"eventTypes": "eventTypes_value",
"useCustomPayload": true,
"customPayload": "customPayload_value",
"description": "description_value",
"errorEmailTo": "errorEmailTo_value",
"enabled": true
})
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
payload = {
"id": "id_value",
"endpoint": "endpoint_value",
"secret": "secret_value",
"eventTypes": "eventTypes_value",
"useCustomPayload": true,
"customPayload": "customPayload_value",
"description": "description_value",
"errorEmailTo": "errorEmailTo_value",
"enabled": true
}
response = requests.put(
"https://api.contit.cloud/webhooks",
json=payload,
headers=headers
)
data = response.json()
{
"endpoint": "https://example.com/hooks/contit",
"description": "Production article sync",
"errorEmailTo": "[email protected]",
"enabled": true,
"eventTypes": {
"article": [0, 1, 2]
},
"useCustomPayload": false
}
{
"id": "wh_a1b2c3d4",
"endpoint": "https://example.com/hooks/contit",
"secret": "wh_d4f6e9c2a1b8...",
"eventTypes": {
"article": [0, 1, 2]
},
"useCustomPayload": false,
"customPayload": null,
"description": "Production article sync",
"errorEmailTo": "[email protected]",
"enabled": true
}
/webhooks/{id}
Delete webhook
Deletes a webhook by its ID. Existing delivery log entries are retained.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Responses
Code examples
curl -X DELETE "https://api.contit.cloud/webhooks/{id}" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.DeleteAsync(
"https://api.contit.cloud/webhooks/{id}", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}",
{
method: "DELETE",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.delete(
"https://api.contit.cloud/webhooks/{id}",
headers=headers
)
data = response.json()
DELETE /webhooks/wh_a1b2c3d4
/webhooks/{id}/enable
Enable webhook
Sets `enabled = true` on the webhook so that future matching events will be delivered.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Responses
(object) |
WebhookModel | The updated webhook configuration |
Code examples
curl -X POST "https://api.contit.cloud/webhooks/{id}/enable" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.PostAsync(
"https://api.contit.cloud/webhooks/{id}/enable", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/enable",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.post(
"https://api.contit.cloud/webhooks/{id}/enable",
headers=headers
)
data = response.json()
POST /webhooks/wh_a1b2c3d4/enable
{
"id": "wh_a1b2c3d4",
"endpoint": "https://example.com/hooks/contit",
"enabled": true
}
/webhooks/{id}/disable
Disable webhook
Sets `enabled = false`. The webhook configuration is preserved but no deliveries are queued until it is enabled again.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Responses
(object) |
WebhookModel | The updated webhook configuration |
Code examples
curl -X POST "https://api.contit.cloud/webhooks/{id}/disable" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.PostAsync(
"https://api.contit.cloud/webhooks/{id}/disable", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/disable",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.post(
"https://api.contit.cloud/webhooks/{id}/disable",
headers=headers
)
data = response.json()
POST /webhooks/wh_a1b2c3d4/disable
{
"id": "wh_a1b2c3d4",
"endpoint": "https://example.com/hooks/contit",
"enabled": false
}
/webhooks/{id}/rotate-secret
Rotate webhook secret
Regenerates the signing secret of the webhook and returns the new value. The previous secret is invalidated immediately — update the receiver's verification logic before invoking this in production.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Responses
(object) |
WebhookModel | The webhook with the new `secret` value |
Code examples
curl -X POST "https://api.contit.cloud/webhooks/{id}/rotate-secret" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.PostAsync(
"https://api.contit.cloud/webhooks/{id}/rotate-secret", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/rotate-secret",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.post(
"https://api.contit.cloud/webhooks/{id}/rotate-secret",
headers=headers
)
data = response.json()
POST /webhooks/wh_a1b2c3d4/rotate-secret
{
"id": "wh_a1b2c3d4",
"endpoint": "https://example.com/hooks/contit",
"secret": "wh_NEWa7f3e1d2c9b6...",
"enabled": true
}
/webhooks/{id}/test
Test webhook endpoint
Sends a synchronous HTTP POST to the configured endpoint with a small signed test payload (`eventType: "webhook.test"`). Adds `Contit-Test: true` and the standard `Contit-Signature` header. Returns the response status, body (truncated to 2000 chars) and duration. Times out after 10 seconds. Does **not** persist a delivery record in the event log.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Responses
isSuccessStatusCode |
boolean | True if the endpoint returned a 2xx status |
statusCode |
integer | HTTP status code returned by the endpoint (0 if the request never reached the server, e.g. DNS error or timeout) |
message |
string | Response body (truncated to 2000 chars) or client-side error message |
durationMs |
integer | Wall-clock duration of the request in milliseconds |
Code examples
curl -X POST "https://api.contit.cloud/webhooks/{id}/test" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.PostAsync(
"https://api.contit.cloud/webhooks/{id}/test", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/test",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.post(
"https://api.contit.cloud/webhooks/{id}/test",
headers=headers
)
data = response.json()
POST /webhooks/wh_a1b2c3d4/test
{
"isSuccessStatusCode": true,
"statusCode": 200,
"message": "{\"ok\":true}",
"durationMs": 184
}
/webhooks/{id}/events
Retrieve webhook delivery log
Returns the delivery log of a webhook, paginated and ordered newest-first. Supports optional filters: `status` (process status), `eventType`, `contentType`, `from`/`to` (insert date range, ISO-8601). `pageSize` is capped at 100.
ReadAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
page |
integer | — | 1-based page number (default 1) |
pageSize |
integer | — | Items per page (default 20, max 100) |
status |
WebhookProcessStatus | — | Filter by process status: -1 Error, 0 Waiting, 1 Processing, 2 Sent |
eventType |
WebhookEventType | — | Filter by event type: 0 Published, 1 Unpublished, 2 Deleted, 3 PublishedCreated, 4 PublishedUpdated |
contentType |
string | — | Filter by content-type key |
from |
datetime | — | Lower bound on insert date (ISO-8601, inclusive) |
to |
datetime | — | Upper bound on insert date (ISO-8601, inclusive) |
Responses
items |
WebhookEventLogModel[] | Delivery entries on the current page |
page |
integer | Current page (1-based) |
pageSize |
integer | Items per page |
total |
integer | Total matching deliveries across all pages |
Code examples
curl -X GET "https://api.contit.cloud/webhooks/{id}/events" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var response = await http.GetAsync("https://api.contit.cloud/webhooks/{id}/events");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/events",
{
method: "GET",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.get(
"https://api.contit.cloud/webhooks/{id}/events",
headers=headers
)
data = response.json()
GET /webhooks/wh_a1b2c3d4/events?page=1&pageSize=20&status=-1
{
"items": [
{
"id": "evt_9b8c7d6e5f4a",
"webhookId": "wh_a1b2c3d4",
"dateInsert": "2026-05-15T14:02:11Z",
"lastUpdate": "2026-05-15T14:02:13Z",
"eventType": 0,
"contentId": "c_d4f6e9c2",
"contentType": "article",
"fields": { "title": "Hello world" },
"translations": { "it": "Ciao mondo" },
"customPayload": null,
"processStatus": -1,
"responseStatusCode": 502,
"isResponseSuccessStatusCode": false,
"responseMessage": "Bad Gateway"
}
],
"page": 1,
"pageSize": 20,
"total": 1
}
/webhooks/{id}/events/{eventId}
Retrieve webhook delivery
Retrieves a single webhook delivery by its ID. Returns 404 if the event does not exist or does not belong to the given webhook.
ReadAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
eventId |
string | ✓ | Delivery ID |
Responses
(object) |
WebhookEventLogModel | The delivery record (same shape as items in the events list) |
Code examples
curl -X GET "https://api.contit.cloud/webhooks/{id}/events/{eventId}" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var response = await http.GetAsync("https://api.contit.cloud/webhooks/{id}/events/{eventId}");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/events/{eventId}",
{
method: "GET",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.get(
"https://api.contit.cloud/webhooks/{id}/events/{eventId}",
headers=headers
)
data = response.json()
GET /webhooks/wh_a1b2c3d4/events/evt_9b8c7d6e5f4a
{
"id": "evt_9b8c7d6e5f4a",
"webhookId": "wh_a1b2c3d4",
"dateInsert": "2026-05-15T14:02:11Z",
"lastUpdate": "2026-05-15T14:02:13Z",
"eventType": 0,
"contentId": "c_d4f6e9c2",
"contentType": "article",
"fields": { "title": "Hello world" },
"translations": { "it": "Ciao mondo" },
"customPayload": null,
"processStatus": 2,
"responseStatusCode": 200,
"isResponseSuccessStatusCode": true,
"responseMessage": "OK"
}
/webhooks/{id}/events/{eventId}/retry
Retry webhook delivery
Re-enqueues an existing delivery for processing. A new delivery record is created in `Waiting` status using the original payload snapshot; the original entry is left untouched for audit.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
eventId |
string | ✓ | Delivery ID to retry |
Responses
Code examples
curl -X POST "https://api.contit.cloud/webhooks/{id}/events/{eventId}/retry" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.PostAsync(
"https://api.contit.cloud/webhooks/{id}/events/{eventId}/retry", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/events/{eventId}/retry",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.post(
"https://api.contit.cloud/webhooks/{id}/events/{eventId}/retry",
headers=headers
)
data = response.json()
POST /webhooks/wh_a1b2c3d4/events/evt_9b8c7d6e5f4a/retry
/webhooks/{id}/generate-sample/{contentTypeId}
Generate sample webhook event
Generates a sample `Published` delivery for the given content type and enqueues it on the webhook. Useful to exercise the receiver end-to-end without touching real content. The sample payload contains a synthetic content ID and the content-type's field keys with `null` values.
WriteAccess policy
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Webhook ID |
contentTypeId |
string | ✓ | Content type ID to base the sample payload on |
Responses
Code examples
curl -X POST "https://api.contit.cloud/webhooks/{id}/generate-sample/{contentTypeId}" \
-H "X-Api-Key: YOUR_API_KEY"
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var content = new StringContent("{}",
Encoding.UTF8, "application/json");
var response = await http.PostAsync(
"https://api.contit.cloud/webhooks/{id}/generate-sample/{contentTypeId}", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
const response = await fetch(
"https://api.contit.cloud/webhooks/{id}/generate-sample/{contentTypeId}",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
}
}
);
const data = await response.json();
import requests
headers = {"X-Api-Key": "YOUR_API_KEY"}
response = requests.post(
"https://api.contit.cloud/webhooks/{id}/generate-sample/{contentTypeId}",
headers=headers
)
data = response.json()
POST /webhooks/wh_a1b2c3d4/generate-sample/article