Webhooks API Reference
Webhooks API Reference
Webhooks allow your application to receive real-time HTTP POST notifications when events occur in the Groundtruth Platform. You register endpoint URLs, select which events to subscribe to, and the platform sends signed payloads to your endpoints as events happen.
Base URL: /api/webhooks
Authentication: All webhook management endpoints require cookie-based session authentication only. You cannot use an API key to manage webhooks.
Available Events
| Event | Description |
|---|---|
engagement.started | An engagement run has begun |
engagement.completed | An engagement run finished successfully |
engagement.failed | An engagement run failed with an error |
deliverable.ready | A deliverable has been generated |
deliverable.approved | A deliverable was approved in the review workflow |
deliverable.revision_requested | A revision was requested for a deliverable |
deliverable.rejected | A deliverable was rejected in the review workflow |
portal.comment_received | A comment was left on the client portal |
quality.scored | A quality score was assigned to a task |
pattern_analysis.completed | Cross-engagement pattern analysis finished |
account.deletion_requested | An account deletion request was initiated |
attachment.uploaded | A file was uploaded to an engagement |
report.generated | A PDF report was generated |
List Webhook Endpoints
GET /api/webhooksReturns all webhook endpoints for the authenticated tenant, including recent delivery history.
Response
{
"webhooks": [
{
"id": "clxyz400whk",
"url": "https://example.com/webhooks/groundtruth",
"events": ["engagement.completed", "deliverable.ready"],
"enabled": true,
"description": "Production webhook",
"createdAt": "2025-05-20T10:00:00.000Z",
"recentDeliveries": [
{
"id": "clxyz410del",
"event": "engagement.completed",
"statusCode": 200,
"durationMs": 245,
"createdAt": "2025-06-01T12:00:00.000Z"
}
]
}
]
}Example
curl -X GET https://app.groundtruth.ai/api/webhooks \
-H "Cookie: sb-access-token=..."Create Webhook Endpoint
POST /api/webhooksCreates a new webhook endpoint. The webhook secret is returned only once in the response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The endpoint URL. Must use HTTPS. |
events | string[] | Yes | Array of event types to subscribe to |
description | string | No | Human-readable description |
Response
Status: 201 Created
{
"id": "clxyz401whk",
"url": "https://example.com/webhooks/groundtruth",
"events": ["engagement.completed", "deliverable.ready"],
"enabled": true,
"description": "Production webhook",
"secret": "whsec_a1b2c3d4e5f6a1b2c3d4e5f6",
"createdAt": "2025-06-01T14:00:00.000Z"
}IMPORTANT: The
secretfield (format:whsec_+ 24 hex characters) is only returned once at creation time. Store it securely. You will need it to verify webhook signatures.
Example
curl -X POST https://app.groundtruth.ai/api/webhooks \
-H "Cookie: sb-access-token=..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/groundtruth",
"events": ["engagement.completed", "deliverable.ready", "engagement.failed"],
"description": "Production webhook"
}'Errors
| Status | Description |
|---|---|
| 400 | Invalid URL (must be HTTPS), missing events, or invalid event type |
Get Webhook Endpoint
GET /api/webhooks/:idReturns a single webhook endpoint with its full delivery history.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook endpoint ID |
Response
{
"webhook": {
"id": "clxyz400whk",
"url": "https://example.com/webhooks/groundtruth",
"events": ["engagement.completed", "deliverable.ready"],
"enabled": true,
"description": "Production webhook",
"createdAt": "2025-05-20T10:00:00.000Z",
"deliveries": [
{
"id": "clxyz410del",
"event": "engagement.completed",
"statusCode": 200,
"responseBody": "{\"ok\":true}",
"durationMs": 245,
"createdAt": "2025-06-01T12:00:00.000Z"
}
]
}
}Example
curl -X GET https://app.groundtruth.ai/api/webhooks/clxyz400whk \
-H "Cookie: sb-access-token=..."Update Webhook Endpoint
PATCH /api/webhooks/:idUpdates an existing webhook endpoint.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook endpoint ID |
Request Body
All fields are optional. Only include fields you want to change.
| Field | Type | Description |
|---|---|---|
url | string | New endpoint URL (must be HTTPS) |
events | string[] | New event subscriptions (replaces existing list) |
enabled | boolean | Enable or disable the endpoint |
description | string | Updated description |
Response
{
"webhook": {
"id": "clxyz400whk",
"url": "https://example.com/webhooks/groundtruth-v2",
"events": ["engagement.completed", "engagement.failed"],
"enabled": true,
"description": "Updated production webhook",
"createdAt": "2025-05-20T10:00:00.000Z"
}
}Example
curl -X PATCH https://app.groundtruth.ai/api/webhooks/clxyz400whk \
-H "Cookie: sb-access-token=..." \
-H "Content-Type: application/json" \
-d '{
"events": ["engagement.completed", "engagement.failed"],
"description": "Updated production webhook"
}'Delete Webhook Endpoint
DELETE /api/webhooks/:idPermanently deletes a webhook endpoint and all its delivery history.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook endpoint ID |
Response
{
"success": true
}Example
curl -X DELETE https://app.groundtruth.ai/api/webhooks/clxyz400whk \
-H "Cookie: sb-access-token=..."Send Test Ping
POST /api/webhooks/:id/testSends a test ping event to the webhook endpoint to verify connectivity. The test payload uses the event type ping.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook endpoint ID |
Response
{
"success": true,
"statusCode": 200,
"durationMs": 312
}Example
curl -X POST https://app.groundtruth.ai/api/webhooks/clxyz400whk/test \
-H "Cookie: sb-access-token=..."Errors
| Status | Description |
|---|---|
| 200 | Test sent, but the remote endpoint may have returned an error. Check statusCode in the response. |
| 404 | Webhook endpoint not found |
Signature Verification
Every webhook delivery includes a cryptographic signature so you can verify that the request originated from the Groundtruth Platform and was not tampered with.
Request Headers
| Header | Description |
|---|---|
X-Groundtruth-Signature | Signature in the format t=<timestamp>,v1=<signature> |
X-Groundtruth-Event | The event type (e.g., engagement.completed) |
Payload Format
{
"event": "engagement.completed",
"data": {
"engagementId": "clxyz500eng",
"status": "completed",
"runId": "clxyz600run"
},
"timestamp": "2025-06-01T12:00:00.000Z"
}How Signatures Work
The signature is computed as an HMAC-SHA256 digest of the string {timestamp}.{body}, using your webhook secret as the key.
- Extract the timestamp (
t) and signature (v1) from theX-Groundtruth-Signatureheader - Construct the signed message:
{timestamp}.{raw_request_body} - Compute the HMAC-SHA256 of the message using your webhook secret
- Compare the computed signature with the
v1value using a timing-safe comparison
Node.js Verification Example
const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
const [tPart, v1Part] = signature.split(',');
const timestamp = tPart.split('=')[1];
const expectedSig = v1Part.split('=')[1];
const message = `${timestamp}.${body}`;
const computed = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(expectedSig)
);
}
// Usage in an Express handler:
app.post('/webhooks/groundtruth', (req, res) => {
const signature = req.headers['x-groundtruth-signature'];
const event = req.headers['x-groundtruth-event'];
const body = req.body; // raw string body
if (!verifyWebhookSignature(body, signature, process.env.GROUNDTRUTH_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(body);
console.log(`Received ${event}:`, payload.data);
// Process the event...
res.status(200).json({ ok: true });
});Delivery Behavior
| Property | Value |
|---|---|
| Timeout | 10 seconds per delivery attempt |
| Method | POST |
| Content-Type | application/json |
Delivery Logging
Each delivery is logged with:
- HTTP status code returned by your endpoint
- Response body (first 1,000 characters)
- Duration in milliseconds
You can view delivery history via the GET /api/webhooks/:id endpoint or in the Groundtruth dashboard.