Skip to content

Campaigns and journeys

A campaign is an automated marketing sequence. Contacts enter when they match the entry segment; they receive a series of templates with configurable delays; they exit when they reach a terminal node, unsubscribe, or no longer match the segment.

Journeys are ongoing, trigger-based campaigns. Broadcasts are one-time sends to a fixed audience. Both follow the same state machine and share the same API tools.

Plan Deploying campaigns requires Growth or above. AI-generated campaigns (generate_campaign) require Growth or above and consume AI credits. Library deploys (deploy_template) also require Growth or above.

{
"id": "cmp_01h7m9k0aabcdefghj1234567",
"business_id": "bus_01h7m9k0aabcdefghj1234567",
"name": "Welcome β€” first 7 days",
"type": "journey",
"status": "deployed",
"entry_segment": "seg_new_signups",
"steps": [
{ "type": "message", "template_id": "tpl_welcome_d0", "channel": "email" },
{ "type": "delay", "duration": "P3D" },
{ "type": "message", "template_id": "tpl_welcome_d3", "channel": "email" },
{ "type": "delay", "duration": "P4D" },
{ "type": "message", "template_id": "tpl_welcome_d7", "channel": "email" }
],
"stats": {
"entered": 847,
"completed": 623,
"in_progress": 181,
"exited_early": 43
},
"deployed_at": "2026-05-22T08:14:55Z",
"created_at": "2026-05-15T10:11:22Z"
}
FieldDescription
typejourney (segment-triggered, ongoing) or broadcast (one-time, fixed audience).
entry_segmentThe audience definition contacts must match to enter.
stepsOrdered sequence of message and delay nodes. duration is ISO 8601 (e.g. P3D = 3 days).
statsLive aggregate counts for deployed journeys; static after ended.
draft ──► pending_review ──► approved ──► deployed ──► ended
β–² β”‚ β”‚
β”‚ └──► (back to draft) β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
β”‚ if checks fail β–Ό β–Ό
└──────────────────────────────── paused ◄─► (resume)
StatusWhat it meansAllowed operations
draftBeing authored or refined.refine_campaign, delete_campaign
pending_reviewAutomated quality checks running.refine_campaign, delete_campaign
approvedChecks passed; ready to deploy.deploy_campaign, delete_campaign
deployedLive and sending.pause_campaign, get_campaign
pausedLive but not sending.resume_campaign
endedReached its scheduled end date, or was explicitly ended.Read-only

Calling a transition on a campaign already in the wrong state returns 409. For example, pause_campaign on a paused campaign returns 409 not_running.

The fastest path. Browse the library, pick a journey, deploy it. Caramel provisions the entry segment, every dependent template, and the journey atomically. The journey lands in deployed immediately β€” no review step, because library content is pre-vetted.

Terminal window
# Browse
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "method": "tools/call", "id": 1,
"params": { "name": "list_template_library",
"arguments": { "business_id": "bus_…", "type": "journey" } }
}'
# Deploy
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "method": "tools/call", "id": 2,
"params": { "name": "deploy_template",
"arguments": { "business_id": "bus_…", "template_id": "lib_restaurant_welcome_v3" } }
}'

See Templates for full details on the library.

Describe the campaign in plain text. Caramel generates a draft campaign using AI.

Terminal window
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "method": "tools/call", "id": 1,
"params": {
"name": "generate_campaign",
"arguments": {
"business_id": "bus_01h7m9k0aabcdefghj1234567",
"prompt": "Re-engagement campaign for customers who haven'\''t visited in 90 days. Friendly tone. 15% discount in the third email.",
"campaign_type": "journey"
}
}
}'

This consumes AI credits. The campaign starts in draft. Refine it before deploying:

Terminal window
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "method": "tools/call", "id": 2,
"params": {
"name": "refine_campaign",
"arguments": {
"campaign_id": "cmp_01h7m9k0aabcdefghj1234567",
"feedback": "Make all subject lines under 8 words. Drop the third email."
}
}
}'

Each refine_campaign call preserves conversation context and builds on the previous iteration. The campaign moves to pending_review once quality checks pass. A quality score below 0.65 keeps it in pending_review β€” refine to raise the score.

The Journeys builder in Omnichannel supports hand-authored campaigns. The public API does not expose journey-creation primitives directly. If your integration needs custom journeys, build them in the dashboard and reference them by campaign_id.

Once a campaign is approved, deploy it:

Terminal window
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "method": "tools/call", "id": 1,
"params": {
"name": "deploy_campaign",
"arguments": {
"business_id": "bus_01h7m9k0aabcdefghj1234567",
"campaign_id": "cmp_01h7m9k0aabcdefghj1234567"
}
}
}'

Deployment is atomic. Either the whole journey activates (entry segment, every step, every template) or none of it does. If a dependency check fails β€” for example, a referenced template is still in draft β€” you get an error and the campaign stays in approved.

Terminal window
# Pause
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-d '{ "jsonrpc":"2.0","method":"tools/call","id":1,
"params":{"name":"pause_campaign","arguments":{"campaign_id":"cmp_…"}} }'
# Resume
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-d '{ "jsonrpc":"2.0","method":"tools/call","id":2,
"params":{"name":"resume_campaign","arguments":{"campaign_id":"cmp_…"}} }'

While paused:

  • Contacts already mid-journey stop progressing. No pending step fires.
  • New contacts matching the entry segment do not enter.
  • On resume, mid-journey contacts continue from where they stopped. New entrants start from step 1.

Pausing is the right tool for β€œfix a typo, then resume.” Do not delete a paused campaign if you want to preserve in-progress contact state.

Deletion is permanent and only allowed on draft, approved, or paused campaigns.

Terminal window
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-d '{ "jsonrpc":"2.0","method":"tools/call","id":1,
"params":{"name":"delete_campaign","arguments":{"campaign_id":"cmp_…"}} }'

Deleting a deployed campaign returns 409 deployed_campaign β€” pause first, then delete. When you delete a paused campaign, in-progress contact state is lost; remaining steps are never sent.

Every journey has exactly one entry segment. A contact enters the journey when they first match the segment. Segment membership is evaluated continuously β€” a contact who gains a new tag or crosses a threshold re-enters matching journeys even if they’ve been in the audience for a long time.

Supported segment operators:

OperatorUse case
EqualsExact match (tag == "vip")
GreaterThanOrEqualThreshold (total_spent >= 100)
ExistsPresence check (phone IS NOT NULL)
WithinRecency window (created_at WITHIN 7 days)
And, OrComposition

Important Contains and GreaterThan are not supported by the segment engine. Using them returns a 500 error and breaks referencing journeys. The dashboard’s segment builder prevents this automatically.

TierMax message steps per journey
Starter5
Lite5
Growth8
Business12
Enterprise20
LifetimeUnlimited

Exceeding the limit causes deploy_campaign to return 422 validation. Check current usage with caramel.v1.meta.usage.