Orbitali Docs

Webhooks

Receive realtime events from Orbitali during calls.

Orbitali sends webhook events to an agent's Server URL while a call is active.

Each request includes the event name in the x-orbitali-event-type header and in message.type.

If the agent has a Server Secret, Orbitali also includes x-orbitali-signature. The value is sha256= followed by the lowercase hex HMAC-SHA256 digest of the raw JSON request body, using the Server Secret as the key.

Verify signatures in TypeScript

Set the same secret as the agent's serverSecret and in your webhook app, for example ORBITALI_WEBHOOK_SECRET. Verify the signature against the raw request body before parsing JSON.

import { createHmac, timingSafeEqual } from "node:crypto";
import { NextResponse } from "next/server";

export const runtime = "nodejs";

function verifyOrbitaliSignature({
  secret,
  rawBody,
  signature,
}: {
  secret: string;
  rawBody: string;
  signature: string | null;
}) {
  if (!signature?.startsWith("sha256=")) {
    return false;
  }

  const actual = signature.slice("sha256=".length);
  if (!/^[a-f0-9]{64}$/i.test(actual)) {
    return false;
  }

  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");

  const actualBuffer = Buffer.from(actual, "hex");
  const expectedBuffer = Buffer.from(expected, "hex");

  return (
    actualBuffer.length === expectedBuffer.length &&
    timingSafeEqual(actualBuffer, expectedBuffer)
  );
}

export async function POST(request: Request) {
  const secret = process.env.ORBITALI_WEBHOOK_SECRET?.trim();
  const rawBody = await request.text();

  if (
    !secret ||
    !verifyOrbitaliSignature({
      secret,
      rawBody,
      signature: request.headers.get("x-orbitali-signature"),
    })
  ) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  let event: unknown;
  try {
    event = JSON.parse(rawBody);
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }

  if (
    typeof event === "object" &&
    event !== null &&
    "message" in event &&
    event.message &&
    typeof event.message === "object" &&
    "type" in event.message &&
    event.message.type === "agent:tool-call"
  ) {
    return NextResponse.json({ ok: true });
  }

  return NextResponse.json({ error: "Unsupported event" }, { status: 400 });
}

Events

EventWhen it is sent
agent:assistant-requestBefore the call starts when an agent uses a dynamic prompt
agent:tool-callWhen the model requests one of the agent's tools

Assistant request

Use agent:assistant-request to build the prompt and greeting from your own data.

{
  "message": {
    "type": "agent:assistant-request",
    "call": {
      "id": "call_123",
      "agentId": "agent_123",
      "accountId": "account_123",
      "phoneNumberId": "phone_123",
      "channel": "voice",
      "direction": "inbound",
      "fromNumber": "+15551234567",
      "toNumber": "+15557654321",
      "startedAt": "2026-05-14T12:00:00.000Z"
    }
  }
}

Return the resolved prompt and optional greeting.

{
  "prompt": "You are the receptionist for Acme Clinic. Help callers book appointments.",
  "greeting": "Thanks for calling Acme Clinic. How can I help?"
}

Tool call

Use agent:tool-call to execute a model-requested action.

{
  "message": {
    "type": "agent:tool-call",
    "call": {
      "id": "call_123",
      "agentId": "agent_123",
      "accountId": "account_123"
    },
    "toolCall": {
      "id": "tool_123",
      "name": "check_availability",
      "arguments": {
        "date": "2026-05-14",
        "time": "15:00"
      }
    }
  }
}

Return a JSON object the model can use.

{
  "available": true,
  "slots": ["15:00", "15:30"]
}

Operational expectations

  • Serve the endpoint over HTTPS.
  • Verify x-orbitali-signature against the raw request body when using a Server Secret.
  • Keep responses fast; callers are waiting in realtime.
  • Return structured JSON for success and errors.
  • Avoid exposing secrets in tool responses because the model may speak relevant details back to the caller.

Privacy-safe webhook handling

  • Treat call metadata, tool arguments, tool responses, and dynamic prompt inputs as customer-controlled personal data.
  • Avoid logging raw request bodies in production. Log event type, call ID, tool name, status, latency, and a request correlation ID instead.
  • Store only the fields your application needs to complete the workflow, and set a retention period for any copied webhook payloads.
  • Redact or hash caller phone numbers before writing application logs unless the raw number is required for routing, fraud prevention, billing, or support.
  • Do not return access tokens, credentials, internal IDs, medical details, payment details, or other secrets in tool responses.
  • Keep serverSecret and ORBITALI_WEBHOOK_SECRET outside source control and rotate them after suspected exposure.
  • If callers opt out or do not consent to recording/transcription, make your webhook path avoid storing prompt context or tool payloads tied to that call.

On this page