Skip to content

Receive a webhook

Webhooks let your agent react to events from external systems. In this tutorial you’ll create a webhook and send it a signed event from a script you control.

For the concepts, see the Webhooks overview.

A webhook belongs to exactly one agent and gives it a private inbound URL. Create one either way — both act on the same webhook with the same rules:

  • From the dashboard: open your agent, go to its Webhooks section, and create a webhook. Use the default generic provider for this example.
  • From the agent itself: ask the agent to create a webhook. Agents have built-in webhook tools and can create and manage their own webhooks.

Creating a webhook returns the URL and secret you need:

{
"webhookId": "whk_01J9Z8Q…",
"url": "https://webhooks.alfe.ai/v1/hooks/whk_01J9Z8Q…",
"signingSecret": "",
"provider": "generic",
"active": true
}
  • url is the address your sender calls.
  • signingSecret lets your sender sign requests that Alfe can verify.

Save this as send-event.mjs. Supply ALFE_WEBHOOK_URL and ALFE_WEBHOOK_SIGNING_SECRET through your local secret manager or environment; use the values from step 1. Do not put the secret in source control or logs.

send-event.mjs
import { createHmac, randomUUID } from "node:crypto";
const url = process.env.ALFE_WEBHOOK_URL;
const secret = process.env.ALFE_WEBHOOK_SIGNING_SECRET;
if (!url || !secret) throw new Error("Set the webhook URL and signing secret");
if (new URL(url).protocol !== "https:") throw new Error("Use the HTTPS webhook URL");
const body = JSON.stringify({ type: "example.created", message: "Hello, agent!" });
const signature = "sha256=" + createHmac("sha256", secret).update(body).digest("hex");
const response = await fetch(url, {
method: "POST",
redirect: "error",
signal: AbortSignal.timeout(10_000),
headers: {
"Content-Type": "application/json",
"X-Alfe-Signature-256": signature,
"Idempotency-Key": randomUUID(),
},
body,
});
if (!response.ok) throw new Error(`Webhook rejected: ${response.status}`);
console.log(await response.json());

Run it after setting the environment variables:

Terminal window
node send-event.mjs

The example creates a new event identifier each time you run it. When retrying one logical event, keep its original identifier and body so it can be deduplicated.

Alfe requires X-Alfe-Signature-256 for every provider hint. A provider’s own signature header does not satisfy this contract automatically. If your provider cannot generate this header, use a receiver you control: verify its native signature first, then sign and forward the accepted raw body to Alfe using the webhook secret. Preserve a stable event identifier across retries. Selecting github, stripe, or slack only adds provider context; it does not enable a native authentication adapter.

Alfe verifies and retains the event before handing it to your connected agent. If the agent is offline, delivery waits for its connection to return. An accepted HTTP response does not mean the agent has finished processing the event. See Delivery and retries for response codes and retry behavior.

4. (Optional) Verify the Alfe signature format locally

Section titled “4. (Optional) Verify the Alfe signature format locally”

This helper verifies the format used by the sender above. It is useful for your own receiver when that receiver explicitly uses the Alfe signature contract. For a provider’s native webhook, use that provider’s verification contract at your receiver before forwarding to Alfe.

verify-signature.ts
import crypto from "node:crypto";
function verifyAlfeSignature(rawBody: Buffer, secret: string, signature: string): boolean {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
if (!/^sha256=[0-9a-f]{64}$/.test(signature)) return false;
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

The full contract, an Express example, and a Python version are in Verifying signatures.

When you’re done, delete the webhook (from the dashboard or via the agent’s tools) — this deactivates its URL, after which inbound requests are rejected with 404. Stop the sending script or remove the URL from your external sender too.

  • Each agent gets its own signed, inbound webhook endpoint.
  • You can manage webhooks from the dashboard or let the agent manage its own — both enforce the same per-agent ownership.
  • Deliveries are HMAC-signed and can be verified anywhere the standard HMAC-SHA256 algorithm is available.