Choose an A/B test's winner
POST/v1/campaigns/{id}/ab-test/winner
Requires an API key with the campaigns:send scope. About keys and scopes
Works with integration and partner keys, not agent keys.
Safe to retry: a repeat returns the current state, or 409 once it has moved on from it. About idempotency
Picks the winning version of a campaign in testing and sends it to the rest of the audience at once, instead of waiting for the automatic pick (or when the test has none, decideAfter: null). The campaign turns sending, then sent. Asking again for the version already picked returns 202 and queues its send again, so a retry is safe; another version once one is picked, or a campaign not in testing, is refused with 409 CONFLICT. Agent keys are refused with 403: this sends.
Path parameters
idstringrequiredThe campaign's id.
uuid
Header parameters
Kirora-WorkspacestringoptionalPartner keys only: the child workspace to act in, by the external id the partner gave it. Without it, a partner key acts in its own workspace. Any other key that sends it is refused with 403, and an unknown workspace is 404.
pattern
^[A-Za-z0-9._:-]{1,128}$Idempotency-KeystringoptionalMakes a retry safe: a repeat with the same key and body within 24 hours gets the first response again, with
Idempotency-Replay: true. The same key with another request is 409IDEMPOTENCY_KEY_REUSED; while the first is still running, 409IDEMPOTENCY_KEY_IN_USE; after anunsafefirst request failed or never returned, 409IDEMPOTENCY_KEY_OUTCOME_UNKNOWN. Use a new key, such as a UUID, for each new request.8–255 characters
Request body
versionintegerrequiredThe version the rest of the audience gets.
min 1 · max 4
Returns
Returns 202 Accepted with a Campaign in data. It can also return 409 Conflict, 413 Payload Too Large or 415 Unsupported Media Type; see the statuses below. Otherwise returns an error; Errors lists every code and which to retry.
202 AcceptedThe campaign with its winner; the rest of the audience is being queued.409 ConflictThe request conflicts with current state — a duplicate unique value, or a resource whose status does not allow this operation.413 Payload Too LargeThe body is larger than this operation accepts (PAYLOAD_TOO_LARGE).415 Unsupported Media TypeThe body is not JSON, or not the upload the operation takes (UNSUPPORTED_MEDIA_TYPE).
idstringrequireduuid
namestringrequiredchannelsstring[]requiredThe channels it sends on.
allContactsbooleanrequiredWhether it goes to all contacts: everyone its channels reach, with consent and suppressions applied as on any send.
listIdsstring[] | nullrequiredThe lists it goes to, if any: a member of any of them receives it once.
segmentIdstring | nullrequiredThe segment it goes to, if any.
uuid
audienceNamestring | nullrequiredThe names of the lists or the segment it went to, once one of them has been deleted (the list leaves
listIds, orsegmentIdbecomesnull). Only a sent or cancelled campaign can lose its audience this way.audienceRulesobject | nullrequiredInline rules choosing the audience, if any. At most one audience is set.
Any of 2 shapes
- Match all
1 child attribute
allobject[]requiredEvery rule or nested group must match.
at least 1 item · at most 20 items
Any of 10 shapes
- Attribute
5 child attributes
type"attribute"requiredfieldstringrequiredattributes.<key>, e.g.attributes.plan.pattern
^attributes\.[\p{L}\p{N}\p{M}_.-]{1,64}$operatorstringrequiredHow to compare. Text comparisons (
equals,notEquals,contains,notContains,startsWith,in) ignore case and accents ("jose" matches "José").notEqualsandnotContainsalso match contacts without the value. Date operators (before,after,withinLastDays,moreThanDaysAgo) compare as dates and times in UTC: a stored ISO-8601 date is the start of that day,beforemeans earlier than the given day andafterlater than it, and a value that is not an ISO-8601 date or date and time never matches.inNextDaysis a date from today to that many days ahead.anniversaryInNextDays(0 = today) andanniversaryThisMonthignore the year, so they suit birthdays, and also read a yearless--MM-DD; a 29 February counts on 28 February in other years. Days are UTC days; these three are for attributes and dates, not contact fields.one of
equals,notEquals,contains,notContains,startsWith,exists,notExists,before,after,gt,gte,lt,lte,in,withinLastDays,moreThanDaysAgo,inNextDays,anniversaryInNextDays,anniversaryThisMonthvaluestringoptionalWhat to compare with, always as text:
"pro","3","true". Equality compares the stored value as text, so a stored 5 matches"5". An ISO date forbeforeandafter; a number forgt,gte,ltandlte; a whole number of days (1 to 3650) forwithinLastDays,moreThanDaysAgoandinNextDays, and 0 to 365 foranniversaryInNextDays. Omit forin,exists,notExistsandanniversaryThisMonth.up to 255 characters
valuesstring[]optionalFor
inonly: up to 100 values, each as text, any of which matches.at least 1 item · at most 100 items
- Contact field
5 child attributes
type"contact"requiredfieldstringrequiredThe contact column to test. Each takes these operators, and is refused with others:
email: equals, notEquals, contains, startsWith, exists, notExists, in;firstName: equals, notEquals, contains, startsWith, exists, notExists, in;lastName: equals, notEquals, contains, startsWith, exists, notExists, in;phone: equals, notEquals, contains, startsWith, exists, notExists, in;timezone: equals, notEquals, contains, startsWith, exists, notExists, in;externalId: equals, notEquals, contains, startsWith, exists, notExists, in;consent.email: equals;consent.push: equals;country: equals, notEquals, in, exists, notExists;locale: equals, notEquals, startsWith, in, exists, notExists;rfmGroup: equals, notEquals, in, exists, notExists;rfmRecency: equals, notEquals, in, exists, notExists, gt, gte, lt, lte;rfmFrequency: equals, notEquals, in, exists, notExists, gt, gte, lt, lte;rfmMonetary: equals, notEquals, in, exists, notExists, gt, gte, lt, lte;purchaseCount: equals, gt, gte, lt, lte;totalSpent: equals, gt, gte, lt, lte;lastPurchasedAt: before, after, withinLastDays, moreThanDaysAgo, exists, notExists;createdAt: before, after, withinLastDays, moreThanDaysAgo.phoneholds +country numbers: writeequals,notEqualsandinvalues with their country code. Consent takes"true"or"false". Purchases:purchaseCount,totalSpent(in the main currency) andlastPurchasedAtare a customer's totals of the workspace's purchase event (purchaseEventin workspace settings: an order, a paid invoice, a booking), recomputed daily; the contact's storedtotalSpentis null when the workspace's purchases carry no amount. In comparisons onpurchaseCountandtotalSpenta missing count or spend is 0, so someone who never purchased has 0 purchases and 0 spent. RFM:rfmGroupand its scoresrfmRecency,rfmFrequencyandrfmMonetary(monetary value: how much they spend) come from the same daily pass. A score is a whole number from 1 to 5, written as text ("5";valuesof them forin), 5 being the most recent, most frequent or highest spending; any other value is refused with 400. Someone who never purchased has norfmGroupor scores: on those four fields they match onlynotExistsandnotEquals. SorfmRecencynotEquals5 includes everyone who never purchased; add anexistsrule on the same field to leave them out (bought, but not recently).one of
email,firstName,lastName,phone,timezone,externalId,consent.email,consent.push,country,locale,rfmGroup,rfmRecency,rfmFrequency,rfmMonetary,purchaseCount,totalSpent,lastPurchasedAt,createdAtoperatorstringrequiredHow to compare. Text comparisons (
equals,notEquals,contains,notContains,startsWith,in) ignore case and accents ("jose" matches "José").notEqualsandnotContainsalso match contacts without the value. Date operators (before,after,withinLastDays,moreThanDaysAgo) compare as dates and times in UTC: a stored ISO-8601 date is the start of that day,beforemeans earlier than the given day andafterlater than it, and a value that is not an ISO-8601 date or date and time never matches.inNextDaysis a date from today to that many days ahead.anniversaryInNextDays(0 = today) andanniversaryThisMonthignore the year, so they suit birthdays, and also read a yearless--MM-DD; a 29 February counts on 28 February in other years. Days are UTC days; these three are for attributes and dates, not contact fields.one of
equals,notEquals,contains,notContains,startsWith,exists,notExists,before,after,gt,gte,lt,lte,in,withinLastDays,moreThanDaysAgo,inNextDays,anniversaryInNextDays,anniversaryThisMonthvaluestringoptionalWhat to compare with, always as text:
"pro","3","true". Equality compares the stored value as text, so a stored 5 matches"5". An ISO date forbeforeandafter; a number forgt,gte,ltandlte; a whole number of days (1 to 3650) forwithinLastDays,moreThanDaysAgoandinNextDays, and 0 to 365 foranniversaryInNextDays. Omit forin,exists,notExistsandanniversaryThisMonth.up to 255 characters
valuesstring[]optionalFor
inonly: up to 100 values, each as text, any of which matches.at least 1 item · at most 100 items
- Event
6 child attributes
type"event"requirednamestringrequiredThe event name.
1–128 characters
withinDaysintegeroptionalOnly events from the last
withinDaysdays.min > 0 · max 3650
notFollowedBystringoptionalOnly when this event name did not follow it afterwards.
1–128 characters
countobjectoptionalHow often; defaults to at least once.
{ operator: equals, value: 0 }is "has not done".whereobject[]optionalProperty conditions the event must meet, up to 5;
countcounts only events meeting all of them.at least 1 item · at most 5 items
- Message activity
9 child attributes
type"activity"requiredactivitystringrequirednotOpenedandnotClicked: sent, and not opened or clicked. Opens include machine opens (seeopenedByMachine).one of
sent,notSent,opened,clicked,notOpened,notClicked,bouncedcountobjectoptionalHow many messages had this activity; defaults to at least one. Not with
notSent.linkstringoptionalWith
clickedornotClicked: only clicks on a link whose address contains this text, ignoring case.1–500 characters
channelstringoptionalOnly messages on this channel (opened a push, not an email); any when left out.
one of
email,pushcampaignIdstringoptionalOnly messages of this campaign.
uuid
tagIdstringoptionalOnly messages of campaigns or journeys carrying this tag, as they are tagged when the rule is evaluated.
uuid
journeyStepIdstringoptionalInside a journey only: the message sent by the step with this id in the contact's current enrollment. Matches nobody outside a journey.
pattern
^[a-z0-9][a-z0-9_-]{0,63}$withinDaysintegeroptionalOnly activity from the last
withinDaysdays. FornotOpenedandnotClickedit is a waiting time instead: only messages sent at least this many days ago count, so each has had that long to be opened or clicked.min > 0 · max 3650
- Push installation
4 child attributes
type"installation"requiredhasstringrequiredactive: reachable by push (granted, active, not invalidated);none: not.one of
active,nonepushProjectIdstringoptionalOnly devices of this push project; any when omitted.
uuid
platformstringoptionalOnly devices on this platform; any when omitted.
one of
ios,android,web
- List membership
3 child attributes
type"list"requiredlistIdsstring[]requiredOne list or several: with
member: truein any of them, withfalsein none of them. Each must be one of the workspace's lists when the rules are saved.at least 1 item · at most 50 items
memberbooleanrequiredTrue for in the lists, false for not in them.
- Segment membership
3 child attributes
type"segment"requiredsegmentIdstringrequireduuid
memberbooleanrequiredTrue for in the segment, false for not in it.
- Topic
4 child attributes
type"topic"requiredtopicIdstringrequiredOne of the workspace's topics when the rules are saved.
uuid
channelstringoptionalReads the contact's choice for this channel, which overrides their topic-wide choice. Without it, the rule reads the topic-wide choice, which a contact's
subscribedshows.one of
email,pushsubscribedbooleanrequiredTrue for contacts the topic reaches, false for those it does not.
- Match all
1 child attribute
allobject[]requiredEvery rule must match.
at least 1 item · at most 20 items
- Match any
1 child attribute
anyobject[]requiredAt least one rule must match.
at least 1 item · at most 20 items
- Match any
1 child attribute
anyobject[]requiredAt least one rule or nested group must match.
at least 1 item · at most 20 items
Any of 10 shapes
- Attribute
5 child attributes
type"attribute"requiredfieldstringrequiredattributes.<key>, e.g.attributes.plan.pattern
^attributes\.[\p{L}\p{N}\p{M}_.-]{1,64}$operatorstringrequiredHow to compare. Text comparisons (
equals,notEquals,contains,notContains,startsWith,in) ignore case and accents ("jose" matches "José").notEqualsandnotContainsalso match contacts without the value. Date operators (before,after,withinLastDays,moreThanDaysAgo) compare as dates and times in UTC: a stored ISO-8601 date is the start of that day,beforemeans earlier than the given day andafterlater than it, and a value that is not an ISO-8601 date or date and time never matches.inNextDaysis a date from today to that many days ahead.anniversaryInNextDays(0 = today) andanniversaryThisMonthignore the year, so they suit birthdays, and also read a yearless--MM-DD; a 29 February counts on 28 February in other years. Days are UTC days; these three are for attributes and dates, not contact fields.one of
equals,notEquals,contains,notContains,startsWith,exists,notExists,before,after,gt,gte,lt,lte,in,withinLastDays,moreThanDaysAgo,inNextDays,anniversaryInNextDays,anniversaryThisMonthvaluestringoptionalWhat to compare with, always as text:
"pro","3","true". Equality compares the stored value as text, so a stored 5 matches"5". An ISO date forbeforeandafter; a number forgt,gte,ltandlte; a whole number of days (1 to 3650) forwithinLastDays,moreThanDaysAgoandinNextDays, and 0 to 365 foranniversaryInNextDays. Omit forin,exists,notExistsandanniversaryThisMonth.up to 255 characters
valuesstring[]optionalFor
inonly: up to 100 values, each as text, any of which matches.at least 1 item · at most 100 items
- Contact field
5 child attributes
type"contact"requiredfieldstringrequiredThe contact column to test. Each takes these operators, and is refused with others:
email: equals, notEquals, contains, startsWith, exists, notExists, in;firstName: equals, notEquals, contains, startsWith, exists, notExists, in;lastName: equals, notEquals, contains, startsWith, exists, notExists, in;phone: equals, notEquals, contains, startsWith, exists, notExists, in;timezone: equals, notEquals, contains, startsWith, exists, notExists, in;externalId: equals, notEquals, contains, startsWith, exists, notExists, in;consent.email: equals;consent.push: equals;country: equals, notEquals, in, exists, notExists;locale: equals, notEquals, startsWith, in, exists, notExists;rfmGroup: equals, notEquals, in, exists, notExists;rfmRecency: equals, notEquals, in, exists, notExists, gt, gte, lt, lte;rfmFrequency: equals, notEquals, in, exists, notExists, gt, gte, lt, lte;rfmMonetary: equals, notEquals, in, exists, notExists, gt, gte, lt, lte;purchaseCount: equals, gt, gte, lt, lte;totalSpent: equals, gt, gte, lt, lte;lastPurchasedAt: before, after, withinLastDays, moreThanDaysAgo, exists, notExists;createdAt: before, after, withinLastDays, moreThanDaysAgo.phoneholds +country numbers: writeequals,notEqualsandinvalues with their country code. Consent takes"true"or"false". Purchases:purchaseCount,totalSpent(in the main currency) andlastPurchasedAtare a customer's totals of the workspace's purchase event (purchaseEventin workspace settings: an order, a paid invoice, a booking), recomputed daily; the contact's storedtotalSpentis null when the workspace's purchases carry no amount. In comparisons onpurchaseCountandtotalSpenta missing count or spend is 0, so someone who never purchased has 0 purchases and 0 spent. RFM:rfmGroupand its scoresrfmRecency,rfmFrequencyandrfmMonetary(monetary value: how much they spend) come from the same daily pass. A score is a whole number from 1 to 5, written as text ("5";valuesof them forin), 5 being the most recent, most frequent or highest spending; any other value is refused with 400. Someone who never purchased has norfmGroupor scores: on those four fields they match onlynotExistsandnotEquals. SorfmRecencynotEquals5 includes everyone who never purchased; add anexistsrule on the same field to leave them out (bought, but not recently).one of
email,firstName,lastName,phone,timezone,externalId,consent.email,consent.push,country,locale,rfmGroup,rfmRecency,rfmFrequency,rfmMonetary,purchaseCount,totalSpent,lastPurchasedAt,createdAtoperatorstringrequiredHow to compare. Text comparisons (
equals,notEquals,contains,notContains,startsWith,in) ignore case and accents ("jose" matches "José").notEqualsandnotContainsalso match contacts without the value. Date operators (before,after,withinLastDays,moreThanDaysAgo) compare as dates and times in UTC: a stored ISO-8601 date is the start of that day,beforemeans earlier than the given day andafterlater than it, and a value that is not an ISO-8601 date or date and time never matches.inNextDaysis a date from today to that many days ahead.anniversaryInNextDays(0 = today) andanniversaryThisMonthignore the year, so they suit birthdays, and also read a yearless--MM-DD; a 29 February counts on 28 February in other years. Days are UTC days; these three are for attributes and dates, not contact fields.one of
equals,notEquals,contains,notContains,startsWith,exists,notExists,before,after,gt,gte,lt,lte,in,withinLastDays,moreThanDaysAgo,inNextDays,anniversaryInNextDays,anniversaryThisMonthvaluestringoptionalWhat to compare with, always as text:
"pro","3","true". Equality compares the stored value as text, so a stored 5 matches"5". An ISO date forbeforeandafter; a number forgt,gte,ltandlte; a whole number of days (1 to 3650) forwithinLastDays,moreThanDaysAgoandinNextDays, and 0 to 365 foranniversaryInNextDays. Omit forin,exists,notExistsandanniversaryThisMonth.up to 255 characters
valuesstring[]optionalFor
inonly: up to 100 values, each as text, any of which matches.at least 1 item · at most 100 items
- Event
6 child attributes
type"event"requirednamestringrequiredThe event name.
1–128 characters
withinDaysintegeroptionalOnly events from the last
withinDaysdays.min > 0 · max 3650
notFollowedBystringoptionalOnly when this event name did not follow it afterwards.
1–128 characters
countobjectoptionalHow often; defaults to at least once.
{ operator: equals, value: 0 }is "has not done".whereobject[]optionalProperty conditions the event must meet, up to 5;
countcounts only events meeting all of them.at least 1 item · at most 5 items
- Message activity
9 child attributes
type"activity"requiredactivitystringrequirednotOpenedandnotClicked: sent, and not opened or clicked. Opens include machine opens (seeopenedByMachine).one of
sent,notSent,opened,clicked,notOpened,notClicked,bouncedcountobjectoptionalHow many messages had this activity; defaults to at least one. Not with
notSent.linkstringoptionalWith
clickedornotClicked: only clicks on a link whose address contains this text, ignoring case.1–500 characters
channelstringoptionalOnly messages on this channel (opened a push, not an email); any when left out.
one of
email,pushcampaignIdstringoptionalOnly messages of this campaign.
uuid
tagIdstringoptionalOnly messages of campaigns or journeys carrying this tag, as they are tagged when the rule is evaluated.
uuid
journeyStepIdstringoptionalInside a journey only: the message sent by the step with this id in the contact's current enrollment. Matches nobody outside a journey.
pattern
^[a-z0-9][a-z0-9_-]{0,63}$withinDaysintegeroptionalOnly activity from the last
withinDaysdays. FornotOpenedandnotClickedit is a waiting time instead: only messages sent at least this many days ago count, so each has had that long to be opened or clicked.min > 0 · max 3650
- Push installation
4 child attributes
type"installation"requiredhasstringrequiredactive: reachable by push (granted, active, not invalidated);none: not.one of
active,nonepushProjectIdstringoptionalOnly devices of this push project; any when omitted.
uuid
platformstringoptionalOnly devices on this platform; any when omitted.
one of
ios,android,web
- List membership
3 child attributes
type"list"requiredlistIdsstring[]requiredOne list or several: with
member: truein any of them, withfalsein none of them. Each must be one of the workspace's lists when the rules are saved.at least 1 item · at most 50 items
memberbooleanrequiredTrue for in the lists, false for not in them.
- Segment membership
3 child attributes
type"segment"requiredsegmentIdstringrequireduuid
memberbooleanrequiredTrue for in the segment, false for not in it.
- Topic
4 child attributes
type"topic"requiredtopicIdstringrequiredOne of the workspace's topics when the rules are saved.
uuid
channelstringoptionalReads the contact's choice for this channel, which overrides their topic-wide choice. Without it, the rule reads the topic-wide choice, which a contact's
subscribedshows.one of
email,pushsubscribedbooleanrequiredTrue for contacts the topic reaches, false for those it does not.
- Match all
1 child attribute
allobject[]requiredEvery rule must match.
at least 1 item · at most 20 items
- Match any
1 child attribute
anyobject[]requiredAt least one rule must match.
at least 1 item · at most 20 items
exclusionsobject | nullrequiredLists and segments left out of its audience ("don't send to");
nullfor none.2 child attributes
listIdsstring[]requiredLists whose members are left out.
at most 20 items · default
[]segmentIdsstring[]requiredSegments whose members are left out, by the segment's rules at send.
at most 10 items · default
[]
topicIdstring | nullrequiredSends only to contacts the topic reaches;
nullsends to everyone with consent.uuid
messageLimitsMessageLimitUserequired2 child attributes
applybooleanrequiredWhether its messages count toward the workspace's message limits and are held by them.
retryForDurationoptionalHow long a message over a limit keeps being retried before it is skipped with reason
message_limit: whole hours, at most 48. Unset, it is skipped at once. Absent whenapplyis false.2 child attributes
valueintegerrequiredmin > 0
unitstringrequiredone of
seconds,minutes,hours,days
utmobject | nullrequiredUTM parameters added to the links in the email.
6 child attributes
enabledbooleanrequiredsourcestringoptionalpattern
^[\p{L}\p{N}\p{M} ._\-+/]+$· 1–100 charactersmediumstringoptionalpattern
^[\p{L}\p{N}\p{M} ._\-+/]+$· 1–100 characterscampaignstringoptionalpattern
^[\p{L}\p{N}\p{M} ._\-+/]+$· 1–100 characterscontentstringoptionalpattern
^[\p{L}\p{N}\p{M} ._\-+/]+$· 1–100 characterstermstringoptionalpattern
^[\p{L}\p{N}\p{M} ._\-+/]+$· 1–100 characters
contentCampaignContentrequired2 child attributes
emailobjectoptionalThe email fields and design; any may be missing on a draft.
7 child attributes
subjectstringoptionalThe subject line. Merge tags are allowed.
1–998 characters
previewTextstringoptionalShown by inboxes after the subject. Merge tags are allowed; keep it near 100 characters.
1–255 characters
htmlstringoptionalThe HTML body. Rendered from the design when one is set.
1–512,000 characters
textstringoptionalThe plain-text alternative.
1–512,000 characters
fromstringoptionalThe sender address, on a verified sending domain.
1–320 characters
replyTostringoptionalWhere replies go; the part before the @ must be ASCII.
email
templateSnapshotobjectoptionalThe email design or HTML, copied into the campaign so later template edits do not change it.
One of 2 shapes
- With schemaVersion, blocks and theme
6 child attributes
kind"design"optionalschemaVersionintegerrequiredmin > 0 · max 1000
blocksobject[]requiredThe blocks, rendered top to bottom: at most 200 across the whole tree and five levels including the root. The footer stays at the root.
at least 1 item · at most 200 items
themeEmailThemerequiredlanguagestringoptionalThe language the email is written in, as a BCP 47 tag ("en", "fr-BE", "ar"). It sets the HTML
langand the reading direction: Arabic, Hebrew, Persian, Urdu and other right-to-left languages render right to left. Defaults to "en".plainTextOverridestring | nulloptionalPlain text sent instead of the version generated from the design or HTML.
up to 512,000 characters
- With kind and html
3 child attributes
kind"html"requiredhtmlstringrequiredup to 512,000 characters
plainTextOverridestring | nulloptionalPlain text sent instead of the version generated from the design or HTML.
up to 512,000 characters
pushobjectoptionalThe notification content.
14 child attributes
titlestringoptionalThe notification title (up to 100 characters).
1–100 characters
bodystringoptionalThe notification text (up to 500 characters).
1–500 characters
actionUrlstringoptionalThe HTTPS page opened when the notification is tapped.
uri · up to 2,048 characters
imageUrlstringoptionalA large picture: web and Android show it; iOS needs the app's service extension.
uri · up to 2,048 characters
iconUrlstringoptionalWeb only; iOS and Android show the app's own notification icon.
uri · up to 2,048 characters
datamap<string, string>optionalUp to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
at most 10 keys · keys: pattern
^[A-Za-z0-9_.-]{1,64}$· values: up to 1,024 charactersbuttonsobject[]optionalUp to 2 action buttons.
at least 1 item · at most 2 items
2 child attributes
titlestringrequiredThe button label.
1–30 characters
urlstringrequiredThe HTTPS page the button opens.
uri · up to 2,048 characters
collapseKeystringoptionalA newer notification with the same key replaces the older one on the device.
pattern
^[A-Za-z0-9_-]{1,64}$soundstringoptionalThe name of a sound the app ships; the device's default if unset.
1–64 characters
badgeintegeroptionalThe number shown on the iOS app icon.
min 0 · max 99999
ttlDurationOrZerooptionalHow long a provider keeps trying an offline device, up to 28 days; zero delivers now or never. Defaults to one day.
2 child attributes
valueintegerrequiredmin 0
unitstringrequiredone of
seconds,minutes,hours,days
urgencystringoptionalhighasks the push service to deliver promptly, even to a sleeping device;normallets it wait for a good moment to save battery. Device settings and the app still decide whether and how it is shown. Unset, a transactional push ishighand any othernormal.one of
high,normaloverridesobjectoptionalFields that differ on one platform, each replacing the shared one there (a whole
datamap, not merged key by key). On iOS and AndroidactionUrlmay be an app link such asmyapp://orders; the shared one and the web one must be HTTPS.3 child attributes
iosobjectoptionalReplaces shared fields on iOS.
14 child attributes
titlestringoptionalThe notification title (up to 100 characters).
1–100 characters
bodystringoptionalThe notification text (up to 500 characters).
1–500 characters
actionUrlstringoptionalpattern
^[a-z][a-z0-9+.-]*:\S+$· up to 2,048 charactersimageUrlstringoptionalA large picture: web and Android show it; iOS needs the app's service extension.
uri · up to 2,048 characters
iconUrlstringoptionalWeb only; iOS and Android show the app's own notification icon.
uri · up to 2,048 characters
datamap<string, string>optionalUp to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
at most 10 keys · keys: pattern
^[A-Za-z0-9_.-]{1,64}$· values: up to 1,024 charactersbuttonsobject[]optionalUp to 2 action buttons.
at least 1 item · at most 2 items
collapseKeystringoptionalA newer notification with the same key replaces the older one on the device.
pattern
^[A-Za-z0-9_-]{1,64}$soundstringoptionalThe name of a sound the app ships; the device's default if unset.
1–64 characters
badgeintegeroptionalThe number shown on the iOS app icon.
min 0 · max 99999
ttlDurationOrZerooptionalHow long a provider keeps trying an offline device, up to 28 days; zero delivers now or never. Defaults to one day.
urgencystringoptionalhighasks the push service to deliver promptly, even to a sleeping device;normallets it wait for a good moment to save battery. Device settings and the app still decide whether and how it is shown. Unset, a transactional push ishighand any othernormal.one of
high,normalinterruptionLevelstringoptionalHow iOS presents it:
passivearrives silently,time-sensitivecan break through Focus when the user allows it, and needs the app's Time Sensitive Notifications capability.one of
passive,active,time-sensitivethreadIdstringoptionalNotifications with the same thread are grouped together in Notification Center.
pattern
^[A-Za-z0-9_.-]{1,64}$
androidobjectoptionalReplaces shared fields on Android.
13 child attributes
titlestringoptionalThe notification title (up to 100 characters).
1–100 characters
bodystringoptionalThe notification text (up to 500 characters).
1–500 characters
actionUrlstringoptionalpattern
^[a-z][a-z0-9+.-]*:\S+$· up to 2,048 charactersimageUrlstringoptionalA large picture: web and Android show it; iOS needs the app's service extension.
uri · up to 2,048 characters
iconUrlstringoptionalWeb only; iOS and Android show the app's own notification icon.
uri · up to 2,048 characters
datamap<string, string>optionalUp to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
at most 10 keys · keys: pattern
^[A-Za-z0-9_.-]{1,64}$· values: up to 1,024 charactersbuttonsobject[]optionalUp to 2 action buttons.
at least 1 item · at most 2 items
collapseKeystringoptionalA newer notification with the same key replaces the older one on the device.
pattern
^[A-Za-z0-9_-]{1,64}$soundstringoptionalThe name of a sound the app ships; the device's default if unset.
1–64 characters
badgeintegeroptionalThe number shown on the iOS app icon.
min 0 · max 99999
ttlDurationOrZerooptionalHow long a provider keeps trying an offline device, up to 28 days; zero delivers now or never. Defaults to one day.
urgencystringoptionalhighasks the push service to deliver promptly, even to a sleeping device;normallets it wait for a good moment to save battery. Device settings and the app still decide whether and how it is shown. Unset, a transactional push ishighand any othernormal.one of
high,normalchannelIdstringoptionalThe Android notification channel to post in, which the app creates; it sets the sound and importance a person can tune, e.g. one for offers and one for orders.
pattern
^[A-Za-z0-9_.-]{1,64}$
webobjectoptionalReplaces shared fields on web.
12 child attributes
titlestringoptionalThe notification title (up to 100 characters).
1–100 characters
bodystringoptionalThe notification text (up to 500 characters).
1–500 characters
actionUrlstringoptionalThe HTTPS page opened when the notification is tapped.
uri · up to 2,048 characters
imageUrlstringoptionalA large picture: web and Android show it; iOS needs the app's service extension.
uri · up to 2,048 characters
iconUrlstringoptionalWeb only; iOS and Android show the app's own notification icon.
uri · up to 2,048 characters
datamap<string, string>optionalUp to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
at most 10 keys · keys: pattern
^[A-Za-z0-9_.-]{1,64}$· values: up to 1,024 charactersbuttonsobject[]optionalUp to 2 action buttons.
at least 1 item · at most 2 items
collapseKeystringoptionalA newer notification with the same key replaces the older one on the device.
pattern
^[A-Za-z0-9_-]{1,64}$soundstringoptionalThe name of a sound the app ships; the device's default if unset.
1–64 characters
badgeintegeroptionalThe number shown on the iOS app icon.
min 0 · max 99999
ttlDurationOrZerooptionalHow long a provider keeps trying an offline device, up to 28 days; zero delivers now or never. Defaults to one day.
urgencystringoptionalhighasks the push service to deliver promptly, even to a sleeping device;normallets it wait for a good moment to save battery. Device settings and the app still decide whether and how it is shown. Unset, a transactional push ishighand any othernormal.one of
high,normal
pushProjectIdstringoptionalThe push project it goes out through. Needed to send.
uuid
abTestobject | nullrequiredIts A/B test, if it has one;
nullsends one version to everyone.8 child attributes
versionsobject[]requiredVersions 2 onward; version 1 is the campaign's own content.
2 child attributes
emailobjectoptionalThis version's email fields and design; any left out are version 1's.
7 child attributes
subjectstringoptionalThe subject line. Merge tags are allowed.
1–998 characters
previewTextstringoptionalShown by inboxes after the subject. Merge tags are allowed; keep it near 100 characters.
1–255 characters
htmlstringoptionalThe HTML body. Rendered from the design when one is set.
1–512,000 characters
textstringoptionalThe plain-text alternative.
1–512,000 characters
fromstringoptionalThe sender address, on a verified sending domain.
1–320 characters
replyTostringoptionalWhere replies go; the part before the @ must be ASCII.
email
templateSnapshotobjectoptionalThe email design or HTML, copied into the campaign so later template edits do not change it.
One of 2 shapes
- With schemaVersion, blocks and theme
- With kind and html
pushobjectoptionalThis version's push title, body and image; any left out are version 1's.
3 child attributes
titlestringoptionalThe notification title (up to 100 characters).
1–100 characters
bodystringoptionalThe notification text (up to 500 characters).
1–500 characters
imageUrlstringoptionalA large picture: web and Android show it; iOS needs the app's service extension.
uri · up to 2,048 characters
testPercentintegerrequiredThe share of the audience, in percent, the versions go to, split evenly between them. The rest get the winner.
min 10 · max 50
winnerBystringrequiredHow the winner is picked: the version with the highest click or open rate over its delivered messages (as the campaign's own rates; over sent until any version has a delivery), or the highest share of its sent messages that reached the campaign's conversion goal (
conversionGoalId). Opens overcount: machine opens (Apple Mail Privacy Protection, Gmail's prefetch, mail scanners) count unread email as opened. A push test is decided by clicks (a tap the app reports with a link to follow, so the push needs anactionUrl) or conversions, not opens. A tie goes to the lower version.one of
clicks,opens,conversionsconversionGoalIdstring | nullrequiredThe conversion goal that decides, when
winnerByisconversions.uuid
decideAfterobject | nullrequiredHow long after the test went out to pick the winner and send it to the rest, from 1 to 72 hours;
nullto pick it by hand.2 child attributes
valueintegerrequiredmin > 0
unitstringrequiredone of
seconds,minutes,hours,days
winnerinteger | nullrequiredThe winning version, once picked.
min 1
testSentAtstring | nullrequiredWhen every test message was queued.
date-time
decidedAtstring | nullrequiredWhen the winner was picked.
date-time
statusstringrequiredWhere the campaign is in its lifecycle.
testing: an A/B test went out and the rest of the audience waits for the winner.sent: every message was created and queued; it says nothing about delivery, whichGET /v1/campaigns/{id}/metricsreports (some may still be queued, skipped or failed).one of
draft,scheduled,sending,testing,sent,cancelledscheduleobject | nullrequiredWhen it sends:
fixed, everyone at one instant, orlocal_time, each contact at a time on their own clock. Set while it isscheduled; a local-time schedule stays set once sending starts, since each contact's time still decides when theirs goes.nullotherwise.One of 2 shapes
- Fixed time
2 child attributes
type"fixed"requiredsendAtstringrequiredThe instant everyone receives it at.
date-time
- Local time
5 child attributes
type"local_time"requiredlocalDateTimestringrequiredThe wall-clock time (
YYYY-MM-DDTHH:MM, no timezone) each contact receives it at on their own clock. It may already have passed in some timezones, not in all.pattern
^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$timezoneIfUnknownstring | nullrequiredThe IANA timezone for contacts without one;
null: the workspace's.ifTimeHasPassedstringrequiredWhere the time has already gone when it is scheduled:
send_nowsends there at once,next_dayat the same time the next day, andskipleaves those contacts out (skipped withlocal_time_passed).one of
send_now,skip,next_daystartsAtstringrequiredWhen sending starts: the earliest timezone reaching
localDateTime.date-time
sentAtstring | nullrequiredWhen sending started; set from
sendingon (testing, sent, cancelled),nullbefore. A local-time campaign starts in the earliest timezone.date-time
deliveryCampaignDeliveryrequiredHow fast it goes out, and until when; frozen with its content at send.
2 child attributes
rateLimitobject | nullrequiredHow fast it goes out;
nullas fast as the plan allows. The plan's own send rate still applies, so a campaign can only slow a send down.2 child attributes
recipientsintegerrequiredAt most this many recipients per
per, spread evenly across it. Counts people, so one person's email and push go together.min 1 · max 1000000
perstringrequiredThe period the limit counts over.
one of
minute,hour,day
expiresAfterobject | nullrequiredFrom when sending starts, from 1 to 720 hours: recipients not yet sent when it passes are skipped with reason
expired. A rate-limited send always expires, after 720 hours unless set. With an A/B test it must outlastabTest.decideAfter.null: no expiry.2 child attributes
valueintegerrequiredmin > 0
unitstringrequiredone of
seconds,minutes,hours,days
pendingAgentReviewbooleanrequiredAn agent key authored or edited it and it is still a draft, so it waits in the agent review queue: sending needs a person's approval.
sendApprovalRequestobject | nullrequiredThe latest request, from a member in the dashboard, that an admin send it while the workspace keeps sending to admins; kept after it sends, cleared by any edit before then.
3 child attributes
userIdstringrequiredThe member who asked.
uuid
requestedAtstringrequireddate-time
notestring | nullrequired
archivedAtstring | nullrequiredWhen it was archived; archived campaigns are left out of the list by default.
date-time
tagsstring[]requiredTag names, sorted without case.
revisionintegerrequiredRaised by every edit. Send it back as
expectedRevisionon the update so a change made since you read it is refused instead of overwritten.min > 0 · max 2147483647
createdAtstringrequireddate-time
updatedAtstringrequireddate-time
Headers on 202 Accepted
Idempotency-Replaystringoptionaltruewhen this is the stored response to an earlier request with the same key.one of
true