POST
/api/public/v1/campaigns

Create a campaign

Starts a campaign as a draft — always, whatever the channel. An SMS one has a carrier registration to get through before it can send, and an email one still wants a look at the audience count first. **Nothing here reaches anybody**; sending is a separate call. The channel is fixed at creation because the audience filter, the content limits and the whole SMS registration hang off it.

Required scope

The access token must carry this scope. A token missing it gets 403 Forbidden. Grant them to an API client under Settings → Company → API, and to a teammate under Employee → Permissions.

campaigns:write

Request Body

PropertyTypeDescription
locationIdstringThe location.
namestringInternal name.
channelstringFixed once set.
subjectstringRequired for EMAIL, ignored for SMS.
bodystringThe message. SMS has a much shorter limit than email.
audienceobject`{ lifecycleStages, inactiveDays, createdWithinDays, neverContacted }` — all four keys required, the numeric two nullable.
POSTExample request
curl
curl -X POST "https://www.pxb.app/api/public/v1/campaigns" \
  -H "Authorization: Bearer your_access_token" \
  -H "Content-Type: application/json" \
  -d '{
    "locationId": "loc_abc123",
    "name": "Autumn service reminder",
    "channel": "EMAIL",
    "subject": "Time for your autumn service",
    "body": "Hi {{name}} — you are due for a service.",
    "audience": {
      "lifecycleStages": [
        "CUSTOMER"
      ],
      "inactiveDays": 180,
      "createdWithinDays": null,
      "neverContacted": false
    }
  }'
200Example response
json
{
  "message": "Campaign created successfully!",
  "content": {
    "campaign": {
      "id": "cmp_3311",
      "locationId": "loc_abc123",
      "name": "Autumn service reminder",
      "channel": "EMAIL",
      "status": "DRAFT",
      "subject": "Time for your autumn service",
      "body": "Hi {{name}} — you are due for a service.",
      "audience": {
        "lifecycleStages": [
          "CUSTOMER"
        ],
        "inactiveDays": 180,
        "createdWithinDays": null,
        "neverContacted": false
      },
      "scheduledAt": null,
      "startedAt": null,
      "sentAt": null,
      "createdAt": "2026-08-26T12:00:00.000Z",
      "updatedAt": "2026-08-26T12:00:00.000Z"
    }
  }
}

In the console

The same thing done by hand, for anyone comparing the API against the screen they already use.

xRM → Campaigns
/xrm/campaigns
  1. Open Campaigns.
  2. New campaign.