Skip to help content
RightMessage

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.

Available on the Scale and Pro plans.

Updated

View as Markdown

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 pathRequestSuccess
GET /campaignsNo filters or pagination parameters200, {"data":[RESOURCE]}
GET /campaigns/{campaign}Resource ID from the list200, {"data":RESOURCE}
POST /campaignsCreate body below201, {"data":RESOURCE}
PUT or PATCH /campaigns/{campaign}Partial body below200, {"data":RESOURCE}
DELETE /campaigns/{campaign}No body204, empty body
POST /campaigns/{campaign}/audiences/{audience}/test/startNo body200, {"campaign":CAMPAIGN,"requires_publish":true}
POST /campaigns/{campaign}/audiences/{audience}/test/restartNo body200, same shape
POST /campaigns/{campaign}/audiences/{audience}/test/end{"keep_version":"A"|"B"|"C"|"D","deploy":false}200, same shape
POST /campaigns/{campaign}/original/undeployNo body200, 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

FieldType and validationCreate default
nameRequired on create, optional on update. String, maximum 255.Required
is_activeBooleantrue
base_urlNullable string, maximum 500. No URL-format validation.null
testingObject. The campaign holdback, separate from audience A/B tests.Enabled with 10 percent holdback when the campaign has audiences; off without audiences
testing.is_enabledBooleantrue with changes, false without
testing.withholdInteger, 1 through 10010
goal_idsArray of strings. Starting an A/B test needs at least one.[]
outcomeany_goal or primary_goalany_goal
changesArray of change objects[]
audience_testsUpdate 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.

FieldType and meaning
idString identifying the stored action. Omit for a new action.
variant_idString 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_idA, 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.
kindoverride, 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.
selectorCSS-selector string. Required on create unless targeting is supplied. On update, required unless id or targeting is supplied.
targetingObject with mode of selector or fingerprint; optional selector, fallbackSelector, and stored fingerprint. Preserve returned Smart Match fingerprints rather than constructing a partial one.
segment_idsComma-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.
pageComma-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.
contentString. Plain content replaces text. If every nonblank line has /find/replace/ form, the service creates find-and-replace entries.
visibilityshow or hide
styleObject of style modifications
attrObject of attribute modifications
add_classes, remove_classesClass-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:

SectionChild fields
visualfontSize with numeric min and max; fontWeight with numeric min; positionY with numeric min and max; boolean isAboveFold
elementString tagName; string-array candidateTags, default []
structureInteger siblingIndex, siblingCount, and depth; string parentChain
semanticsNullable strings id, ariaRole, and ariaLabel; nullable object dataAttributes; string-array meaningfulClasses, default []
contentInteger 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 accepts deploy (boolean). For an audience, the kept version becomes its changes and the test is removed. For the original page, nothing is kept unless deploy is 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. or This operation requires a running test.
  • This audience has no test. or Unknown 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.

HTTP
POST /v1/123456/campaigns/cpn_example1/audiences/original/test/end HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json

{"keep_version": "B", "deploy": true}

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.

  • fill skips existing changes, including non-text changes.
  • rewrite updates a matching change or creates one when absent. Text-only rewrites retain other modification fields.
  • Each item supplies selector, optional Smart Match targeting, a modifications object, and page rules. Unlike REST's page string, this tool uses a non-empty array of {domain, path, operator} objects. Outside the editor, omission uses the campaign's base_url page, 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_tests lists each test as {variant_id, test: {id, state, run?, versions: [{id, name, weight}]}}, original page first. state is setup or running.
  • original_deployment is null, or {test_id, version_id, name, deployed_at, proven, changes} after an original page test ended with deploy. proven is true only when that version's recorded verdict was Winner; deployed_at is 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.

HTTP
GET /v1/123456/campaigns HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json

GET /v1/123456/campaigns/cpn_example1 HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json

POST /v1/123456/campaigns HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json

{
	"name": "WorkForge campaign",
	"base_url": "https://example.com/pricing"
}

PATCH /v1/123456/campaigns/cpn_example1 HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json

{
	"name": "WorkForge updated"
}

DELETE /v1/123456/campaigns/cpn_example1 HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json

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.

JSON
{
	"data": {
		"id": "cpn_example1",
		"name": "WorkForge campaign",
		"is_active": true,
		"base_url": "https://example.com/pricing",
		"testing": {
			"is_enabled": false,
			"withhold": 10
		},
		"goal_ids": [],
		"outcome": "any_goal",
		"changes": [],
		"audience_tests": [],
		"original_deployment": null
	}
}

Still need a hand?

Tell us what you’re trying to do and where you’re getting stuck.

support@rightmessage.com