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.
The campaign object
Section titled βThe campaign objectβ{ "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"}| Field | Description |
|---|---|
type | journey (segment-triggered, ongoing) or broadcast (one-time, fixed audience). |
entry_segment | The audience definition contacts must match to enter. |
steps | Ordered sequence of message and delay nodes. duration is ISO 8601 (e.g. P3D = 3 days). |
stats | Live aggregate counts for deployed journeys; static after ended. |
Deployment state machine
Section titled βDeployment state machineβ draft βββΊ pending_review βββΊ approved βββΊ deployed βββΊ ended β² β β β ββββΊ (back to draft) ββββββ΄βββββ β if checks fail βΌ βΌ βββββββββββββββββββββββββββββββββ paused βββΊ (resume)| Status | What it means | Allowed operations |
|---|---|---|
draft | Being authored or refined. | refine_campaign, delete_campaign |
pending_review | Automated quality checks running. | refine_campaign, delete_campaign |
approved | Checks passed; ready to deploy. | deploy_campaign, delete_campaign |
deployed | Live and sending. | pause_campaign, get_campaign |
paused | Live but not sending. | resume_campaign |
ended | Reached 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.
Three paths to a deployed campaign
Section titled βThree paths to a deployed campaignβPath 1 β Deploy a library entry
Section titled βPath 1 β Deploy a library entryβ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.
# Browsecurl -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" } } }'
# Deploycurl -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.
Path 2 β Generate from natural language
Section titled βPath 2 β Generate from natural languageβDescribe the campaign in plain text. Caramel generates a draft campaign using AI.
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:
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.
Path 3 β Author in the dashboard
Section titled βPath 3 β Author in the dashboardβ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:
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.
Pause and resume
Section titled βPause and resumeβ# Pausecurl -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_β¦"}} }'
# Resumecurl -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.
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.
Entry segments
Section titled βEntry segmentsβ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:
| Operator | Use case |
|---|---|
Equals | Exact match (tag == "vip") |
GreaterThanOrEqual | Threshold (total_spent >= 100) |
Exists | Presence check (phone IS NOT NULL) |
Within | Recency window (created_at WITHIN 7 days) |
And, Or | Composition |
Important
ContainsandGreaterThanare not supported by the segment engine. Using them returns a500error and breaks referencing journeys. The dashboardβs segment builder prevents this automatically.
Per-tier step limits
Section titled βPer-tier step limitsβ| Tier | Max message steps per journey |
|---|---|
| Starter | 5 |
| Lite | 5 |
| Growth | 8 |
| Business | 12 |
| Enterprise | 20 |
| Lifetime | Unlimited |
Exceeding the limit causes deploy_campaign to return 422 validation. Check current usage with caramel.v1.meta.usage.
Next steps
Section titled βNext stepsβ- Templates β the message content journeys send
- Forms and audience β how contacts enter entry segments
- Tools reference β
generate_campaign,deploy_campaign,pause_campaign, and more