Webhook events
Every webhook event type and its data fields, the signature headers, and the retry schedule.
Graphon sends each event as a JSON POST to the webhook URL. To create a webhook and verify its events, read Receive events with webhooks.
Event body
Every event has the same top-level fields.
| Field | Type | Description |
|---|---|---|
id | string | The event ID, which starts with evt. It equals the webhook-id header. |
type | string | The event type. |
timestamp | string | When the change happened, in UTC (ISO 8601). Not when Graphon sent the POST. |
workspaceId | string | The workspace ID. |
data | object | The fields of the event type. |
- An event holds IDs, a status, and error codes. It never holds object content or metadata values. Read the object with the Storage API when you need it.
- Graphon can add event types and fields. Ignore a type or a field that you do not know, and answer
2xx. A webhook witheventTypes: nullalso gets types that Graphon adds later.
Event types
| Type | When Graphon sends it |
|---|---|
object.indexed | An object version became searchable. Its indexing status moved to ready. |
object.indexing_failed | An object version could not be indexed. Its indexing status moved to failed or unsupported. |
webhook.test | You called Test a webhook. Only that call sends it. A webhook cannot filter for it, and the delivery log does not keep it. |
object.indexed
An object version became searchable. Its indexing status moved to ready.
| Field | Type | Description |
|---|---|---|
data.object.bucketId | string | The bucket ID. |
data.object.key | string | The object key. |
data.object.versionId | string | The object version. A newer version of the same key has its own events. |
data.object.status | string | ready |
Example
{
"id": "evtW2nC6vJ0hS",
"type": "object.indexed",
"timestamp": "2026-10-14T09:05:12.481Z",
"workspaceId": "wspq7ws2np5l8z4",
"data": {
"object": {
"bucketId": "bktA1b2C3d4E5",
"key": "incidents/timeout.json",
"versionId": "ver_3f9c2a7d1e4b4c8a9f0d6b2e5a1c7d3f",
"status": "ready"
}
}
}object.indexing_failed
An object version could not be indexed. Its indexing status moved to failed or unsupported.
| Field | Type | Description |
|---|---|---|
data.object.bucketId | string | The bucket ID. |
data.object.key | string | The object key. |
data.object.versionId | string | The object version. A newer version of the same key has its own events. |
data.object.status | string | failed: indexing stopped with an error. unsupported: Graphon refused the object, for example for its size or its metadata. |
data.object.error.code | string | For failed, the error code from the index, such as ingest_failed or index_timeout. For unsupported, the refusal code, such as invalid_metadata. |
data.object.error.message | string | Text for a person to read. Do not parse it. |
Example
{
"id": "evtK5mP9qL3dF",
"type": "object.indexing_failed",
"timestamp": "2026-10-14T09:07:40.019Z",
"workspaceId": "wspq7ws2np5l8z4",
"data": {
"object": {
"bucketId": "bktA1b2C3d4E5",
"key": "incidents/scan.pdf",
"versionId": "ver_8b1e4d2a9c7f4e3b8a6d5c1f0e9b2a74",
"status": "failed",
"error": {
"code": "ingest_failed",
"message": "The file is not a readable PDF."
}
}
}
}webhook.test
You called Test a webhook. Only that call sends it. A webhook cannot filter for it, and the delivery log does not keep it.
| Field | Type | Description |
|---|---|---|
data.webhook.id | string | The webhook ID. |
Example
{
"id": "evtH7jN2cR6tY",
"type": "webhook.test",
"timestamp": "2026-10-14T09:01:00.112Z",
"workspaceId": "wspq7ws2np5l8z4",
"data": {
"webhook": {
"id": "whkN4vB7qX2mZ"
}
}
}Headers
| Header | Value |
|---|---|
webhook-id | The event ID. Every retry and replay of the event sends the same value. |
webhook-timestamp | When Graphon sent this POST, in Unix seconds. Each attempt has a new value. |
webhook-signature | v1, and the base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body}. The key is the base64-decoded part of the secret after whsec_. During a secret rotation, the header holds two signatures, separated by a space, the new one first. |
content-type | application/json |
user-agent | Graphon-Webhooks/1 |
This is the Standard Webhooks scheme. Check each POST with the standardwebhooks library for Node.js or Python. Refuse a POST whose timestamp is more than 5 minutes away from your clock.
Retries
Graphon delivers each event at least once, in any order. It makes up to 10 attempts over about 75 hours.
| Attempt | Wait after the previous attempt | About this long after the first |
|---|---|---|
| 1 | Sent at once | 0 |
| 2 | 5 seconds | 5 seconds |
| 3 | 5 minutes | 5 minutes |
| 4 | 30 minutes | 35 minutes |
| 5 | 2 hours | 2 hours 35 minutes |
| 6 | 5 hours | 7 hours 35 minutes |
| 7 | 10 hours | 17 hours 35 minutes |
| 8 | 14 hours | 31 hours 35 minutes |
| 9 | 20 hours | 51 hours 35 minutes |
| 10 | 24 hours | 75 hours 35 minutes |
- Each wait varies by up to 10% either way.
- A
Retry-Afterheader on a429or5xxanswer replaces a shorter wait, up to 24 hours. - A replay starts a new round of up to 10 attempts.
| Answer | Result |
|---|---|
2xx | Succeeded. |
429, 5xx, no answer in 15 seconds, a network error, or a TLS error | Retried. After the last attempt, failed. |
3xx | Failed. Graphon does not follow redirects. |
410 | Failed, and Graphon turns the webhook off. |
| Any other 4xx | Failed. |
Graphon also turns a webhook off when a delivery fails, the webhook is older than 72 hours, and it got no 2xx answer in the last 72 hours. A webhook that Graphon turned off shows disabledReason failing. The delivery log keeps each event for 30 days.