Campaigns API reference
Manage your campaign copy, audience targeting, goals, holdback settings, and A/B tests in the project draft without changing the live website.
Use campaigns to change content on existing website pages for selected audiences. This API flattens a campaign's variants and actions into a changes array.
Each audience needs at least one segment. Original page is the unchanged website, not an audience you can write changes to. For the editor workflow, see personalize page content and personalization audiences.
Authentication and campaign operations
Paths below follow https://api.rightmessage.com/v1/{teamPid}. GET requires read or write; mutations require write. The * ability satisfies both. The shared request conventions cover bearer authentication, headers, project access, and rate limits.
| Method and path | Request | Success |
|---|---|---|
GET /campaigns | No filters or pagination parameters | 200, {"data":[RESOURCE]} |
GET /campaigns/{campaign} | Resource ID from the list | 200, {"data":RESOURCE} |
POST /campaigns | Create body below | 201, {"data":RESOURCE} |
PUT or PATCH /campaigns/{campaign} | Partial body below | 200, {"data":RESOURCE} |
DELETE /campaigns/{campaign} | No body | 204, empty body |
POST /campaigns/{campaign}/audiences/{audience}/test/start | No body | 200, {"campaign":CAMPAIGN,"requires_publish":true} |
POST /campaigns/{campaign}/audiences/{audience}/test/restart | No body | 200, same shape |
POST /campaigns/{campaign}/audiences/{audience}/test/end | {"keep_version":"A"|"B"|"C"|"D","deploy":false} | 200, same shape |
POST /campaigns/{campaign}/original/undeploy | No body | 200, same shape |
PUT and PATCH have the same partial-update behavior. An omitted top-level field retains its value. A collection with no resources returns {"data":[]}. Reads return the draft when one exists, otherwise the stable configuration.
Errors and draft effects
The shared error contract applies. Invalid request fields return 422 with message and field-keyed errors. Full-configuration validation can return 500 instead.
Mutations save a draft, not live changes. Publishing applies the whole project draft, including edits made elsewhere. The draft contract covers snapshots, dashboard updates, and save failures.
Campaign fields
| Field | Type and validation | Create default |
|---|---|---|
name | Required on create, optional on update. String, maximum 255. | Required |
is_active | Boolean | true |
base_url | Nullable string, maximum 500. No URL-format validation. | null |
testing | Object. The campaign holdback, separate from audience A/B tests. | Enabled with 10 percent holdback when the campaign has audiences; off without audiences |
testing.is_enabled | Boolean | true with changes, false without |
testing.withhold | Integer, 1 through 100 | 10 |
goal_ids | Array of strings. Starting an A/B test needs at least one. | [] |
outcome | any_goal or primary_goal | any_goal |
changes | Array of change objects | [] |
audience_tests | Update only. Array of {variant_id, test} A/B test setups, described below. Omission preserves tests. | None |
A supplied goal_ids list replaces the list. With primary_goal, the first attached goal is primary. A supplied testing object replaces the stored object, except that omitted withhold retains its previous value. Configuration repair fills an omitted is_enabled with true, even if testing was previously disabled. Send both testing fields when changing testing settings.
Change objects
Every field below belongs in each changes[] item. New changes need a selector or Smart Match target and at least one valid segment ID, except challenger rows for the original page test (variant_id: "original"), which carry no segment targeting. On an existing change, omitted fields retain their stored values.
| Field | Type and meaning |
|---|---|
id | String identifying the stored action. Omit for a new action. |
variant_id | String identifying its stored variant. Use with id to disambiguate repeated legacy action IDs. "original" is reserved for the original page test's challenger rows. |
version_id | A, B, C, or D. The A/B test version the row belongs to. Omit for the audience's own changes (version A). Original page rows must name B–D. |
kind | override, with action_id, for a challenger row that alters one of version A's actions: skip: true drops it, or fields: [{path: ["text"], op: "set", value: "New copy"}] (op is set or remove) changes it. Original page rows can't use it. |
selector | CSS-selector string. Required on create unless targeting is supplied. On update, required unless id or targeting is supplied. |
targeting | Object with mode of selector or fingerprint; optional selector, fallbackSelector, and stored fingerprint. Preserve returned Smart Match fingerprints rather than constructing a partial one. |
segment_ids | Comma-separated segment ID string, not an array. Every named segment must exist in this project's current configuration. Required and non-empty for a new change. Omission on an existing change preserves its audience targeting; empty or null targeting is rejected. |
page | Comma-separated domain/path strings such as example.com/pricing, example.com/demo. Do not include a URL scheme. A bare domain uses * as its path; a leading slash uses any domain. |
content | String. Plain content replaces text. If every nonblank line has /find/replace/ form, the service creates find-and-replace entries. |
visibility | show or hide |
style | Object of style modifications |
attr | Object of attribute modifications |
add_classes, remove_classes | Class-name strings, separated by commas or spaces; a leading . is ignored |
New changes with the same ordered segment-ID list share a variant. Use the same segment ordering throughout a create request; differently ordered lists can create separate variants even when they name the same audience. Audience rules for new variants require all named segments. A new change with an omitted or null page is scoped to the campaign's base_url page: the one sent in the same request, otherwise the stored one. Without a base_url, it applies to all domains and paths, and its returned page string is */*. Send * to target every page explicitly.
Smart Match fingerprint schema
targeting.mode is required when a targeting object is stored. targeting.selector and targeting.fallbackSelector are strings. A targeting.fingerprint object requires text, which requires string content. Its optional matchType is exact, contains, or startsWith, defaulting to exact.
The remaining fingerprint sections are optional objects:
| Section | Child fields |
|---|---|
visual | fontSize with numeric min and max; fontWeight with numeric min; positionY with numeric min and max; boolean isAboveFold |
element | String tagName; string-array candidateTags, default [] |
structure | Integer siblingIndex, siblingCount, and depth; string parentChain |
semantics | Nullable strings id, ariaRole, and ariaLabel; nullable object dataAttributes; string-array meaningfulClasses, default [] |
content | Integer charCount and wordCount; string textPrefix |
These declarations do not impose a maximum fingerprint size or a numeric range on visual measurements. A returned fingerprint records a specific element. Reusing its fields unchanged preserves the stored targeting representation.
Updating changes without losing content
Omitting changes leaves every stored action untouched. Supplying it defines the new action list. Stored actions omitted from that list are removed, along with variants whose actions were all omitted. Variants that already had no actions are retained because the flat representation cannot name them.
For a retained change, send its returned id and variant_id. Omitted fields retain their stored values, and unchanged returned values preserve the original representation, including existing ANY audience rules. Explicit null clears content, targeting, style, attributes, or classes; a null selector becomes an empty selector, and a null page resets targeting to all pages. A null or empty segment_ids is rejected rather than removing audience targeting. This does not make the whole changes list a patch.
A duplicate action ID, a mismatched variant ID, an ambiguous repeated legacy ID, or an unknown ID without new targeting can return 422 under errors.changes. Unknown segment references return 422 under the matching changes.N.segment_ids path. Do not retry a partial list as though it were an append operation.
Empty audience targeting returns 422 under errors.changes with Each audience must target at least one segment. Provide non-empty segment_ids; Original is the unchanged page.
A/B tests
An A/B test splits one audience, or the original page, into whole versions. A is the audience's existing changes, or for the original page the unchanged page (Control). B, C, and D are challengers. The editor workflow is in run an A/B test on a page or audience.
Setup through update
Send audience_tests: [{"variant_id": "var_…", "test": {}}] in a PUT or PATCH to create a setup with A and B at 50/50, without touching changes. Use "original" as the variant_id to test the original page; a campaign without audiences can still test it. To add C or D, send the full test.versions list of {id, name, weight}. Weights are integers from 1 to 99 totaling 100, with at most four versions, A plus at least one challenger. Challenger content goes in changes with version_id.
state and run can't change through update. Setup errors return 422 under errors.audience_tests, including Use the start, restart or end audience test operation to change its lifecycle., Create a setup first; use start-audience-test to start it., and Supply one test object per existing variant_id, or "original" for the original page. Names can change while a test runs. Versions, weights, content, and targeting change only through a restart. Deleting a tested audience while its test runs returns End the test before deleting its audience, or restart the test before changing its lifecycle.
Lifecycle endpoints
{audience} is an audience ID from the campaign, or original for the original page test. Each endpoint changes the draft only, saves a snapshot, and returns {"campaign": …, "requires_publish": true}, where campaign is the stored draft campaign. Visitors see nothing new until you publish; publishing is what starts or restarts the test for visitors.
- start starts a test in setup state with a fresh run.
- restart gives a running test a fresh run so versions, weights, content, or targeting can change. Earlier results stay in history.
- end requires
keep_version(A–D) and acceptsdeploy(boolean). For an audience, the kept version becomes its changes and the test is removed. For the original page, nothing is kept unlessdeployis true, which keeps showing that version (B–D) to everyone who sees the original page until undeploy. - original/undeploy removes that deployed winner, once your website shows the changes itself.
Starting and restarting require Pro. Ending and undeploying don't need Pro.
Refusals return 422 with message and errors.audience_test. Start and restart join every blocker: Choose a campaign goal before starting a test., Starting an A/B test needs the Pro plan., Add at least one challenger version., and Version B must differ from A. Other refusals:
This operation requires a setup test.orThis operation requires a running test.This audience has no test.orUnknown audience …Only the original page deploys a winner. Ending an audience test keeps the version as its changes.Control is the original page, so there is nothing to deploy.The original page has no deployed winner.
A missing or invalid keep_version returns 422 under errors.keep_version. An unknown campaign returns 404.
Edit one audience through MCP
The REST campaign endpoints don't expose a separate fill-or-rewrite operation. A supplied REST changes array still follows the replacement-list rules above.
If you're using an assistant connected through MCP, set-audience-changes addresses one existing campaign_id and variant_id without replacing other audiences. Righty calls the same operation set_audience_changes. Its required mode is fill or rewrite, not replace.
fillskips existing changes, including non-text changes.rewriteupdates a matching change or creates one when absent. Text-only rewrites retain other modification fields.- Each item supplies
selector, optional Smart Matchtargeting, amodificationsobject, and page rules. Unlike REST'spagestring, this tool uses a non-empty array of{domain, path, operator}objects. Outside the editor, omission uses the campaign'sbase_urlpage, and page rules are required only when it has none; inside the editor, omission uses the current page exactly.
See the linked MCP reference for complete arguments, limits, validation, and results.
Campaign response fields
The response includes id, name, is_active, base_url, testing, goal_ids, changes, audience_tests, original_deployment, and outcome when stored. Each change includes every key in the change table. Missing optional values serialize as null. The service generates cpn_ campaign IDs, variant IDs, and action IDs.
audience_testslists each test as{variant_id, test: {id, state, run?, versions: [{id, name, weight}]}}, original page first.stateissetuporrunning.original_deploymentis null, or{test_id, version_id, name, deployed_at, proven, changes}after an original page test ended with deploy.provenis true only when that version's recorded verdict was Winner;deployed_atis null on older deployments.
Deleting a campaign removes it from the draft. A campaign with a live A/B test returns 422: End the live A/B test before deleting this campaign. Publishing activates or removes the campaign on the website. The campaign holdback (testing) is separate from audience A/B tests and from the flow/question/offer split-test lifecycle.
Illustrative requests and responses
These illustrative examples use the fictional WorkForge project. The same resource schema covers every CRUD response.
The PATCH example also works with PUT. The create, item-read, and update envelopes have this shape. The list places these resource objects in its data array. An update returns the updated values. DELETE returns no JSON.