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/jsonRequired 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
| Field | Type | Description |
|---|---|---|
severity | string | blocker, warning, or info. |
code | string | Stable machine-readable issue code. |
message | string | Human-readable explanation. |
step | object | Optional journey step reference when the issue maps to a send step. |
refs | object | Optional object IDs such as templateId, senderProfileId, or manifestId. |
remediation | array | Optional remediation links for UI clients. |
Common issue codes
| Code | Meaning |
|---|---|
AUDIENCE_NOT_ASSIGNED | No included audience is bound to the campaign. |
NO_START_NODE | Campaign graph is missing its start step. |
TEMPLATE_NOT_ASSIGNED | Send step has no template. |
TEMPLATE_NOT_FOUND | Referenced template no longer exists. |
TEMPLATE_NOT_PUBLISHED | Template exists but has no published version. |
MANIFEST_NOT_FOUND | Published template has no composition manifest. |
SENDER_PROFILE_NOT_FOUND | Email sender profile is missing or inactive. |
SENDER_PROFILE_NOT_VERIFIED | Sender domain is not verified. |
DIRECT_MAIL_ADDRESS_COVERAGE_MISSING | Direct mail audience is missing required postal fields. |
DIRECT_MAIL_PROOF_RENDER_FAILED | Direct mail proof could not be rendered. |
HTTP errors
| Code | HTTP | Meaning |
|---|---|---|
JOURNEY_API.PUBLIC.CAMPAIGNS.NOT_FOUND | 404 | Campaign not found. |
JOURNEY_API.PUBLIC.CAMPAIGNS.TYPE_UNSUPPORTED | 409 | Only broadcast campaigns support public preflight in v1. |
JOURNEY_API.PUBLIC.CAMPAIGNS.VERSION_MISSING | 409 | Campaign has no current version. |
JOURNEY_API.AUTH.INSUFFICIENT_SCOPE | 403 | Token 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 TrueSee Also
- Schedule & Send - commit to execution after preflight passes
- Audience Binding - fix missing audience
- Content Binding - fix missing template