Campaign Runs
A run represents a single dispatch of a published campaign to an audience.
Runs are generated in two ways:
- Scheduled Runs: The campaign's initial run, created automatically when you publish via
POST /schedule-jobsorPOST /send-jobs. - On-Demand Runs: Additional runs triggered manually or programmatically via
POST /campaigns/:id/runs. This is used for rolling enrollment cycles, follow-up sends, or triggering a new direct mail drop for a refreshed audience.
This page documents how to retrieve the history of a campaign's runs and how to trigger new ones via the API.
Base URL: https://api.experiture.ai/public/v1
List Campaign Runs
Returns the most recent runs for a campaign, including their current execution status. Useful for operational dashboards, polling for completion, or retrieving generated export files.
GET /campaigns/:id/runs?limit=25
Authorization: Bearer <token>Required scope: campaigns:read
Path parameters
| Name | Type | Description |
|---|---|---|
campaign_id | string (UUID) | Campaign identifier. |
Query parameters
| Name | Type | Description |
|---|---|---|
limit | integer | 1–50, default 25. Limits the number of runs returned. |
Response — 200 OK
Note: To keep this example concise, the full package schema is omitted from the JSON below. See Advanced: Execution Packages for the complete package and artifact schema.
{
"success": true,
"data": {
"campaignId": "4130bada-9264-465f-bc0c-a26bebcfcc81",
"name": "Spring Re-engagement Direct Mail",
"channel": "direct_mail",
"manifestId": "33333333-3333-3333-3333-333333333333",
"touchpointId": 123,
"runs": [
{
"runId": "5e5e5e5e-5e5e-5e5e-5e5e-5e5e5e5e5e5e",
"scheduleRunId": "5e5e5e5e-5e5e-5e5e-5e5e-5e5e5e5e5e5e",
"scheduleId": "44444444-4444-4444-4444-444444444444",
"manifestId": "33333333-3333-3333-3333-333333333333",
"touchpointId": 123,
"channel": "direct_mail",
"runKind": "on_demand",
"runStatus": "enqueued",
"requestSource": "api",
"requestedAt": "2026-05-09T13:40:11.000Z",
"unitCount": 17480,
"packageStatus": "ready",
"package": {}
}
]
}
}If the campaign has no manifest yet (never finalized) the response returns runs: [].
Run object fields
| Field | Type | Description |
|---|---|---|
runId | string (UUID) | Stable run identifier (alias of scheduleRunId). |
scheduleRunId | string (UUID) | Internal identifier (matches runId). |
scheduleId | string (UUID) | The bound schedule that produced this run. |
manifestId | string (UUID) | Composition manifest the run executed against. |
touchpointId | integer | Touchpoint number within the campaign manifest. |
channel | string | Normalized channel (email, sms, push, direct_mail, landing_page). |
runKind | string | How the run was categorized — see Run Kinds. |
runStatus | string | Latest known execution status — see Run Status. |
requestSource | string | Origin of the run — see Request Sources. |
requestedAt | string | ISO 8601 timestamp the run was queued. |
unitCount | integer | null | Total number of recipients or mailpieces processed in this run. |
packageStatus | string | null | Status of the export package, if applicable. |
package | object | null | Only present when the run generates physical artifacts (e.g., Direct Mail drops). See Advanced: Execution Packages. |
Trigger a New Run
Requests a new run against an already published campaign. For example, use this to drop another batch of a Direct Mail piece against an updated audience snapshot, to enqueue a rolling-enrollment cycle, or to trigger an on-demand re-send to a freshly-suppressed cohort.
POST /campaigns/:id/runs
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: <uuid-v4> (optional, 24-hour deduplication)Required scope: campaigns:update
Published only. The campaign must already be in authoringStatus: "published" (i.e. its initial schedule-job has finalized). Run requests against drafts return 409 NOT_PUBLISHED.
Backpressure. A campaign can have at most 5 active run requests (statuses pending, claiming, enqueued) processing at one time. Additional requests return 429 BACKPRESSURE until the worker drains the queue.
Path parameters
| Name | Type | Description |
|---|---|---|
campaign_id | string (UUID) | Campaign identifier. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
audience | object | null | No | Optional audience override for this run only. If omitted, the run reuses the published campaign's original targeting receipt. If provided, include must contain at least one entry. |
requestSource | enum | No | The system or actor triggering the run. One of "api", "platform", "automation", or "integration". Defaults to "api". |
requestKind | enum | No | The operational purpose of the run. In v1 the only accepted value is "on_demand" (also the default). Sending any other value returns 400 JOURNEY_API.VALIDATION.BODY_SCHEMA_MISMATCH. Additional kinds (e.g. recurring-enrollment, imported-list, and automation sources) will be added alongside their execution adapters in a future version. |
idempotencyKey | string | null | No | A caller-supplied key. If omitted, the API uses the Idempotency-Key request header. |
metadata | object | null | No | Arbitrary integration metadata stored alongside the run request and surfaced on the run object. |
audience object
| Field | Type | Required | Description |
|---|---|---|---|
include | array | Yes | 1–25 audience/segment entries ({ audienceId, type }). |
exclude | array | No | 0–25 suppression entries ({ audienceId, type }). |
Response — 202 Accepted
{
"success": true,
"data": {
"runRequestId": "7c3e6a2a-1d4f-49ab-aabb-ccccccccdddd",
"runId": "5e5e5e5e-5e5e-5e5e-5e5e-5e5e5e5e5e5e",
"scheduleRunId": "5e5e5e5e-5e5e-5e5e-5e5e-5e5e5e5e5e5e",
"campaignId": "4130bada-9264-465f-bc0c-a26bebcfcc81",
"scheduleId": "44444444-4444-4444-4444-444444444444",
"manifestId": "33333333-3333-3333-3333-333333333333",
"touchpointId": 123,
"channel": "direct_mail",
"status": "pending",
"requestSource": "api",
"requestKind": "on_demand",
"dueAt": "2026-05-09T13:40:11.000Z",
"requestedAt": "2026-05-09T13:40:11.000Z"
}
}The request is queued asynchronously. To observe the run's progress, poll GET /campaigns/:id/runs and read the matching runId's runStatus property.
Errors
| Code | HTTP | Meaning |
|---|---|---|
JOURNEY_API.CAMPAIGN_RUNS.INVALID_ID | 400 | The supplied campaign_id is not a valid UUID. |
JOURNEY_API.VALIDATION.BODY_SCHEMA_MISMATCH | 400 | The request body failed schema validation — e.g. a requestKind other than on_demand, or an unknown field. |
JOURNEY_API.CAMPAIGN_RUNS.AUDIENCE_REQUIRED | 400 / 409 | The request supplied an empty audience.include, or the published campaign has no default targeting receipt to inherit. |
JOURNEY_API.CAMPAIGN_RUNS.TARGETING_RECEIPT_MISSING | 409 | The published campaign is still finalizing its initial audience—wait for the original schedule-job's finalization to complete and try again. |
JOURNEY_API.CAMPAIGN_RUNS.NOT_FOUND | 404 | Campaign not found in the token tenant. |
JOURNEY_API.CAMPAIGN_RUNS.NOT_PUBLISHED | 409 | Campaign exists but is not in published status. |
JOURNEY_API.CAMPAIGN_RUNS.MANIFEST_MISSING | 409 | The published campaign has no composition manifest mapping. Internal data inconsistency — contact support. |
JOURNEY_API.CAMPAIGN_RUNS.MANIFEST_INCOMPLETE | 409 | The composition manifest exists but lacks the schedule_id needed to bind a run. |
JOURNEY_API.CAMPAIGN_RUNS.SCHEDULE_INACTIVE | 409 | The bound schedule is inactive. Re-activate or reschedule the campaign before requesting more runs. |
JOURNEY_API.CAMPAIGN_RUNS.BACKPRESSURE | 429 | Campaign already has 5 active run requests processing. Retry after one of them resolves. |
JOURNEY_API.CAMPAIGN_RUNS.CREATE_FAILED | 500 | The run request could not be persisted. Safe to retry with the same Idempotency-Key. |
JOURNEY_API.AUTH.INSUFFICIENT_SCOPE | 403 | Token lacks campaigns:update. |
Run Metadata Reference
Run Kinds
runKind on a run object records how the run originated. It is a response field — you don't set it directly. (The requestKind you send on POST /runs is a separate field, and in v1 only accepts on_demand.)
| Value | Meaning |
|---|---|
scheduled | The run came from the campaign's original POST /schedule-jobs or POST /send-jobs — the first send of the campaign. |
on_demand | The run came from a POST /campaigns/:id/runs request. |
delivery_run | A digital (email/SMS/push) execution package produced by the run. |
production_drop | A Direct Mail execution package produced by the run. |
activation_run | A landing-page activation package produced by the run. |
Run Status
A run progresses through these states, visible on runs[].runStatus:
| Value | Meaning |
|---|---|
pending | The run request has been accepted but not yet claimed by the worker. |
claiming | A worker is in the process of claiming the request and reserving execution slots. |
enqueued | The run is actively enqueued in the execution pipeline. |
failed | The worker rejected or could not place the run. |
cancelled | The run was cancelled (administratively or via tenant action) before it dispatched. |
A failed or cancelled run does not block additional run requests — but it does count toward the 5-active-request backpressure limit until the worker clears it.
Request Sources
requestSource records who or what created the run, so downstream audit reports can distinguish them:
| Value | Description |
|---|---|
api | Public API consumer (this endpoint, default). |
platform | Experiture platform UI created the request. |
automation | Internal automation rail (e.g., journey-driven re-runs). |
integration | Pre-built integration (CRM sync, marketing automation connector). |
schedule | Synthetic source attributed to the campaign's original, implicit first run. Read-only — not accepted on POST /campaigns/:id/runs. |
Run Lifecycle Diagram
Whether a run is created automatically by a schedule or manually via the API, the execution lifecycle is identical.
POST /schedule-jobs POST /campaigns/:id/runs
│ │
│ (initial, finalizes asynchronously) │ (additional, on a published campaign)
▼ ▼
run #0 ─────────► enqueued ────► package: pending → assembling → ready → handed_off
│
└──► failed ─────► package: failed (failureKind populated)Advanced: Execution Packages & Exports
Unlike digital channels (Email, SMS) which are event-driven, channels like Direct Mail require the platform to generate secure export files (PDFs, USPS manifest CSVs, Intelligent Mail Barcodes).
These files are bundled into an Execution Package. When querying /campaigns/:id/runs for a campaign that generates physical artifacts, the response will include a package object containing secure URIs to these files.
| Channel | packageKind | What it carries |
|---|---|---|
email, sms, push | delivery_run | Typically null. Event-driven. |
direct_mail | production_drop | Roster, Intelligent Mail Barcode (IMb) payloads, fold/insert manifest, permit indicia metadata. |
landing_page | activation_run | Activation event roster, per-piece personalization, page-variant resolution. |
Package Status
If a package is generated, it goes through its own lifecycle independent of the overall run status:
| Value | Meaning |
|---|---|
pending | Run request has been queued; the package row exists but is empty. |
assembling | The platform is materializing artifacts (rosters, IMb data, content drops). |
ready | All artifacts are written and the package is ready for handoff to the downstream provider. |
handed_off | The package has been delivered to the downstream provider (e.g., postal print provider). |
failed | Package assembly failed. failureKind, lastErrorCode, and lastErrorMessage are populated. |
cancelled | The package was cancelled before handoff. |
failureKind is one of upstream_execution, finalizer_aggregation, handoff, artifact_write, provider.
Package Object Fields
| Field | Type | Description |
|---|---|---|
executionPackageId | string (UUID) | Stable package identifier. |
executionId | string (UUID) | null | Latest execution row that produced or updated this package. null while pending. |
scheduleRunId | string (UUID) | Run that produced the package. |
packageKind | enum | delivery_run / production_drop / activation_run. |
packageStatus | enum | See Package Status above. |
unitCount | integer | Number of units in the package. |
packageRootUri | string | null | Tenant-scoped storage prefix. Always one of abfss://, https://, s3://, production/, or packages/. |
directMailProductionPackageId | string | null | Direct-mail-only — the underlying production-package identifier. |
failureKind | enum | null | When packageStatus is failed, one of upstream_execution, finalizer_aggregation, handoff, artifact_write, provider. |
lastErrorCode, lastErrorMessage | string | null | Diagnostic detail when failed. |
metadata | object | Channel-specific metadata. |
channelPackage | object | null | For Direct Mail: { type: "direct_mail_production_package", id, status }. |
artifacts | array | Individual artifact files (roster, IMb data, content drops, etc.). See below. |
createdAt, updatedAt | string | null | ISO 8601 timestamps. |
Artifact Object Fields
Each entry in the package.artifacts[] array represents a distinct file generated for the run:
| Field | Type | Description |
|---|---|---|
artifactId | string | Artifact identifier. |
role | string | What the artifact represents (e.g. delivery_roster, intelligent_mail_barcodes, content_drop). |
uri | string | Tenant-scoped storage URI. Same prefix rules as packageRootUri. |
contentType | string | null | MIME type. |
rowCount | integer | null | Row count for tabular artifacts. |
byteSize | string | null | Decimal string (avoids JSON-number precision loss for large files). |
checksumSha256 | string | null | Lowercase SHA-256 of the artifact bytes. |
piiClassification | enum | Data sensitivity. One of none, contact, phone, address, postal_identity, message_content, tokenized_identifier. |
metadata | object | Artifact-specific metadata. |
createdAt | string | null | ISO 8601 write timestamp. |
Storage URIs are tenant-scoped. The uri field is the canonical reference to the exported file, but it is only readable from within an Experiture-managed compute environment that has the tenant's data-plane credentials. Use a tenant-scoped credential broker (e.g., via the connectors framework) — never share these URIs directly with end-user clients.
See Also
- Schedule & Send — create the campaign's first run via the schedule/send job endpoints
- Status & Metrics — surface-level execution state and pipeline counts
- Preflight — validate readiness before creating a new run
- Campaign Reporting Guide — patterns for ingesting delivery events into BI