Webhooks
Webhooks can be configured in the Gabber dashboard.
For a list of webhook payloads, see Webhook.
Example Use Cases
- An app that has a credit sytem could use the
usage.tracked
webhook to deduct credits on the platform. - Realtime sessions that use tools calls could use the
tool.calls_finished
webhook to respond into the session with tool call results. - The
realtime_session.state_changed
webhook can be used to make relevant updates when a phone number is called
Local Development
Since your computer is likely not exposed on a static public IP address to the internet, Gabber can't send webhook requests to it. This presents a challenge for developing webhook handlers.
The easiest way to get around this is to use a tunneling service. Ngrok is a very popular, free choice.
Running:
ngrok http 3000
gives you a public endpoint that proxies all traffic to your localhost:3000. Learn more at ngrok.com.
Deliverability
Webhook requests are retried 3 times to ensure deliverability.
Security
Since your backend's webhook endpoints must be public to recieve webhooks, it's important to verify that Gabber is the sender (and not a malicious actor).
Every webhook request is sent with an X-Webhook-Signature
header which is an HMAC-SHA256 hex string of the request body signed with your configured Gabber API key.
The following shows an example taken from our Rizz.AI Example NextJS App of how to verify webhook signatures:
import { NextRequest } from "next/server";
import crypto from "crypto";
import { CreditsController } from "@/lib/server/controller/credits";
import { UserController } from "@/lib/server/controller/user";
export async function POST(req: NextRequest) {
const textBody = await req.text();
const webhookSignature = req.headers.get("X-Webhook-Signature");
const key = process.env.GABBER_API_KEY;
if (!webhookSignature) {
return new Response("No signature provided", { status: 400 });
}
if (!key) {
return new Response("Server misconfigured - missing API key");
}
const computedSignature = crypto
.createHmac("sha256", key)
.update(textBody, "utf8")
.digest("hex");
if (computedSignature !== webhookSignature) {
// This could be from the wrong api key configured here or in the webhook gabber dashboard
// or it could be from a malicious actor trying to send a request
console.error("Signature mismatch");
return new Response("Invalid signature", { status: 403 });
}
const parsedBody = JSON.parse(textBody);
const { type, payload } = parsedBody;
if (type === "usage.tracked") {
const { human_id, type, value } = payload;
if (type === "conversational_seconds") {
// Use 1 credit per second
const creditCost = value;
const resp = await CreditsController.reportCreditUsage(
human_id,
creditCost * -1,
);
const entry = resp.data;
await UserController.updateLimits(human_id, entry.balance);
}
}
return new Response(null, { status: 200 });
}