Graphon Docs

Receive events with webhooks

Get a signed POST when an object version becomes searchable or fails to index: register a webhook, verify each event, handle retries, rotate the secret, and replay missed events.

View as Markdown

Overview

A webhook is an HTTPS URL that Graphon calls when something happens in your workspace. Use one instead of polling. For example, learn when an object version becomes searchable.

  • Graphon sends each event as a JSON POST.
  • Graphon signs each POST with the Standard Webhooks scheme.
  • Graphon retries a failed POST for about three days.
  • Graphon keeps a delivery log for 30 days. You can replay events from it.

The Webhook events reference lists each event type and its fields. Manage webhooks with the API, the graphon webhooks CLI commands, or the MCP tools in the Webhooks toolset.

Personal and workspace webhooks

Webhooks have owners, like API keys.

Personal webhookWorkspace webhook
Who can create itAny member, at any roleA workspace API key, at any role, or an Admin
Who can see, edit, and delete itThe member, and any AdminEvery workspace API key, and any Admin
ownerme, the default for a memberworkspace, the default for a workspace API key
When the member leaves or is removedGraphon deletes itIt keeps working

A webhook does not depend on the API key that created it. It keeps working after that key is revoked.

Choose events and buckets

  • eventTypes lists the event types to send. Leave it out, or send null, to get every type, including types that Graphon adds later.
  • bucketIds lists the buckets whose events to send. Leave it out, or send null, to get every bucket, including buckets that you create later.

A new webhook gets only the events that happen after you create it.

Create a webhook

Graphon returns the signing secret once, in secret. Store it in a secret manager.

cURL

curl --request POST \
  --url "https://api.beta.graphon.ai/v1/workspaces/$GRAPHON_WORKSPACE_ID/webhooks" \
  --header "Authorization: Bearer $GRAPHON_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "indexing-callbacks",
    "url": "https://hooks.example.com/graphon",
    "eventTypes": ["object.indexed", "object.indexing_failed"]
  }'

Response

{
  "webhook": {
    "id": "whkN4vB7qX2mZ",
    "name": "indexing-callbacks",
    "url": "https://hooks.example.com/graphon",
    "eventTypes": ["object.indexed", "object.indexing_failed"],
    "bucketIds": null,
    "enabled": true,
    "ownerKind": "user"
  },
  "secret": "whsec_<shown once>"
}

CLI

# Writes the secret to .env as GRAPHON_WEBHOOK_SECRET instead of printing it.
graphon webhooks create --name indexing-callbacks \
  --url https://hooks.example.com/graphon \
  --event-type object.indexed --event-type object.indexing_failed \
  --write-env .env

# Send one signed webhook.test event and show the answer.
graphon webhooks test <webhookId>

The URL must follow these rules:

  • It uses HTTPS.
  • It holds no user name, password, or fragment. A query string and any port are fine.
  • Its host resolves only to public addresses. Graphon checks this when you create or update the webhook, and again on each connection.
  • It answers directly. Graphon does not follow redirects or use a proxy.

To check your receiver, send a test event with graphon webhooks test or the Test a webhook operation. The test event (webhook.test) goes once, with no retries, and the delivery log does not keep it. A test works while the webhook is off.

Verify each event

Each POST carries three signature headers:

HeaderValue
webhook-idThe event ID. A retry and a replay send the same value.
webhook-timestampWhen Graphon sent the POST, in Unix seconds.
webhook-signaturev1, and a base64 HMAC-SHA256 signature. During a secret rotation, two signatures, separated by a space.

Check them with a Standard Webhooks library. The library refuses a timestamp more than 5 minutes away from your clock, so a copied POST cannot be sent again later. Answer 400 to a POST that fails the check.

Install

npm install standardwebhooks   # Node.js
pip install standardwebhooks   # Python

Node.js

import express from 'express';
import { Webhook } from 'standardwebhooks';

const webhook = new Webhook(process.env.GRAPHON_WEBHOOK_SECRET);
const app = express();

// Keep the raw body. The signature covers the exact bytes that Graphon sent.
app.post('/graphon', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = webhook.verify(req.body, req.headers);
  } catch {
    res.status(400).end();
    return;
  }
  enqueue(event); // Your queue or background job.
  res.status(204).end();
});

app.listen(3000);

Python

import os

from flask import Flask, request
from standardwebhooks import Webhook, WebhookVerificationError

webhook = Webhook(os.environ["GRAPHON_WEBHOOK_SECRET"])
app = Flask(__name__)


@app.post("/graphon")
def graphon_event():
    # Keep the raw body. The signature covers the exact bytes that Graphon sent.
    try:
        event = webhook.verify(request.get_data(), dict(request.headers))
    except WebhookVerificationError:
        return "", 400
    enqueue(event)  # Your queue or background job.
    return "", 204

Verify the raw body

Pass the body bytes exactly as they arrived. If your framework parses the JSON first and you serialize it again, the signature no longer matches.

Drop duplicates

Graphon delivers each event at least once. A retry, a replay, or a lost answer can send the same event again.

  • Store the webhook-id of each event that you handle. It equals the event id. Skip an event whose ID you already have, and answer 2xx.
  • Events can arrive in any order. Compare the event timestamp and the object versionId, or read the current state from the API, when order matters.
  • Ignore an event type or a field that you do not know, and answer 2xx.

Answer fast

Graphon waits 15 seconds for an answer. No answer counts as a failure, and Graphon retries. Answer 2xx first, then do the work in a queue or a background job.

Retries

Your answerWhat Graphon does
2xxThe delivery succeeded.
429, 5xx, no answer in 15 seconds, a network error, or a TLS errorRetries later.
3xxThe delivery failed. Graphon does not follow redirects.
410The delivery failed, and Graphon turns the webhook off.
Any other 4xxThe delivery failed.
  • Graphon makes up to 10 attempts over about 75 hours. The retry schedule lists each wait.
  • To slow Graphon down, answer 429 with a Retry-After header. When it asks for a longer wait than the schedule, Graphon waits that long, up to 24 hours.
  • 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.

To turn a webhook off yourself, update it with enabled: false. Its pending deliveries end as failed. To turn it on again, update it with enabled: true, then replay what it missed.

An off webhook records nothing

Graphon does not record events for a webhook while it is off. A replay cannot send them later. A replay can send only events that the webhook got before it turned off.

Rotate the secret

Rotating makes a new signing secret and shows it once. The previous secret keeps signing during an overlap, so you can deploy the new secret with no failed POSTs.

  • The overlap is 24 hours by default. Set previousSecretExpiresInSeconds from 0 to 604800 (7 days). 0 stops the previous secret at once.
  • During the overlap, webhook-signature holds both signatures. The library accepts a POST that matches either secret.
  • A second rotation during an overlap stops the oldest secret at once.
  • The webhook shows signing.secretFingerprint, not the secret, so you can tell which secret is current.

CLI

# The previous secret keeps signing for 24 hours unless you pass --previous-expires-in.
graphon webhooks rotate-secret <webhookId> --write-env .env

Read the log and replay

The delivery log lists each event that the webhook got, newest first, with its status (pending, succeeded, or failed) and its last attempt. One delivery shows the exact body and every attempt.

CLI

graphon webhooks deliveries <webhookId> --status failed
graphon webhooks delivery <webhookId> <eventId>

Replay sends past events again, with the same event ID and body, and a new timestamp and signature. Choose events by ID, or by time with since and until. onlyFailed skips deliveries that succeeded.

cURL

curl --request POST \
  --url "https://api.beta.graphon.ai/v1/workspaces/$GRAPHON_WORKSPACE_ID/webhooks/<webhookId>/replay" \
  --header "Authorization: Bearer $GRAPHON_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"since": "2026-10-13T00:00:00Z", "onlyFailed": true}'

CLI

graphon webhooks replay <webhookId> --since 2026-10-13T00:00:00Z --only-failed
graphon webhooks replay <webhookId> --event <eventId> --event <eventId>
  • A replay starts a new round of up to 10 attempts.
  • A delivery that is still pending stays queued once.
  • A replay on a webhook that is off returns 409 webhook_disabled. Turn the webhook on first.
  • Delete removes the webhook and its log. To stop deliveries and keep the log, turn the webhook off.

See also

The Webhook events reference covers each event body, the headers, and the retry schedule. The Create a webhook reference covers every webhook operation.

Was this page helpful?