Custom Webhooks give developers full control over the Atllas integration layer. You can trigger calls via HTTP POST and receive call results delivered to any endpoint you specify.
Custom Webhooks are intended for developers and technical users. If you’d prefer a no-code solution, use Zapier — Inbound to trigger calls and Zapier — Outbound to receive results instead.
Inbound Webhooks — Trigger a Call
Inbound webhooks let external systems trigger AI calls on-demand. When your CRM (or any system) sends a request to your inbound webhook URL, Atllas initiates a call automatically.
Setting Up
- Go to Integrations → Webhooks and click Inbound
- Select Custom Webhook to create this type of webhook
- A webhook is created with a unique slug
- Click Create Campaign to configure the AI script
- Set up your prompt and test it
- Use the webhook URL to trigger calls from your CRM
Endpoint
POST https://wh.atll.as/{your-webhook-slug}
Each inbound webhook is bound to a specific campaign at creation time, so the campaign does not need to be specified per request.
Request Body
{
"type": "calls.execute",
"data": {
"phoneNumber": "+15551234567",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"companyName": "Acme Corp",
"note": "Expressed interest in the enterprise plan"
}
}
| Field | Required | Description |
|---|
type | Yes | Must be "calls.execute" |
data.phoneNumber | Yes | E.164 format recommended (+15551234567) |
data.firstName | No | Used for personalisation |
data.lastName | No | |
data.email | No | Used for email follow-ups if configured |
data.companyName | No | |
data.note | No | Additional context for this contact |
data.address1, data.address2, data.city, data.state, data.zipCode | No | Address fields, available for personalisation |
Field names are camelCase. Sending snake_case (e.g. first_name, phone_number) will be ignored — the call will be placed using only phoneNumber.
Response
A 201 response means the request was accepted. The campaign attached to the webhook must be active for the call to actually be placed; if the user is out of credits, the call is recorded as cancelled even though the response status is the same.
Customising the Phone Call
The webhook is bound to an inbound campaign in AI Calling. That campaign owns every call setting — voice, call script, voicemail message, follow-ups, call forwarding, keypad navigation, language, background ambiance, and so on. Whatever’s on the campaign is what every webhook-triggered call uses.
To change call behaviour:
- Open AI Calling and find the campaign your webhook is bound to (it’ll be labelled as inbound)
- Edit any setting — voice, script, follow-ups, voicemail, etc.
- Save
Inbound campaigns can be edited at any time (unlike outbound campaigns, which lock once they start dialing). Changes apply to all future calls triggered by the webhook — already-completed calls keep the settings they ran with.
Viewing Results
Every call triggered by the webhook appears in the bound campaign’s results table in AI Calling — the same place outbound campaign results live.
Each call result includes the transcript, recording, AI summary, lead warmth classification, and extracted action items. See Call Results & Transcripts for what’s in the detail panel.
If you also want each completed call delivered to your own endpoint as it happens, configure a Response Webhook on the inbound webhook (covered below) — Atllas will POST the result back to your URL when the call wraps up.
Outbound Webhooks — Receive Call Results
Outbound webhooks broadcast every call completion event to your configured endpoint(s). Whenever a call finishes — whether it came from a manual campaign, an inbound webhook trigger, or a test call — Atllas POSTs the results to every outbound webhook URL you’ve configured.
Creating an outbound webhook manually is only for Custom Webhooks. If you want to send call results to Zapier, set up an outbound Zapier integration instead — its outbound webhook is created automatically and labelled Managed by Zapier.
Setting Up
- Go to Integrations and click Outbound
- Enter your endpoint URL
- Select HTTP method (POST or GET)
- Optionally add custom headers for authentication (see Custom Headers)
- Click Test to verify connectivity, then save
Event
Outbound webhooks emit a single event:
| Event | When |
|---|
call.finished | Fires after the call completes and final analysis is attached (transcript, summary, lead warmth) |
Payload
{
"type": "call.finished",
"timestamp": 1730179200000,
"webhook": {
"id": "webhook_abc123",
"type": "outbound"
},
"call": {
"id": "call_xyz789",
"status": "finished",
"recipient": {
"phoneNumber": "+15551234567",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"address1": "123 Main St",
"city": "Austin",
"state": "TX",
"zipCode": "78701",
"note": "VIP lead"
},
"summary": "Jane expressed interest in the enterprise plan and asked about pricing.",
"transcript": [
{ "who": "assistant", "what": "Hi, is this Jane?", "when": "2024-04-30T14:00:03.000Z" },
{ "who": "user", "what": "Yes, speaking.", "when": "2024-04-30T14:00:05.000Z" }
],
"recording": "https://...",
"duration": 142,
"analysis": {
"sentiment": "positive",
"lead_warmth": "hot",
"justification": "Expressed immediate interest and asked specific pricing questions."
},
"tokensConsumed": 1
},
"campaign": {
"id": "campaign_123",
"name": "Q4 Outreach"
}
}
Field Reference
| Path | Description |
|---|
type | Always "call.finished" |
timestamp | Unix epoch in milliseconds |
webhook.id | The outbound webhook’s ID |
webhook.type | Always "outbound" |
call.id | The call ID |
call.status | One of: finished, missed, failed |
call.recipient.phoneNumber | Contact phone in E.164 |
call.recipient.firstName / lastName / email | Contact identity fields |
call.recipient.address1 / address2 / city / state / zipCode / note | Optional address + note |
call.summary | One-paragraph AI summary |
call.transcript | Array of { who, what, when } turns. who is typically "user" (caller) or "assistant" (AI); when is an ISO timestamp |
call.recording | URL to the MP3 recording |
call.duration | Call length in seconds |
call.analysis.sentiment | positive, neutral, or negative |
call.analysis.lead_warmth | hot, warm, cold, dead, or unknown |
call.analysis.justification | AI’s reasoning for the warmth classification |
call.tokensConsumed | Credits used for the call |
campaign.id / campaign.name | Source campaign info |
Status Values
| Status | Meaning |
|---|
finished | Call completed successfully |
missed | Recipient didn’t answer or went to voicemail |
failed | Technical error occurred |
Lead Warmth Values
| Value | Meaning |
|---|
hot | High interest, ready to move forward |
warm | Some interest, needs nurturing |
cold | Low interest or not qualified |
dead | No interest or wrong number |
unknown | Unable to determine |
Sentiment Values
| Value | Meaning |
|---|
positive | Friendly, receptive conversation |
neutral | Professional, factual interaction |
negative | Frustrated or hostile |
Recipient field names are camelCase (firstName, phoneNumber) and there is no company field on the recipient. Build your integrations against the names shown above.
Response Webhooks
Response webhooks are configured on inbound webhooks to send call results back to a specific endpoint after an inbound-triggered call completes — forming a request/response pattern.
Setting Up
- Click Configure on your inbound webhook
- Scroll to Response Webhook (Optional)
- Enter your callback URL
- Optionally add custom headers
The response webhook fires with type: "call.completed" and the same overall payload shape as outbound webhooks — with one difference: recipient, summary, transcript, recording, duration, analysis, and tokensConsumed are all optional, since some calls (e.g. ones that never connected) won’t have all fields populated. Note that outbound webhooks use type: "call.finished", not call.completed.
Custom headers let you authenticate against CRMs or other systems that require API keys or tokens.
- When creating or editing a webhook, expand Custom Headers (Optional)
- Click Add Header
- Enter header name and value (e.g.
Authorization, Bearer token123)
- Headers are encrypted at rest and never exposed in the portal after saving
FollowUp Boss Example
FollowUp Boss requires these headers:
X-System: your-app-name
X-System-Key: your-api-key
Authorization: Basic base64(api-key:)
Add them as:
X-System → atllas-integration
X-System-Key → your-followupboss-api-key
Authorization → Basic {base64-encoded-key}
HubSpot Example
HubSpot workflow webhooks typically don’t require custom headers — just configure the webhook URL in your HubSpot workflow.
For HubSpot API endpoints, add:
Authorization: Bearer your-access-token
Testing & Logs
Each webhook has a Test button that sends a call.completed-shaped payload with example data:
{
"type": "call.completed",
"timestamp": 1730179200000,
"webhook": { "id": "webhook_test_outbound_456", "type": "outbound" },
"call": {
"id": "call_test_abc123def456",
"status": "finished",
"recipient": {
"phoneNumber": "+15555551234",
"firstName": "Jessica",
"lastName": "Smith",
"email": "jessica.smith@example.com"
},
"summary": "Example call summary from the test button.",
"transcript": [
{ "who": "assistant", "what": "Hi Jessica, this is Alex.", "when": "2024-04-30T14:00:03.000Z" },
{ "who": "user", "what": "I'm doing well, thanks for calling!", "when": "2024-04-30T14:00:05.000Z" }
],
"recording": "https://storage.googleapis.com/atllas-call-recordings/test-recording-example.mp3",
"duration": 123,
"analysis": { "sentiment": "positive", "lead_warmth": "hot", "justification": "Example justification." },
"tokensConsumed": 42
},
"campaign": { "id": "campaign_test_xyz789", "name": "Example Campaign" }
}
The test validates:
- The URL is reachable from our servers
- Custom headers are configured correctly
- Your endpoint responds with a 2xx status
The test payload uses call.completed as the event type, while real outbound webhook deliveries use call.finished. If your endpoint routes behaviour by event type, handle both before relying on it in production.
Delivery Logs
The delivery log shows recent webhook delivery attempts with:
- Timestamp
- HTTP status code returned by your endpoint
- Response body (truncated)
- Whether delivery succeeded or failed
Look for these status code ranges when debugging:
200–299: Success
400–499: Client error (check your endpoint)
500–599: Server error (your endpoint is down)
errored: Network/timeout failure
Troubleshooting
Webhook Not Firing
For inbound webhooks:
- Verify the campaign is created (click Create Campaign)
- Check the campaign has a prompt configured
- Ensure you’re sending the correct payload format (
calls.execute)
- Check webhook logs for incoming requests
For outbound webhooks:
- Verify the URL is correct and reachable
- Use the Test button to verify connectivity
- Check delivery logs for error messages
Authentication Errors
If receiving 401 or 403 errors:
- Verify custom headers are correct
- Check that the API key hasn’t expired
- Test with a simple endpoint (such as
webhook.site) first
- Review the destination system’s authentication requirements
Call Not Triggering from Inbound Webhook
- Ensure campaign status is not
executing or completed
- Check you have available calling credits
- Verify the phone number is in correct format (
+1...)
- Check webhook logs for “insufficient funds” errors
Security Best Practices
- Keep webhook URLs secret — treat them like API keys
- Use HTTPS only — never use HTTP endpoints
- Monitor delivery logs — watch for unusual activity
- Rotate headers periodically — update API keys regularly
- Test in staging first — use the Test button before production