Webhooks
Receive a server’s lifecycle events as HTTP POSTs
Register a URL and DataGrout POSTs to it whenever one of your server’s runs or background tasks finishes, or a tool call fails. These are the same events a WebSocket subscriber receives on ws:<server_uuid>:events, delivered to an endpoint you host instead — the shape a workflow platform’s trigger node needs.
Endpoints
Each endpoint is available in two forms, with the same authentication as the server’s MCP endpoint:
| Form | Base path | Use when |
|---|---|---|
| Per-server | /api/v1/servers/{server_uuid} |
You hold an access token, an mTLS identity or an OAuth token for the server |
| Token-scoped | /api/v1 |
You connected by OAuth; the server is the one your token was granted (a per-server access token is refused here) |
| Method | Path | Response |
|---|---|---|
POST |
/webhooks |
201 — the new webhook, including its secret |
GET |
/webhooks/{id} |
200 — the webhook (no secret), or 404 |
DELETE |
/webhooks/{id} |
204, or 404 |
POST |
/webhooks/{id}/test |
200 — a sample event was sent; see Send a test event |
A webhook belongs to one server. Any other server’s credential gets the same 404 as for a webhook that never existed.
Register a webhook
curl -X POST https://gateway.datagrout.ai/api/v1/servers/YOUR_UUID/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.example.com/datagrout", "events": ["run.completed", "task.failed"]}'
| Field | Required | Meaning |
|---|---|---|
url |
yes | Where to POST events. Must be https, and must not name a private, loopback or link-local address. |
events |
no | Event names to deliver. Omit or pass [] for every event. |
source |
no | A label for what registered it, e.g. the name of your integration. |
{
"id": "6f1c1f3e-7a7e-4f9b-9d4c-1b2a3c4d5e6f",
"url": "https://hooks.example.com/datagrout",
"events": ["run.completed", "task.failed"],
"secret": "q8ZJv3Wb…",
"created_at": "2026-10-16T12:00:00Z"
}
Store the secret. It is returned only in this response, and every delivery carries it.
Send a test event
curl -X POST https://gateway.datagrout.ai/api/v1/servers/YOUR_UUID/webhooks/WEBHOOK_ID/test \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"event": "run.completed"}'
Sends one delivery of event to the webhook’s URL right away, exactly as a real one is sent, with placeholder values in data (run_id is 0, ids end in _sample). event defaults to the first event the webhook is subscribed to, or run.completed if it receives every event; an event the webhook does not receive is a 422.
{"event": "run.completed", "delivered": true, "response_status": 200}
delivered is true for any 2xx. response_status is the receiver’s status, or null with a delivery_error when it could not be reached. The result counts toward the webhook’s delivery record like any other delivery.
Errors
Errors use the gateway’s usual shape:
{"error": {"code": 404, "type": "not_found", "message": "Webhook not found"}}
An invalid registration is a 422 with the failing fields in details:
{
"error": {
"code": 422,
"type": "invalid_request",
"message": "Invalid webhook: url must use https",
"details": {"url": ["must use https"]}
}
}
Deliveries
POST <your url>
content-type: application/json
user-agent: DataGrout-Webhook/1.0
x-datagrout-event: run.completed
x-datagrout-server: <server_uuid>
x-datagrout-webhook-secret: <the webhook's secret>
{
"event": "run.completed",
"server_id": "<server_uuid>",
"timestamp": "2026-10-16T12:00:01.123456Z",
"data": {
"run_id": 4821,
"execution_id": "exec_…",
"status": "success",
"tool_name": "data-grout@1/discovery.plan@1",
"source": "api",
"duration_ms": 1830
}
}
Check x-datagrout-webhook-secret against the secret you stored, and reject the request if it does not match.
Reply with any 2xx to acknowledge. Each event is sent once: there are no retries, a response must arrive within 10 seconds, and redirects are not followed.
Events
| Event | When | data |
|---|---|---|
run.completed |
A run reached a terminal status — success, failed, timeout or cancelled |
run_id, execution_id, status, tool_name, source, duration_ms |
task.completed |
A background task finished | task_id, tool_name, cache_ref, status |
task.failed |
A background task failed | task_id, tool_name, error, status |
tool_call.failed |
A tool call errored inside a run | run_id, execution_id, tool_name, error |
run_id is the integer runs.get accepts; task_id and cache_ref are the strings tasks.* and cache_ref arguments take.
Related
- REST API — the API these endpoints are part of
- Authentication — tokens, mTLS and OAuth for the server
- JSON-RPC Transport — calling the server’s tools over plain HTTP