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.