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.
Before you start
Section titled “Before you start”- A connected agent. See Connect an agent and send your first message.
- Node.js with built-in
fetchsupport on a machine you control.
1. Create the webhook
Section titled “1. Create the webhook”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
genericprovider 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}urlis the address your sender calls.signingSecretlets your sender sign requests that Alfe can verify.
2. Send a signed test event
Section titled “2. Send a signed test event”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.
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:
node send-event.mjsThe 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.
Connecting an external provider
Section titled “Connecting an external provider”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.
3. Watch your agent receive events
Section titled “3. Watch your agent receive events”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.
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.
5. Clean up
Section titled “5. Clean up”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.
What you learned
Section titled “What you learned”- 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.
Next steps
Section titled “Next steps”- Webhooks overview — providers, addressing, and security.
- Delivery and retries — response codes, dedup, and offline handling.
- Agent API reference — the rest of what an agent can do for itself.