Webhooks
Register a callbackUrl and receive a signed project.completed event instead of polling.
Pass callbackUrl to POST /v1/search and Orbit POSTs a signed project.completed event when the project finishes. Your endpoint must respond 2xx within 10 seconds.
Registering
curl -X POST .../v1/search \
-H "Authorization: Bearer $ORBIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brief": "...",
"callbackUrl": "https://yourapp.com/webhooks/orbit"
}'The callbackUrl must be https://. It's per-request — each search call registers its own callback.
Payload
{
"event": "project.completed",
"projectId": "abc123",
"status": {
"status": "done",
"done": true,
"resultCount": 42,
"relevantCount": 35,
"irrelevantCount": 5,
"unknownCount": 2,
"requestedTarget": null,
"clarificationQuestion": null,
"error": null
},
"resultsUrl": "https://<deployment>.convex.site/v1/projects/abc123/results"
}resultsUrl is ready to fetch immediately.
Signature verification
Every delivery includes X-Orbit-Signature: sha256=<hmac-sha256-hex>. Always verify before trusting.
With the SDK:
import { verifyWebhookSignature } from "@orbit/sdk";
const valid = await verifyWebhookSignature({
payload: rawBody, // unparsed string
signature: req.headers["x-orbit-signature"],
secret: process.env.ORBIT_WEBHOOK_SECRET!,
});
if (!valid) return new Response("bad signature", { status: 401 });The ORBIT_WEBHOOK_SECRET is per-workspace. Find it in Settings in the Orbit UI.
Retry behavior
Failed deliveries (non-2xx, timeout, connection error) retry 3 times with exponential backoff. Design your handler to be idempotent — the same projectId may arrive more than once.