Skip to content

Campaigns & Recipients

Ragtime models one-to-one outreach as campaigns and recipients:

  • A campaign is a scheduled outreach with its own target device, schedule (start/end), budget, and lifecycle (draft → upcoming → active → completed). Campaigns are created and configured in the dashboard.
  • A recipient is a personalized chat link (/i/<token>) inside a campaign — one per customer — carrying its own per-person instructions and interaction limits. Recipients are created and managed through the v1 API (or the dashboard).

Each recipient inherits its campaign’s device as the rendering surface, so it takes on that device’s styling, languages, voice, and access rules.

Typical use case: a one-to-one outreach where you create one link per contact (keyed by email), optionally pre-loading each link with personalized context (“greet them by name, reference their recent order”) and capping how often they can interact.

  1. Create a campaign in the dashboard (choose its device, schedule, and budget). It starts as a draft.
  2. While the campaign is a draft or upcoming, create recipients in it via the API — you get back a unique link (containing an unguessable token).
  3. Launch the campaign in the dashboard. Its links go live while it is active.
  4. Share each link with your customer (e.g. by email).
  5. When they open it during the active window, the assistant applies the recipient’s stored instructions and enforces its limits.
  6. You can list, update, disable, or delete recipients while the campaign is editable.
Scope Allows
recipients:read List and fetch recipients and their state.
recipients:write Create, update, and delete recipients.

See API Keys to create a key with these scopes.

Recipients are scoped to a campaign, so you first need a campaign id. Campaigns are created in the dashboard, but you can list them (with their current state and id) via the API:

Terminal window
curl https://your-ragtime-instance.com/api/v1/projects/{projectId}/campaigns \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE"
{
"projectId": "...",
"campaigns": [
{
"id": "c1a2b3...",
"name": "Spring 2026 outreach",
"state": "active",
"budgetMode": "metered",
"deviceId": "d4e5f6...",
"startDate": "2026-07-01T00:00:00Z",
"endDate": "2026-07-31T00:00:00Z",
"recipientCount": 42,
"timing": {
"state": "active",
"now": "2026-07-02T12:00:00Z",
"startsInSeconds": null,
"runningForSeconds": 129600,
"endedAgoSeconds": null
}
}
]
}

Use the id of a campaign whose state is draft or upcoming as the campaignId when creating recipients. Creating a campaign, launching it, setting its budget, and halting it are dashboard actions and are not exposed in the v1 API.

startDate and endDate are absolute ISO 8601 instants (returned in UTC, e.g. 2026-07-01T00:00:00Z). They represent a single point in time, so the campaign transitions between states at the same instant for everyone, regardless of where the caller or their customers are located. Format them into any local timezone you like on your side.

Every campaign includes a timing object so you don’t have to do date math to answer “how long until it starts / how long has it been running / how long ago did it end.” All durations are whole seconds, and only the field relevant to the current state is populated (the rest are null):

Field Populated when Meaning
state always The campaign’s lifecycle state.
now always Server time (UTC) the values were computed at.
startsInSeconds upcoming Seconds until the campaign starts.
runningForSeconds active Seconds the campaign has been running.
endedAgoSeconds completed Seconds since the campaign ended (or was halted).

Because the values are language-agnostic seconds, you can render them however suits your integration (e.g. "Launching in 4d 3h", "Ended 31m ago").

Send a POST with the target campaignId and the recipient’s details. The response includes the shareable link. The rendering device is inherited from the campaign.

Terminal window
curl -X POST https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"campaignId": "c1a2b3...",
"externalId": "jane@northwind.example",
"label": "Jane Doe",
"maxTotalInteractions": 5,
"maxInteractionsPerWindow": 1,
"windowSeconds": 86400,
"injectionMode": "append",
"injectionInstructions": "The customer is Jane Doe. Greet her by name and reference her recent onboarding."
}'
// 201 Created
{
"status": "created",
"id": "a1b2c3d4-...",
"campaignId": "c1a2b3...",
"externalId": "jane@northwind.example",
"label": "Jane Doe",
"deviceId": "d4e5f6...",
"isActive": true,
"link": "https://your-ragtime-instance.com/i/8f3aK2...token...",
"injectionMode": "append",
"injectionInstructions": "The customer is Jane Doe...",
"maxTotalInteractions": 5,
"maxInteractionsPerWindow": 1,
"windowSeconds": 86400,
"interactionCount": 0,
"lastInteractionAt": null,
"createdAt": "2026-06-29T10:00:00Z"
}

The campaign referenced by campaignId must be editable (draft or upcoming). Creating recipients in an active or completed campaign returns 409.

Field Type Required Description
campaignId string Yes The campaign the recipient belongs to. Must be draft or upcoming.
externalId string No Your own identifier (e.g. email). Unique per campaign; enables idempotent batches.
label string No Display name for the recipient. Max 191 chars.
isActive boolean No Defaults to true. Disabled links show a blocked message.
maxTotalInteractions integer\ null No Lifetime cap on interactions. null = unlimited.
maxInteractionsPerWindow integer\ null No Cap on interactions within a rolling window.
windowSeconds integer\ null No Length of the rolling window, in seconds. Pairs with maxInteractionsPerWindow.
injectionMode string No "append" (default) adds to the base instructions; "replace" overrides them.
injectionInstructions string No Personalized instructions for this recipient. Supports {email} / {label} placeholders (see below). Limit depends on injectionMode: append ≤ 500 words, replace ≤ 7500 characters.
disabledMessage string No Message shown when the recipient is disabled or its total limit is reached.
cooldownMessage string No Message shown when the per-window limit is reached.

injectionInstructions supports two placeholders that are resolved per recipient at create/update time and stored already-substituted:

  • {email} → the recipient’s externalId
  • {label} → the recipient’s label

This makes a single template unique per row — ideal for batch imports. For example, "My name is {label}." becomes "My name is Jane Doe." for a recipient labelled Jane Doe. Placeholders are case-insensitive; unknown placeholders are left untouched, and a missing value resolves to an empty string.

The limit is mode-dependent, because append text layers on top of the assistant’s full base prompt while replace swaps the custom-instructions block:

  • injectionMode: "append" — up to 500 words.
  • injectionMode: "replace" — up to 7500 characters.

In both modes the assistant’s underlying base prompt (persona, safety, knowledge retrieval) still applies — replace only swaps the custom-instructions block, not the entire system prompt.

externalId is unique per campaign. Re-creating a recipient with an existing externalId in the same campaign returns 409 Conflict (single) or is reported as "duplicate" (batch). The same externalId can be reused across different campaigns. Recipients created without an externalId are never deduplicated.

Send an items array (up to 200 per request). A shared campaignId applies to all items.

Terminal window
curl -X POST https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"campaignId": "c1a2b3...",
"items": [
{ "externalId": "jane@northwind.example", "label": "Jane Doe" },
{ "externalId": "john@northwind.example", "label": "John Roe", "maxTotalInteractions": 3 }
]
}'
// 201 Created (or 422 if every item failed)
{
"created": 2,
"failed": 0,
"total": 2,
"results": [
{ "status": "created", "id": "...", "externalId": "jane@northwind.example", "link": "https://.../i/..." },
{ "status": "created", "id": "...", "externalId": "john@northwind.example", "link": "https://.../i/..." }
]
}
"externalId": "jane@northwind.example",

Returns recipients in a campaign with their current state and limits. Paginated. campaignId is required.

Terminal window
curl "https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients?campaignId=c1a2b3...&limit=50&offset=0&active=true" \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE"
{
"projectId": "...",
"campaignId": "c1a2b3...",
"total": 1280,
"limit": 50,
"offset": 0,
"allowance": {
"used": 420,
"included": 1000,
"remaining": 580,
"exhausted": false
},
"recipients": [
{
"id": "...",
"campaignId": "c1a2b3...",
"externalId": "jane@northwind.example",
"label": "Jane Doe",
"isActive": true,
"link": "https://your-ragtime-instance.com/i/...",
"interactionCount": 2,
"maxTotalInteractions": 5,
"maxInteractionsPerWindow": 1,
"windowSeconds": 86400,
"lastInteractionAt": "2026-06-28T14:11:00Z",
"createdAt": "2026-06-20T09:00:00Z"
}
]
}
Query param Description
campaignId Required. The campaign whose recipients to list.
limit Page size (1–200, default 50).
offset Number of rows to skip.
active true or false to filter by active state.

total and allowance reflect the campaign, so you can paginate with limit/offset.

Terminal window
curl https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients/{recipientId} \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE"

Returns the same object shape as the list entries. Responds 404 if the recipient is not in this project.

PATCH any subset of: label, isActive, injectionMode, injectionInstructions, disabledMessage, cooldownMessage, maxTotalInteractions, maxInteractionsPerWindow, windowSeconds. Send null to clear a nullable field.

Terminal window
curl -X PATCH https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients/{recipientId} \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "isActive": false, "maxTotalInteractions": 10 }'

The token/link, campaignId, externalId, and interactionCount are immutable.

Terminal window
curl -X DELETE https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients/{recipientId} \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE"
# 200 OK
{ "success": true, "id": "a1b2c3d4-..." }

Delete a list of recipients, or all of them for the project. This endpoint is project-wide (it spans every campaign), so use the ids form to stay within one campaign.

Terminal window
# By id (max 500 per request)
curl -X POST https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients/bulk-delete \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "ids": ["id-1", "id-2", "id-3"] }'
# Everything
curl -X POST https://your-ragtime-instance.com/api/v1/projects/{projectId}/recipients/bulk-delete \
-H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{ "all": true }'
{ "success": true, "deleted": 3, "ids": ["id-1", "id-2", "id-3"] }

When a customer opens their /i/<token> link:

  • The recipient’s campaign must be active (within its start/end window and not halted). Links for draft, upcoming, or completed campaigns show a neutral “currently unavailable” message.
  • The campaign’s device rules apply next (active state, IP allow-list).
  • The recipient’s instructions are merged into the assistant’s system prompt server-side. Because they are stored by you (the operator), they are trusted and applied regardless of the persona — append adds to the base instructions, replace overrides them. This differs from Chat Injection, where the embedding page supplies instructions at runtime and is gated by deployment settings.
  • The recipient’s limits are enforced.

One interaction is one conversation (session). Continuing a conversation within the same page load does not consume another interaction — reloading the link starts a new one.

  • maxTotalInteractions — once reached, the link is blocked and shows disabledMessage.
  • maxInteractionsPerWindow + windowSeconds — limits how often a recipient can start a new conversation (e.g. once per 24h). When exceeded, the link shows cooldownMessage and tells them to come back later.

interactionCount and lastInteractionAt on each recipient reflect live usage.

Each campaign can set an optional cap on how many recipient links it holds. Creating recipients beyond the cap returns 409:

{ "error": "Recipient limit exceeded", "cap": 2000, "existing": 2000, "requested": 1 }

The cap is set per campaign in Dashboard → Deployment → Campaigns. It limits how many recipient links a campaign can hold at once; it is an operational guardrail, not a billing meter.

Conversations through recipient links are metered against the campaign’s budget — a rolling bucket of conversations (the “taxi-meter” / pay-per-conversation mode). Each new conversation (session) consumes one unit. The counter only ever increases: deleting recipients does not refund usage.

  • The budget is set per campaign by an org admin or project owner in Dashboard → Deployment → Campaigns, and extended with top-offs.
  • A fixed-price (buffet) campaign is uncapped instead — usage is kept in check by the per-recipient limits, and conversations are prepaid at $0 each.
  • When a metered budget is used up, new conversations are hard-stopped — the link shows a neutral “currently unavailable” message until the budget is increased. In-progress conversations are unaffected.
  • A metered campaign with no cap set is treated as unlimited.

The list endpoint reports the current budget state for the campaign so integrations can detect when it’s used up:

{
"projectId": "...",
"campaignId": "c1a2b3...",
"total": 1280,
"allowance": {
"used": 1280,
"included": 1280,
"remaining": 0,
"exhausted": true
},
"recipients": [ ... ]
}

When exhausted is true, new conversations on that campaign’s recipient links return 403 until the budget is raised.

Each recipient conversation is billed at the campaign’s price per conversation. This price is managed in the dashboard (Deployment → Campaigns), not through the API.

  • The price is set per campaign. A metered campaign with no price set bills recipient conversations at no charge.
  • The price is captured on each budget top-off, so past budget changes keep the price they were made at even if you change it later (visible in the campaign’s budget history and CSV export).
  • The recipient limit (how many links a campaign can hold) is separate from the conversation budget and price.
  • A fixed-price (buffet) campaign bills a flat agreed total in the month it is launched instead; its per-conversation price is $0.

Pricing is not exposed in the v1 API: budgets, prices, schedules, and limits are configured in the dashboard, while the API creates and manages the recipient links themselves.

Like all v1 endpoints, recipient endpoints are rate-limited per API key. Every response includes:

Header Description
X-RateLimit-Limit Maximum requests per window.
X-RateLimit-Remaining Remaining requests in current window.
X-RateLimit-Reset Unix timestamp when the window resets.
Retry-After Seconds to wait (only on 429).
Status Meaning
200 Success (list, get, update, delete, bulk delete).
201 Recipient(s) created.
400 Invalid request (bad field, missing campaignId, batch too large).
401 Missing or invalid API key.
403 Key doesn’t match the project or lacks the required scope.
404 Recipient or campaign not found in this project.
409 Duplicate externalId, campaign not editable, or its recipient cap is exceeded.
422 Batch create where every item failed.
429 Rate limit exceeded. Check Retry-After.

You can also manage everything without the API in Dashboard → Deployment → Campaigns: create campaigns (choosing device, schedule, and budget mode), add recipients individually or in bulk, launch and halt campaigns, top off budgets, copy links, edit per-recipient instructions and limits, purge or bulk-delete recipients, and export a campaign report.