Docs/Guides/Receiving Webhook Events

Receiving webhook events

This guide walks through setting up a webhook endpoint, subscribing to events, verifying signatures, and handling delivery failures. Webhooks let you build real-time integrations without polling.

Overview

When data changes in Ptolemy, the API emits an event (e.g. instrument.created). If you have a webhook registered with a subscription to that event type, Ptolemy will deliver an HTTP POST to your endpoint within seconds.

1. Register endpoint
Create a webhook with POST /webhooks and store the signing secret.
2. Subscribe to events
Add subscriptions for the event types you care about.
3. Handle deliveries
Verify the signature, process the payload, return 2xx.

Step 1 — Register your webhook

Create a webhook endpoint pointing to your server. Store the secret immediately — it’s shown once:

webhook = client.post("/webhooks", json={ "url": "https://your-app.example.com/webhooks/ptolemy", "label": "production", "active": True }).json() webhook_id = webhook["id"] secret = webhook["secret"] # Store in env variable NOW

Step 2 — Subscribe to events

Add subscriptions for the event types you want to receive. Use * to receive all event types:

# Subscribe to specific events for event_type in ["instrument.created", "instrument.updated", "instrument.archived"]: client.post(f"/webhooks/{webhook_id}/subscriptions", json={ "event_type": event_type }{) # Or subscribe to all events at once client.put(f"/webhooks/{webhook_id}/subscriptions", json=["*"])

Step 3 — Handle incoming deliveries

Your endpoint must verify the signature and respond within 10 seconds. Always return a 2xx status even if you process the event asynchronously.

from fastapi import FastAPI, Request, HTTPException import hashlib, hmac, time app = FastAPI() WEBHOOK_SECRET = os.environ["PTOLEMY_WEBHOOK_SECRET"] @app.post("/webhooks/ptolemy") async def handle_webhook(request: Request): payload = await request.body() sig_header = request.headers.get("Ptolemy-Signature", "") if not verify_signature(payload, sig_header, WEBHOOK_SECRET): raise HTTPException(status_code=401, detail="Invalid signature") event = json.loads(payload) # Queue for async processing, respond immediately await_queue.put(event) return { "ok": True }
Process webhook events asynchronously. Acknowledge receipt immediately by returning 2xx, then process in a background job. If your handler takes more than 10 seconds, Ptolemy will treat it as a timeout and retry.

Debugging failed deliveries

If your endpoint isn’t receiving events as expected, check the delivery log:

List recent failed deliveries
POST /webhook-deliveries/filter { "filter": { "and": [ { "field": "webhook_id", "op": "eq", "value": "your-webhook-uuid" }, { "field": "status", "op": "eq", "value": "failed" } ] }, "sort": [{ "field": "delivered_at", "direction": "desc" }] }

Once you’ve fixed your endpoint, manually redeliver a failed event:

Redeliver a failed event
POST /webhook-deliveries/{deliveryId}/redeliver # Returns 202 Accepted
PrivacyTermsStatus© 2025 Ptolemy Pty Ltd