Guide to AgentLine Webhooks: Features and Best Practices
AgentLine webhooks tell your application what is happening on your AI agents' phone lines: an inbound call is answered, the caller says something, a call completes or fails, or a text message arrives. AgentLine gives each AI agent a real US or Canadian phone number, and webhooks are one of the ways those call and SMS events reach your code.
This guide covers how AgentLine webhooks are configured and signed, the events an agent's webhook receives, what AgentLine leaves undocumented, and the practices that keep an AgentLine receiver reliable.
What are AgentLine webhooks?
Each AgentLine agent can have one webhook URL. That URL receives all of the agent's events as signed JSON POST requests. There is no way to pick event types: a webhook gets everything, and setting a new URL for the agent replaces the old webhook.
Each delivery carries an X-Webhook-Signature header with the hex HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret. You can rename the header per webhook, but the default is X-Webhook-Signature.
Webhooks are not AgentLine's only channel. For agents running locally without a public URL, AgentLine recommends its Agent Relay, an outbound connection that keeps events until they are acknowledged. There is also an events mailbox you can poll. This guide covers webhooks.
AgentLine webhook features
| Feature | Details |
|---|---|
| Configuration | API: POST https://api.agentline.cloud/v1/webhooks, plus list, delete and test |
| API authentication | Bearer al_live_... API key |
| Scope | One webhook per agent, receiving all of that agent's events |
| Signature header | X-Webhook-Signature by default, renameable with signature_header |
| Scheme | HMAC-SHA256, bare lowercase hex |
| Signed content | The raw request body, nothing else |
| Secret | Set by you or auto-generated, returned in full once |
| Testing | POST /v1/webhooks/test?agent_ sends a signed webhook.test event |
| Retries, timeout, source IPs | Not documented |
Common events
AgentLine lists 10 delivered event types. It describes only the first two, so the rest are read from their names:
| Event | Fires when |
|---|---|
call.received | An inbound call is answered |
call.utterance | The caller finishes a live turn |
call.completed | A call finishes |
call.owner_task | Not described by AgentLine. The name suggests a task raised for the agent's owner |
call.failed | A call fails |
call.busy | The line was busy |
call.no-answer | The call wasn't answered |
call.canceled | A call was cancelled |
sms.received | An inbound SMS arrives |
webhook.test | You trigger a test delivery |
AgentLine doesn't publish a webhook body. Its events mailbox returns events as { event_, which is a good guess at the delivery shape, but it is a guess. Send a webhook.test, log the body, and build against what arrives.
Two documented details are useful once you do. The conversation field on call.utterance labels turns as user and assistant, while the transcript endpoint GET /v1/calls/{id}/transcript uses human and agent for the same speakers. Map one to the other if you combine them.
Setting up AgentLine webhooks
Set the webhook for an agent with your API key:
curl -X POST "https://api.agentline.cloud/v1/webhooks" \
-H "Authorization: Bearer $AGENTLINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "'"$AGENT_ID"'",
"url": "https://example.com/webhooks/agentline"
}'
The response includes the signing secret in full. Save it straight away, for example as AGENTLINE_WEBHOOK_SECRET: later reads mask it. You can also pass your own secret in the request.
Then send a test:
curl -X POST "https://api.agentline.cloud/v1/webhooks/test?agent_id=$AGENT_ID" \
-H "Authorization: Bearer $AGENTLINE_API_KEY"
AgentLine sends webhook.test "through the exact same pipeline as real events", so a verified test means the whole chain works.
If you receive AgentLine webhooks through Hookdeck, create a source with the AgentLine type, paste the signing secret, and use the source URL as the webhook url. Keep the default X-Webhook-Signature header: don't set signature_header, because Hookdeck's AgentLine source only reads the default header.
Securing AgentLine webhooks
Compute an HMAC-SHA256 of the raw request body with the signing secret as the key, hex-encode it, and compare it with X-Webhook-Signature in constant time.
const crypto = require('crypto');
const express = require('express');
function verifyAgentLineSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const received = Buffer.from(signatureHeader.trim().toLowerCase(), 'utf8');
const computed = Buffer.from(expected, 'utf8');
return received.length === computed.length && crypto.timingSafeEqual(received, computed);
}
const app = express();
app.post('/webhooks/agentline', express.raw({ type: '*/*' }), (req, res) => {
const secret = process.env.AGENTLINE_WEBHOOK_SECRET;
if (!secret) return res.sendStatus(500); // fail closed
if (!verifyAgentLineSignature(req.body, req.get('x-webhook-signature'), secret)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString('utf8'));
// Confirm the type field from a real delivery; the mailbox format uses event_type.
res.sendStatus(200);
});
The same check in Python:
import hashlib
import hmac
def verify_agentline_signature(raw_body: bytes, signature_header, secret: str) -> bool:
if not signature_header or not secret:
return False
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(
signature_header.strip().lower().encode("utf-8"), expected.encode("utf-8")
)
# In FastAPI: raw_body = await request.body()
# verify_agentline_signature(raw_body, request.headers.get("x-webhook-signature"),
# os.environ["AGENTLINE_WEBHOOK_SECRET"])
AgentLine's own JavaScript sample calls crypto.timingSafeEqual without checking lengths, which throws when the header is missing or the wrong length. The length check above turns that into a 401. AgentLine's Node package isn't on npm, so there is no SDK verifier to use instead.
If you set a custom signature_header such as X-Hub-Signature-256, AgentLine doesn't say whether the value format changes too, for example to GitHub's sha256= prefix. Log a test delivery before you rely on it.
There is no timestamp in the request, so nothing tells you how old a delivery is.
Make AgentLine webhooks production-ready. Hookdeck Event Gateway verifies the
X-Webhook-Signatureheader, stores every call and SMS event, and delivers it to your service with retries you control.
AgentLine webhook limitations and pain points
The delivery body isn't documented
The Problem: You can't write a router from the docs, because they don't say which field holds the event type.
Why It Happens: AgentLine lists the event names and documents the mailbox format, but shows no webhook body.
Workarounds:
- Send
webhook.testand log the full body before you write handlers. - Read the type defensively, for example
event.event_, and log anything you can't route.type ?? event.type
How Hookdeck Can Help: Hookdeck stores every request with its full headers and body, so the test delivery and the first real events are there to inspect before you write any routing logic.
One webhook gets everything
The Problem: call.utterance traffic during busy calls drowns out the events you care about, and you can't unsubscribe from it.
Why It Happens: Each agent has one webhook and it receives every event type. There is no event filter on the AgentLine side.
Workarounds:
- Acknowledge unwanted types immediately with a
200. - Use the Agent Relay for live turns if your agent can hold an outbound connection, and keep the webhook for the rest.
How Hookdeck Can Help: Hookdeck filters on the event type can drop call.utterance or send it to a separate destination, so each service sees only what it needs.
A custom signature header breaks verification
The Problem: Verification fails after someone changes the webhook to use X-Hub-Signature-256.
Why It Happens: signature_header renames the header, and verifiers that look for X-Webhook-Signature no longer find it. Re-posting the webhook replaces it, and AgentLine auto-generates a secret whenever you omit one, so an update without a secret may change the key.
Workarounds:
- Keep the default header unless a downstream tool needs another name.
- Pass your own
secretwhen you update a webhook, so a URL change doesn't rotate the key.
How Hookdeck Can Help: Hookdeck's AgentLine source checks the default header. Hookdeck answers AgentLine with a 200 either way, then records a request that fails the check as failed verification, so it never reaches your destination, and you can see the headers that actually arrived.
Delivery guarantees are unstated
The Problem: You don't know whether a failed delivery is retried.
Why It Happens: AgentLine documents retention for the Relay, which keeps events until acknowledged, but says nothing about webhook retries, timeouts or success codes.
Workarounds:
- Return
200quickly and process asynchronously. - After an outage, check call records through the API rather than assuming every event arrived.
How Hookdeck Can Help: Hookdeck accepts each delivery as it arrives and stores it, then delivers to your service with retries you configure.
Best practices
Verify against the raw body
The signature covers the exact bytes AgentLine sent. In Express, use express.raw({ type: '*/*' }). In Next.js, use await request.text(). In FastAPI, use await request.body().
Save the secret when you create the webhook
The secret is shown once. Store it in your secret manager as part of the same script that sets the webhook.
Keep live turns fast
call.utterance arrives while a caller is on the line. If your reply logic depends on it, keep the handler short and move anything slow, like CRM updates, to a background job.
Make handlers idempotent
If the delivery body carries an event_id, as the mailbox format does, store it and skip repeats. See our guide to webhook idempotency and why to process webhooks asynchronously.
Conclusion
AgentLine sends every event for an agent to one webhook URL, signed with a bare hex HMAC-SHA256 of the raw body in X-Webhook-Signature. Save the secret when you create the webhook, keep the default header, verify the raw bytes, and use a webhook.test delivery to learn the body shape before you route on it.
Hookdeck Event Gateway sits in front of your receiver, verifies every AgentLine signature, and lets you filter the event stream before it reaches your service.
Get started with Hookdeck for free and handle AgentLine webhooks reliably in minutes.