Skip to content

Create a bonus campaign

POST /campaigns
Scopeprogram:write
Customer session tokenNot accepted: call from your server with an API key
Rate limit classwrite (limits)
Idempotency keyRequired (idempotency)
MCP toolcharm_create_campaign

Creates a campaign such as “double points this weekend on the Summer collection”. It starts as a draft unless status: "scheduled" (a scheduled one turns active on its own when its window opens). Emails stay off unless the request switches a phase on. Call with dry_run: true first, show the merchant the campaign, the diff and would_email, then repeat without it.

FieldTypeDescription
namestringShown to shoppers on the storefront banner and in campaign emails. No links.
templatestring: custom, black_friday, mothers_day, member_anniversary, win_back, valentines, holiday_season, flash_weekend, new_collection, vip_exclusive, reward_sale, exclusive_rewardStart from a preset (multiplier, stacking, audience). Its dates and emails are not used: send starts_at/ends_at, and emails stay off unless switched on. (optional)
typestring: earning_boost, reward_spotlightearning_boost (default) multiplies points earned; reward_spotlight puts rewards on sale or unlocks them for the window. (optional)
multipliernumberPoints multiplier while it runs, e.g. 2 for double points. Above 1 to go live; at most 10. (optional)
stacking_modestring: compound, max, replaceWith the VIP tier multiplier: multiply both (compound, default), take the higher (max), or use the campaign’s (replace, never below the tier’s). (optional)
starts_atstring or nullISO 8601 with a time zone, e.g. 2026-10-03T00:00:00+02:00. (optional, nullable)
ends_atstring or null(optional, nullable)
rule_typesarray of string: order_placed, account_created, newsletter_signup, sms_signup, birthday, referral, product_review, follow_instagram, follow_tiktok, follow_x, like_facebook, share_facebook, share_x, visit_urlThe ways to earn it boosts (default order_placed). Each must be a way to earn the shop has switched on. (optional)
product_scopeobjectWhich purchases count (order rules only): { mode: all | include | exclude, product_ids, collection_ids, tags }. Ids are numeric or GIDs and must exist. (optional)
audienceobjectWho it applies to: { mode: all | tier | tag, tier_ids, tags }. (optional)
limitsobjectCaps, null for none: { max_orders, max_bonus_points }. (optional)
badge_textstring or nullShort badge on product points, e.g. 2×. (optional, nullable)
banner_copyobjectStorefront banner wording per published language: { "cs": { "title": "…", "subtitle": "…" } }; null removes a language. (optional)
spotlight_rewardsarray of objectFor reward_spotlight: [{ reward_id, sale_points_cost }]; a sale price below the reward’s cost, or null to only unlock a disabled reward. (optional)
emailsobjectSwitch the campaign emails on or off: { teaser, launch, last_chance } booleans. Off unless switched on. An enabled phase mails the whole audience when it is due — check would_email in a dry run and confirm with the merchant first. (optional)
statusstring: draft, scheduleddraft (default) or scheduled. A scheduled campaign turns active on its own when its window opens. (optional)
dry_runbooleanPreview only: validate, resolve references and return the diff and summary, but write nothing. Costs no idempotency key and is limited as a read. Call with this first, show the merchant, then repeat without it. (optional)
if_revisionintegerThe program revision you last read (from GET /program or a previous write). When the program changed since, the write is refused with 409 revision_conflict instead of overwriting a concurrent admin edit. (optional)

Returns CampaignWriteResult.

FieldTypeDescription
dry_runbooleanTrue when nothing was written.
revisionintegerThe campaign’s revision after the write (unchanged on a dry run).
operationstring: create, update, delete, none
diffarray of FieldChange
campaignCampaign or null(nullable)
would_emailobject or null(nullable)
{
"dry_run": false,
"revision": 1,
"operation": "create",
"diff": [
{
"path": "path",
"before": null,
"after": null
}
],
"campaign": {
"id": "id",
"name": "Gold",
"type": "earning_boost",
"template": "template",
"status": "draft",
"multiplier": 1,
"stacking_mode": "stacking mode",
"starts_at": "2026-10-01T09:30:00.000Z",
"ends_at": "2026-10-01T09:30:00.000Z",
"rule_types": [
"rule types"
],
"product_scope": {
"mode": "all",
"product_ids": [
null
],
"collection_ids": [
null
],
"tags": [
null
]
},
"audience": {
"mode": "all",
"tier_ids": [
null
],
"tags": [
null
]
},
"limits": {
"max_orders": 1,
"max_bonus_points": 120
},
"consumed": {
"orders": 1,
"bonus_points": 120
},
"badge_text": "badge text",
"banner_copy": {},
"spotlight_rewards": [
{
"reward_id": null,
"sale_points_cost": null
}
],
"emails": {
"teaser": null,
"launch": null,
"last_chance": null
},
"deletable": false,
"revision": 1,
"created_at": "2026-10-01T09:30:00.000Z",
"updated_at": "2026-10-01T09:30:00.000Z"
},
"would_email": {
"phases": [
"phases"
],
"recipients": 1
}
}
Terminal window
curl -X POST "https://charm.appfleece.app/api/v1/campaigns" \
-H "Authorization: Bearer chrm_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"Gold"}'

unauthorized · invalid_token · insufficient_scope · access_required · validation_error · rate_limited · idempotency_conflict

Every error body carries code, message, request_id and a doc_url pointing at the matching entry in the error catalog.