Campaigns Planned
Campaigns are proactive chat messages triggered by visitor behaviour. A campaign defines a trigger (URL match, time-on-page, exit intent, custom event), an audience filter, the message, and a schedule. When matched, the widget fires the message into the visitor's open chat.
This endpoint is on the roadmap. Surface and shape may change before launch. For early access, contact us; an alpha is running with a small group of customers.
Trigger kinds
| Kind | Config | Fires when |
|---|---|---|
page-stay | urlPattern, secondsOnPage | Visitor on a matching URL for the given seconds. |
exit-intent | urlPattern | Mouse moves toward the close-tab area. |
scroll-depth | urlPattern, percent | Visitor scrolls past a given percentage. |
custom-event | eventName | Your site fires Message.track(eventName). |
idle | secondsIdle | No interaction for the given seconds. |
List campaigns
GET/api/v1/campaignsAuth: Bearer
Code samples
cURL
curl 'https://app.message.com/api/v1/campaigns' \
-H 'Authorization: Bearer YOUR_WORKSPACE_JWT'JavaScript
const res = await fetch('https://app.message.com/api/v1/campaigns', {
headers: { Authorization: 'Bearer ' + token }
});
const { campaigns } = await res.json();Python
import requests
r = requests.get(
"https://app.message.com/api/v1/campaigns",
headers={"Authorization": f"Bearer {token}"},
)
campaigns = r.json()["campaigns"]Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/campaigns")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
http.request(req)
end
campaigns = JSON.parse(res.body)["campaigns"]PHP
<?php
$ctx = stream_context_create([
"http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$campaigns = json_decode(file_get_contents("https://app.message.com/api/v1/campaigns", false, $ctx), true)["campaigns"];Get one campaign
GET/api/v1/campaigns/:idAuth: Bearer
200 OK
{
"id": "uuid",
"version": 1,
"name": "Cart abandonment nudge",
"status": "active",
"trigger": {
"kind": "page-stay",
"urlPattern": "/cart",
"secondsOnPage": 30
},
"audience": {
"filters": { "isReturning": true, "tag": "in-cart" }
},
"message": {
"body": "Need help checking out? I can grab a code if it helps."
},
"schedule": {
"businessHoursOnly": true,
"frequencyCap": "1-per-visitor-per-day"
},
"createdAt": "2026-05-13T15:30:00Z"
}Create a campaign
POST/api/v1/campaignsAuth: Bearer
| Field | Type | Description |
|---|---|---|
| namerequired | string | Internal name. Used in reporting. |
| triggerrequired | object | Trigger config; kind drives shape. |
| audienceoptional | object | Filter on visitor attributes. Omit to target everyone. |
| messagerequired | object | The message to send when matched. |
| scheduleoptional | object | Business-hours gating and per-visitor frequency caps. |
Body
{
"name": "Cart abandonment nudge",
"trigger": {
"kind": "page-stay",
"urlPattern": "/cart",
"secondsOnPage": 30
},
"audience": {
"filters": { "isReturning": true }
},
"message": {
"body": "Need help checking out?"
}
}Update a campaign
PATCH/api/v1/campaigns/:idAuth: Bearer
Requires expectedVersion. Edits to a running campaign take effect within ~30 seconds via the live widget config push.
Pause and resume
POST/api/v1/campaigns/:id/pauseAuth: Bearer
POST/api/v1/campaigns/:id/resumeAuth: Bearer
Toggle status between paused and active without losing config.
Campaign report
GET/api/v1/campaigns/:id/reportAuth: Bearer
Returns impressions, engagements, replies, and (if a goal is attached) conversions for the date range.
Errors
| Code | When |
|---|---|
400 invalid_trigger | Trigger config invalid for its kind. |
402 plan_limit | Workspace plan does not include campaigns or the cap is hit. |
409 stale_version | Optimistic-lock mismatch. |
Common pitfalls
- No frequency cap. Without one, returning visitors see the same nudge on every visit. Default to 1-per-visitor-per-day.
- Targeting "everyone". Wide audiences feel spammy. Lean on visitor attributes (returning, segment, intent) to keep the message relevant.
- Confusing campaigns with AI engagements. Campaigns are rule-driven; AI engagements are model-driven.