Marketing Platform Integration
Trigger voice calls directly from CleverTap, MoEngage, Braze, and other marketing platforms
Marketing Platform Integration
This guide covers how to integrate Agent Studio with marketing automation platforms like CleverTap, MoEngage, Braze, and WebEngage to trigger voice calls directly from campaigns.
Overview
Marketing platforms can trigger Agent Studio calls via a webhook ingestion endpoint, eliminating the need for a custom backend service in between.
┌─────────────────┐ Webhook ┌─────────────────┐ Voice Call ┌─────────────────┐
│ CleverTap │ ───────────────► │ Agent Studio │ ────────────────► │ User │
│ Campaign │ │ /webhooks/ │ │ (+91...) │
│ │ │ ingest │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘Benefits:
- No custom backend service required
- Direct integration from marketing platform
- Multi-tenant: each tenant configures their own campaigns
- Works with any platform that supports outbound webhooks
Quick Start
1. Create an API Key
- Go to Dashboard → Settings → API Keys
- Create a key with scope:
calls:create - Save the key securely
2. Get Your Webhook URL
Your webhook ingestion URL is:
POST https://api.yourdomain.com/api/v1/webhooks/ingest3. Configure Your Marketing Platform
In your marketing platform's webhook/campaign settings, configure:
| Field | Value |
|---|---|
| URL | https://api.yourdomain.com/api/v1/webhooks/ingest |
| Method | POST |
| Headers | Authorization: Bearer {your_api_key} |
| Content-Type | application/json |
Request Format
{
"workflow_slug": "free-trial-followup",
"phone_number": "+919876543210",
"user_id": "user-123",
"user_context": {
"name": "John",
"trial_started_at": "2026-02-01",
"plan_type": "premium"
},
"metadata": {
"campaign_id": "trial_recovery_jan",
"source": "clevertap"
}
}Required Fields
| Field | Type | Description |
|---|---|---|
phone_number | string | User's phone number in E.164 format (e.g., +919876543210) |
One of These Required
| Field | Type | Description |
|---|---|---|
workflow_slug | string | Workflow to execute (for multi-agent flows) |
agent_slug | string | Single agent to use (for simple calls) |
Note: Provide either
workflow_slugORagent_slug, not both.
Optional Fields
| Field | Type | Description |
|---|---|---|
user_id | string | External user identifier for tracking |
user_context | object | Data passed to the agent (name, preferences, etc.) |
metadata | object | Additional tracking data (campaign_id, source, etc.) |
Response
Success (201 Created)
{
"call_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}Errors
| Status | Meaning |
|---|---|
400 | Invalid request (missing fields, invalid phone number) |
401 | Invalid or missing API key |
403 | API key lacks calls:create scope |
404 | Workflow or agent not found |
422 | Validation error (e.g., both workflow_slug and agent_slug provided) |
Platform-Specific Setup
CleverTap
- Go to Settings → Channels → Webhooks
- Create a new webhook:
Name: Agent Studio Voice Call
URL: https://api.yourdomain.com/api/v1/webhooks/ingest
Method: POST
Headers:
Authorization: Bearer as_live_xxxxx
Content-Type: application/json- In your campaign, use the webhook action with this body template:
{
"workflow_slug": "free-trial-followup",
"phone_number": "$Phone",
"user_id": "$Identity",
"user_context": {
"name": "$Name",
"email": "$Email"
},
"metadata": {
"campaign_id": "$CampaignId",
"source": "clevertap"
}
}MoEngage
- Go to Settings → App Settings → APIs
- Add a Custom Connector:
Connector Name: Agent Studio
Base URL: https://api.yourdomain.com
Authentication: API Key
Header Name: Authorization
Header Value: Bearer as_live_xxxxx- Create a webhook action in your campaign:
Endpoint: /api/v1/webhooks/ingest
Method: POST
Body:
{
"workflow_slug": "free-trial-followup",
"phone_number": "{{UserAttribute['phone']}}",
"user_id": "{{UserAttribute['user_id']}}",
"user_context": {
"name": "{{UserAttribute['name']}}"
},
"metadata": {
"campaign_id": "{{CampaignId}}",
"source": "moengage"
}
}Braze
- Go to Settings → Connected Content
- In your campaign, use a webhook:
Webhook URL: https://api.yourdomain.com/api/v1/webhooks/ingest
HTTP Method: POST
Headers:
Authorization: Bearer as_live_xxxxx
Content-Type: application/json
Body:
{
"workflow_slug": "free-trial-followup",
"phone_number": "{{${phone_number}}}",
"user_id": "{{${user_id}}}",
"user_context": {
"name": "{{${first_name}}}"
},
"metadata": {
"campaign_id": "{{campaign.${api_id}}}",
"source": "braze"
}
}WebEngage
- Go to Data Platform → Integrations → REST API
- Configure the webhook:
URL: https://api.yourdomain.com/api/v1/webhooks/ingest
Method: POST
Headers:
Authorization: Bearer as_live_xxxxx
Content-Type: application/json
Payload Template:
{
"workflow_slug": "free-trial-followup",
"phone_number": "${user.phone}",
"user_id": "${user.userId}",
"user_context": {
"name": "${user.firstName}"
},
"metadata": {
"campaign_id": "${journey.journeyId}",
"source": "webengage"
}
}Using Agent Context
Data passed in user_context is available to your agents via template variables:
In Agent Prompts
You are calling {{user.name}} about their {{user.plan_type}} trial
that started on {{user.trial_started_at}}.In Tool Actions
{
"type": "api_call",
"url": "https://your-api.com/calls/log",
"body": {
"phone": "{{context.phone_number}}",
"user_id": "{{user.user_id}}",
"campaign": "{{metadata.campaign_id}}"
}
}Available Variables
| Variable | Source |
|---|---|
{{user.*}} | From user_context in request |
{{context.phone_number}} | Phone number being called |
{{metadata.*}} | From metadata in request |
{{params.*}} | Tool parameters (during tool execution) |
Workflow vs Agent
Use workflow_slug when:
- You have multi-agent flows with handoffs
- Different scenarios route to different agents
- You need shared context across agents
{
"workflow_slug": "customer-support",
"phone_number": "+919876543210"
}Use agent_slug when:
- Simple single-agent calls
- Testing individual agents
- No handoffs needed
{
"agent_slug": "trial-followup-agent",
"phone_number": "+919876543210"
}Tracking Calls
Via Metadata
Include tracking data in metadata:
{
"workflow_slug": "trial-followup",
"phone_number": "+919876543210",
"metadata": {
"campaign_id": "jan_trial_recovery",
"variant": "A",
"source": "clevertap",
"segment": "high_value"
}
}This data appears in:
- Call detail view in dashboard
- Call status webhooks (if configured)
- Analytics exports
Via Call Status Webhooks
Configure Call Status Webhooks to receive notifications when calls complete:
{
"type": "call.completed",
"data": {
"call_id": "...",
"metadata": {
"campaign_id": "jan_trial_recovery",
"source": "clevertap"
},
"duration_seconds": 120,
"transcript": [...]
}
}Rate Limits
| Limit | Value |
|---|---|
| Requests per minute | 600 |
| Concurrent calls | Based on your plan |
For high-volume campaigns, consider:
- Staggering campaign sends
- Using campaign scheduling in your marketing platform
- Contacting support for limit increases
Error Handling
In CleverTap
Configure retry settings:
- Retry on: 5xx errors, timeouts
- Max retries: 3
- Backoff: Exponential
Monitoring Failed Calls
- Check Dashboard → Calls for failed status
- Review error reasons in call details
- Set up Call Status Webhooks for
call.failedevents
Security Best Practices
- Use separate API keys per platform - Easier to rotate and audit
- Use Live keys for production - Test keys have rate limits
- Restrict key scopes - Only grant
calls:create - Monitor API key usage - Review in dashboard settings
- Rotate keys periodically - Especially if team members leave
Example: Free Trial Recovery Campaign
Workflow Setup
Create a workflow free-trial-followup with an agent that:
You are a Tap Health voice assistant conducting a brief follow-up call.
The user {{user.name}} started their free trial on {{user.trial_started_at}}
but didn't complete activation.
1. Greet them briefly
2. Mention they started a free trial but didn't complete it
3. Ask if they faced any issues
4. Offer to help them complete it
5. Thank them and end the call
Use the capture_lead_response tool to record their feedback.CleverTap Campaign
Trigger: User property trial_status = incomplete AND days_since_trial_start > 1
Webhook body:
{
"workflow_slug": "free-trial-followup",
"phone_number": "$Phone",
"user_id": "$Identity",
"user_context": {
"name": "$Name",
"trial_started_at": "$trial_start_date",
"plan_type": "$selected_plan"
},
"metadata": {
"campaign_id": "trial_recovery_v2",
"source": "clevertap",
"segment": "$user_segment"
}
}Result Tracking
Configure a call status webhook to receive results:
@app.post("/webhooks/call-results")
async def handle_call_result(request: Request):
event = await request.json()
if event["type"] == "call.completed":
data = event["data"]
# Update CleverTap with call result
await clevertap.profile_push({
"identity": data["user_id"],
"properties": {
"last_voice_call": datetime.now().isoformat(),
"voice_call_status": "completed",
"voice_call_duration": data["duration_seconds"],
}
})Related
- Backend Integration Guide - For custom backend integrations
- Call Status Webhooks - Receive call completion notifications
- Tool Editor - Create tools to capture data during calls