Webhooks
Configure a webhook URL to receive HTTP POST notifications as each node executes and when the workflow completes.
How webhooks work
When you provide a webhookUrl with your workflow, Graph Compose sends an HTTP POST request to that URL at two points:
- After each node executes (or fails, or is skipped). The payload includes the node ID, execution state, and result or error.
- When the workflow completes. The payload includes the final workflow result or error.
Webhook delivery is fire-and-forget. If your endpoint is unreachable or returns an error, the failure is logged but does not affect workflow execution. Nodes continue to run regardless of whether webhook delivery succeeds.
Configure a webhook
You can set the webhook URL on the GraphCompose instance with .withWebhookUrl(), or pass it as an option to .execute(). Both approaches are equivalent. If you provide a URL in both places, the .execute() option takes precedence.
import { GraphCompose } from '@graph-compose/client'
const graph = new GraphCompose({
token: process.env.GRAPH_COMPOSE_TOKEN
})
graph
.node('fetch_data')
.get('https://api.example.com/data')
.end()
.withWebhookUrl('https://api.your-app.com/webhooks')
const result = await graph.execute()
Payload structure
Every webhook POST request sends a JSON body with this top-level structure:
Top-level payload
{
"workflowId": "wf_abc123",
"runId": "run_def456",
"type": "node",
"data": { }
}
| Field | Type | Description |
|---|---|---|
workflowId | string | The unique ID of the workflow definition |
runId | string | The unique ID for this specific execution run |
type | "node" or "completion" | Whether this payload is for a single node event or the final workflow completion |
data | object | The event-specific payload. Shape depends on the type field. |
Node events
A node event (type: "node") is sent after each node finishes executing, fails, or is skipped. The data field contains:
| Field | Type | Description |
|---|---|---|
timestamp | number | Unix timestamp in milliseconds when the event occurred |
nodeId | string | The ID of the node |
nodeType | string | The type of the node (e.g., "http", "error_boundary") |
executionState | string | The node's execution state. See execution states. |
result | unknown (optional) | The node's result data, present when the node executed successfully |
error | object (optional) | Error details, present when the node failed |
The error object, when present, has this shape:
| Field | Type | Description |
|---|---|---|
message | string | The error message |
type | string (optional) | The error category, if available |
details | unknown (optional) | Additional error-specific information |
{
"workflowId": "wf_abc123",
"runId": "run_def456",
"type": "node",
"data": {
"timestamp": 1709347200000,
"nodeId": "get_user",
"nodeType": "http",
"executionState": "executed",
"result": {
"data": { "id": "u_1", "name": "Alice" },
"statusCode": 200,
"headers": {}
}
}
}
Completion events
A completion event (type: "completion") is sent once when the entire workflow finishes, whether it succeeded or failed. The data field contains:
| Field | Type | Description |
|---|---|---|
timestamp | number | Unix timestamp in milliseconds when the workflow completed |
result | unknown (optional) | The final workflow result, present on success |
error | unknown (optional) | Error details, present if the workflow failed |
Completion event
{
"workflowId": "wf_abc123",
"runId": "run_def456",
"type": "completion",
"data": {
"timestamp": 1709347205000,
"result": {
"get_user": {
"data": { "id": "u_1", "name": "Alice" },
"statusCode": 200,
"headers": {}
},
"send_email": {
"data": { "sent": true },
"statusCode": 200,
"headers": {}
}
}
}
}
Execution states
The executionState field on node events indicates what happened to the node:
| State | Meaning |
|---|---|
pending | The node has not started executing yet |
executed | The node completed successfully |
executed_and_failed | The node executed but returned a failure (HTTP error, network error, or validation failure) |
terminated | The node was terminated (e.g., by a terminateWhen condition) |
skipped | The node was skipped because an upstream dependency failed |
Operational details
Webhooks are delivered with these characteristics:
- HTTP method: POST with
Content-Type: application/json - No retries: Each webhook is sent once. If the request fails, the failure is logged and no retry is attempted.
- Non-blocking: Webhook delivery failures do not affect workflow execution. Nodes continue to run regardless.
- Delivery order: Node events are sent as each node completes. The completion event is sent last, after all nodes have finished.
Your webhook endpoint should return a 2xx status quickly. Graph Compose does not wait for or inspect the response body. Long-running processing should happen asynchronously on your side.
Best practices
- Return a 2xx response from your endpoint as fast as possible. Offload processing to a background job or queue.
- Store incoming payloads before processing them. This lets you replay events if your processing logic fails.
- Use the
workflowIdandrunIdfields to correlate node events with the final completion event. - Use
executionStateto filter events. For example, if you only care about failures, check for"executed_and_failed"and ignore"executed"or"skipped". - Ensure your endpoint is idempotent. Although Graph Compose does not retry webhooks, network conditions can occasionally cause duplicate delivery.