Campaign Object
Core CRUD and lifecycle operations on public campaign objects: create, list, get, update metadata, soft-delete, archive, and clone.
Base URL: https://api.experiture.ai/public/v1
v1 campaign type. Public Campaigns v1 supports campaignType: "broadcast". journey is accepted by the schema only so the API can return a stable unsupported-type response instead of pretending the campaign does not exist.
Create Campaign
Creates a new broadcast campaign draft. You can seed audience, content, and schedule intent during creation, or configure them later through the dedicated binding endpoints.
POST /campaigns
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: <uuid-v4> (optional, 24-hour deduplication)Required scope: campaigns:create
Request body
| Field | Type | Required | Description |
|---|---|---|---|
campaignType | string | Yes | Must be "broadcast" in v1. |
name | string | Yes | 1-255 characters. |
channel | string | Yes | email, sms, push, or direct_mail. |
description | string | null | No | Optional description. |
category | string | null | No | Classification hint. Invalid or omitted values default to general. |
tags | string[] | No | Up to 25 tags, max 100 chars each. Category is auto-added. |
externalId | string | null | No | Your system's identifier for this campaign. |
metadata | object | null | No | Arbitrary external-system metadata. |
content | object | null | No | Optional content seed. templateId may be omitted at create time. |
audience | object | null | No | Optional audience seed. If supplied, include must contain at least one entry. |
schedule | object | null | No | Optional schedule intent seed. If sendAt is supplied and mode is omitted, the API treats it as mode: "at". |
content object
| Field | Type | Required | Description |
|---|---|---|---|
templateId | string | null | No | ID of a published template matching the campaign channel. |
senderProfileId | string | null | No | Sender profile ID for channels that use sender profiles. |
subjectLine | string | null | No | Email subject line. Stored only as draft configuration until content is complete. |
audience object
| Field | Type | Required | Description |
|---|---|---|---|
include | array | Yes | 1-25 audience/segment entries. |
exclude | array | No | 0-25 suppression entries. |
Each audience entry has audienceId and type, where type is "audience" or "segment".
schedule object
The schedule seed is channel-dependent. For digital channels (email, sms, push):
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | No | "now" or "at". Defaults to "at" when sendAt is present, otherwise "now". |
sendAt | string | null | Conditional | ISO 8601 timestamp. Required when resolved mode is "at". |
timezone | string | null | No | IANA timezone string. Defaults to "UTC". |
For direct mail campaigns, use the batch-date model instead:
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | Yes | Must be "batch_on_date". |
batchDate | string | Yes | Target fulfillment date in YYYY-MM-DD format. |
timezone | string | null | No | IANA timezone used to interpret the date. |
The schedule object in all GET responses includes a kind discriminator ("digital_send" or "direct_mail_batch") and, for direct mail, a timezoneSource field indicating how the timezone was resolved. See Schedule & Send for full details.
Example request
curl -X POST https://api.experiture.ai/public/v1/campaigns \
-H "Authorization: Bearer <your_access_token>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"campaignType": "broadcast",
"name": "Spring Re-engagement Email",
"channel": "email",
"category": "retention",
"tags": ["spring-2026", "re-engagement"],
"externalId": "crm-campaign-8821"
}'Response - 201 Created
{
"success": true,
"data": {
"campaignId": "4130bada-9264-465f-bc0c-a26bebcfcc81",
"versionId": "11111111-1111-1111-1111-111111111111",
"campaignType": "broadcast",
"authoringStatus": "draft",
"channel": "email",
"links": {
"self": "/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81",
"summary": "/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81/summary"
}
}
}Errors
| Code | HTTP | Meaning |
|---|---|---|
JOURNEY_API.PUBLIC.CAMPAIGNS.TYPE_UNSUPPORTED | 409 | campaignType is not supported in v1. |
JOURNEY_API.PUBLIC.CAMPAIGNS.INVALID_CHANNEL | 400 | channel value is not supported. |
JOURNEY_API.PUBLIC.CAMPAIGNS.SCHEDULE.SEND_AT_REQUIRED | 400 | Schedule mode resolves to "at" but no sendAt was supplied. |
JOURNEY_API.PUBLIC.CAMPAIGNS.CONTENT.TEMPLATE_NOT_FOUND | 404 | templateId does not exist in this tenant. |
JOURNEY_API.PUBLIC.CAMPAIGNS.CONTENT.TEMPLATE_NOT_PUBLISHED | 409 | Template exists but is not published. |
JOURNEY_API.PUBLIC.CAMPAIGNS.CONTENT.CHANNEL_MISMATCH | 409 | Template channel does not match campaign channel. |
JOURNEY_API.AUTH.INSUFFICIENT_SCOPE | 403 | Token lacks campaigns:create. |
List Campaigns
Returns a paginated list of supported broadcast campaigns in the tenant.
GET /campaigns
Authorization: Bearer <token>Required scope: campaigns:read
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Results per page. Range 1-100. |
offset | integer | 0 | Number of records to skip. |
sortBy | string | updatedAt | createdAt, name, or updatedAt. |
sortDirection | string | desc | asc or desc. |
campaignType | string | broadcast | Only broadcast is supported in v1. journey returns 409 TYPE_UNSUPPORTED. |
status | string | - | Filter by authoring status, such as draft, published, or archived. |
channel | string | - | Filter by channel, such as email or direct_mail. |
tag | string | - | Filter campaigns containing this tag. |
search | string | - | Case-insensitive search on name and description. |
updatedAfter | ISO 8601 | - | Return campaigns updated after this timestamp. |
updatedBefore | ISO 8601 | - | Return campaigns updated before this timestamp. |
Response - 200 OK
{
"success": true,
"data": [
{
"campaignId": "4130bada-9264-465f-bc0c-a26bebcfcc81",
"name": "Spring Re-engagement Email",
"campaignType": "broadcast",
"channel": "email",
"authoringStatus": "draft",
"category": "retention",
"tags": ["retention", "spring-2026", "re-engagement"],
"createdAt": "2026-04-25T10:00:00.000Z",
"updatedAt": "2026-04-25T10:30:00.000Z",
"links": {
"self": "/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81",
"summary": "/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81/summary"
}
}
],
"pagination": {
"total": 42,
"limit": 50,
"offset": 0
}
}Get Campaign
Returns the full public campaign object. This uses the same public summary presenter plus object metadata such as versionId, description, externalId, and metadata.
GET /campaigns/:id
Authorization: Bearer <token>Required scope: campaigns:read
Response - 200 OK
{
"success": true,
"data": {
"campaignId": "4130bada-9264-465f-bc0c-a26bebcfcc81",
"name": "Spring Re-engagement Email",
"campaignType": "broadcast",
"channel": "email",
"authoringStatus": "draft",
"createdAt": "2026-04-25T10:00:00.000Z",
"updatedAt": "2026-04-25T10:30:00.000Z",
"schedule": {
"kind": "digital_send",
"mode": "now",
"sendAt": null,
"timezone": "UTC",
"scheduled": false
},
"audience": {
"included": [
{ "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": null, "type": "audience", "memberCount": null }
],
"excluded": [],
"reach": {
"status": "idle",
"evaluatedCount": null,
"evaluatedAt": null,
"isStale": false
}
},
"content": {
"template": {
"id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
"name": "Published API Email",
"status": "published",
"channel": "email"
},
"proofStatus": "unavailable"
},
"preflight": {
"status": "not_evaluated",
"checks": []
},
"latestExecution": {
"status": "not_scheduled",
"lastExecutionAt": null,
"counts": null
},
"versionId": "11111111-1111-1111-1111-111111111111",
"description": null,
"category": "retention",
"tags": ["retention", "spring-2026"],
"externalId": "crm-campaign-8821",
"metadata": { "source": "crm" },
"links": {
"self": "/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81",
"summary": "/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81/summary"
}
}
}Errors
| Code | HTTP | Meaning |
|---|---|---|
JOURNEY_API.PUBLIC.CAMPAIGNS.NOT_FOUND | 404 | No campaign with this ID in the token tenant. |
JOURNEY_API.PUBLIC.CAMPAIGNS.SUMMARY_TYPE_UNSUPPORTED | 409 | Campaign exists but its type does not have a public object presenter yet. |
JOURNEY_API.AUTH.INSUFFICIENT_SCOPE | 403 | Token lacks campaigns:read. |
Update Campaign Metadata
Updates campaign metadata. It does not change audience, content, or schedule configuration.
PATCH /campaigns/:id
Authorization: Bearer <token>
Content-Type: application/jsonRequired scope: campaigns:update
All request fields are optional.
| Field | Type | Description |
|---|---|---|
name | string | 1-255 characters. |
description | string | null | Description. |
category | string | null | Classification hint. |
tags | string[] | Replaces existing tags. Category is auto-added. |
externalId | string | null | External system ID. null clears it. |
metadata | object | null | Replaces external metadata. null clears it. |
Response - 200 OK - same shape as Get Campaign.
Delete Campaign
Soft-deletes a campaign. Returns no body.
DELETE /campaigns/:id
Authorization: Bearer <token>Required scope: campaigns:delete
Response - 204 No Content
Archive Campaign
Moves a campaign to archived status. The record remains visible when listing with status=archived.
POST /campaigns/:id/archive
Authorization: Bearer <token>
Content-Type: application/jsonRequired scope: campaigns:delete
Response - 200 OK - same shape as Get Campaign with authoringStatus: "archived".
Clone Campaign
Creates a new draft broadcast campaign as a copy of an existing broadcast.
POST /campaigns/:id/clone
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: <uuid-v4> (optional, 24-hour deduplication)Required scope: campaigns:create
| Field | Type | Default | Description |
|---|---|---|---|
name | string | null | "{source} (Copy)" | Name for the clone. |
includeAudience | boolean | true | Copy audience bindings. |
includeContent | boolean | true | Copy content binding. |
includeSchedule | boolean | false | Copy schedule intent. |
externalId | string | null | - | External ID for the clone. |
metadata | object | null | - | External metadata for the clone. |
Response - 201 Created - same shape as Get Campaign for the new clone.
Common errors
| Code | HTTP | Meaning |
|---|---|---|
JOURNEY_API.PUBLIC.CAMPAIGNS.NOT_FOUND | 404 | Campaign not found. |
JOURNEY_API.PUBLIC.CAMPAIGNS.TYPE_UNSUPPORTED | 409 | Source campaign is not a broadcast. |
JOURNEY_API.PUBLIC.CAMPAIGNS.SUMMARY_UNAVAILABLE | 409 | Source campaign is missing persisted broadcast state. |
JOURNEY_API.AUTH.INSUFFICIENT_SCOPE | 403 | Token lacks the required scope. |
See Also
- Audience Binding - configure who receives the campaign
- Content Binding - assign template and sender
- Schedule & Send - schedule or trigger execution
- Campaigns Overview - lifecycle diagram and scope reference