OrbitPeople.aidocs

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 to it once, when the search finishes — including a search that found nobody — or reaches the target count in your brief. A brief that ends in awaiting_input sends no event; submit a sharper brief.

Registering

curl -X POST https://api.orbitpeople.ai/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 a public http(s) URL — use https. A private or localhost address is accepted but never called, so use a public tunnel when testing locally. It's per request: each search registers its own callback.

Payload

{
  "event": "project.completed",
  "projectId": "abc123",
  "status": {
    "projectId": "abc123",
    "status": "done",
    "done": true,
    "totalResults": 42,
    "verifiedResults": 35,
    "requestedTarget": null,
    "starredResults": 0,
    "clarificationQuestion": null,
    "error": null
  },
  "resultsUrl": "https://api.orbitpeople.ai/v1/projects/abc123/results"
}

status is the same object GET /v1/projects/{projectId} returns. resultsUrl is ready to fetch immediately, with your API key.

Signature verification

Every delivery carries X-Orbit-Signature: sha256=<hex> — an HMAC-SHA256 of the raw request body, keyed with your workspace's webhook secret. Always verify before trusting the payload, and compute it over the bytes you received, not over re-serialized JSON.

The webhook secret (whsec_…) is in Settings → Workspace → API (/settings/workspace/api), under the key list — click to reveal it.

Node.js:

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyOrbitSignature(rawBody: string, header: string, secret: string) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const provided = header.replace(/^sha256=/, "").toLowerCase();
  return (
    provided.length === expected.length &&
    timingSafeEqual(Buffer.from(provided), Buffer.from(expected))
  );
}

// rawBody: the request body exactly as received, before any JSON parsing.
// header:  the value of the X-Orbit-Signature request header.
const ok = verifyOrbitSignature(
  rawBody,
  header,
  process.env.ORBIT_WEBHOOK_SECRET!,
);
// Reject the delivery (e.g. respond 401) when ok is false.

Python:

import hashlib, hmac

def verify_orbit_signature(raw_body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    provided = header.removeprefix("sha256=").lower()
    return hmac.compare_digest(provided, expected)

Delivery and retries

Respond 2xx as soon as you've received the event, and do the work afterwards. A delivery that fails (non-2xx or unreachable) is tried three times in total — immediately, after 30 seconds, then after 5 minutes. Make your handler idempotent: the same projectId may arrive more than once. If nothing arrives, poll GET /v1/projects/{projectId} instead.

On this page