API Reference
Campaigns
Preflight

Preflight

Validate campaign readiness before scheduling or sending. Public preflight runs the shared publish-readiness checks and adds public Campaigns API readiness gates such as audience binding.

Base URL: https://api.experiture.ai/public/v1


Run Preflight

POST /campaigns/:id/preflight
Authorization: Bearer <token>
Content-Type: application/json

Required scope: campaigns:read

Request body: {} (empty object)

Example request

curl -X POST https://api.experiture.ai/public/v1/campaigns/4130bada-9264-465f-bc0c-a26bebcfcc81/preflight \
  -H "Authorization: Bearer <your_access_token>" \
  -H "Content-Type: application/json" \
  -d '{}'

Response - 200 OK

{
  "success": true,
  "data": {
    "journeyId": "4130bada-9264-465f-bc0c-a26bebcfcc81",
    "versionId": "11111111-1111-1111-1111-111111111111",
    "canPublish": false,
    "evaluatedAt": "2026-04-25T10:30:00.000Z",
    "summary": {
      "blockers": 1,
      "warnings": 0,
      "info": 0
    },
    "issues": [
      {
        "severity": "blocker",
        "code": "AUDIENCE_NOT_ASSIGNED",
        "message": "Campaign must include at least one audience before it can be sent.",
        "remediation": [
          {
            "type": "link",
            "label": "Configure Audience",
            "href": "/campaigns/broadcast/4130bada-9264-465f-bc0c-a26bebcfcc81/edit?step=audience"
          }
        ]
      }
    ],
    "bindingsPreview": []
  }
}

Blocked preflight is still 200 OK. Use data.canPublish and data.summary.blockers to decide whether the campaign can move to execution.

Issue fields

FieldTypeDescription
severitystringblocker, warning, or info.
codestringStable machine-readable issue code.
messagestringHuman-readable explanation.
stepobjectOptional journey step reference when the issue maps to a send step.
refsobjectOptional object IDs such as templateId, senderProfileId, or manifestId.
remediationarrayOptional remediation links for UI clients.

Common issue codes

CodeMeaning
AUDIENCE_NOT_ASSIGNEDNo included audience is bound to the campaign.
NO_START_NODECampaign graph is missing its start step.
TEMPLATE_NOT_ASSIGNEDSend step has no template.
TEMPLATE_NOT_FOUNDReferenced template no longer exists.
TEMPLATE_NOT_PUBLISHEDTemplate exists but has no published version.
MANIFEST_NOT_FOUNDPublished template has no composition manifest.
SENDER_PROFILE_NOT_FOUNDEmail sender profile is missing or inactive.
SENDER_PROFILE_NOT_VERIFIEDSender domain is not verified.
DIRECT_MAIL_ADDRESS_COVERAGE_MISSINGDirect mail audience is missing required postal fields.
DIRECT_MAIL_PROOF_RENDER_FAILEDDirect mail proof could not be rendered.

HTTP errors

CodeHTTPMeaning
JOURNEY_API.PUBLIC.CAMPAIGNS.NOT_FOUND404Campaign not found.
JOURNEY_API.PUBLIC.CAMPAIGNS.TYPE_UNSUPPORTED409Only broadcast campaigns support public preflight in v1.
JOURNEY_API.PUBLIC.CAMPAIGNS.VERSION_MISSING409Campaign has no current version.
JOURNEY_API.AUTH.INSUFFICIENT_SCOPE403Token lacks campaigns:read.

Recommended Workflow

Run preflight before creating schedule or send jobs:

import requests
 
def can_publish(campaign_id: str, token: str) -> bool:
    response = requests.post(
        f"https://api.experiture.ai/public/v1/campaigns/{campaign_id}/preflight",
        json={},
        headers={"Authorization": f"Bearer {token}"},
    )
    response.raise_for_status()
    report = response.json()["data"]
    return report["canPublish"] is True

See Also