AI runs
Queue AI runs over a campaign's contacts, check eligibility and reset flows or chosen contacts.
Queue an AI run for a campaign in the current workspace
/v1/campaigns/{campaignId}/ai-runsai-runs.createReturns 202 with a run id. Poll GET /v1/ai-runs/{aiRunId}. A campaign outside X-Ken-Workspace-Id is not_found before the run is queued. With dryRun: true the request is validated the same way and the answer is 200 with what the run would process: batchSize (the requested batch, else the flow's submission limit when processAll, else 500), eligibleCount (eligible contacts now, in segmentId when set), projectedContactCount (the lower of the two, an upper bound) and the flow's blockingGate and blockingReason. Nothing is queued, no quota is reserved and no credits are spent. Requires WriteAccess.
Parameters
400 workspace_required).Request body
application/json, optional.
200), without queueing it, reserving quota or spending credits. Default false.Response 200
Also answers 202. data is a AiRunDryRunModel.
eligibleCount and batchSize: an upper bound on what the run processes.curl -X POST "https://api.ken.so/v1/campaigns/812/ai-runs" \
-H "Authorization: Bearer $KEN_API_KEY" \
-H "Ken-Version: 2026-11-02" \
-H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"flow": "string",
"batchSize": 1,
"processAll": true,
"segmentId": 812,
"provider": "string",
"model": "string",
"dryRun": true
}'{
"success": true,
"data": {
"dryRun": true,
"campaignId": 812,
"flow": "string",
"segmentId": 812,
"processAll": true,
"provider": "string",
"model": "string",
"batchSize": 1,
"eligibleCount": 1,
"projectedContactCount": 1,
"blockingGate": "string",
"blockingReason": "string"
}
}The run's campaign must belong to X-Ken-Workspace-Id. A missing run, or a run outside the workspace, is not_found. Requires ReadAccess.
Parameters
400 workspace_required).Response 200
data is a AiRunModel.
curl -X GET "https://api.ken.so/v1/ai-runs/3fa85f64-5717-4562-b3fc-2c963f66afa6" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"id": "string",
"status": "string",
"flow": "string",
"campaignId": 812,
"segmentId": 812,
"provider": "string",
"model": "string",
"createdAt": "2026-09-23T15:02:11Z",
"startedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z",
"contactsProcessed": 1,
"batchId": "string",
"error": "string"
}
}Read AI-run eligibility for a campaign, with an optional invalidation preview
/v1/campaigns/{campaignId}/ai-runs/eligibilityai-runs.get-eligibilityReplaces eligible counts and the invalidation preview. Pass flow and preview (freshness or reset) to include the preview. A campaign outside the workspace is not_found before either read. Requires ReadAccess.
Parameters
400 workspace_required).Response 200
data is a AiRunEligibilityModel.
curl -X GET "https://api.ken.so/v1/campaigns/812/ai-runs/eligibility" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"campaignId": 812,
"flows": [
{
"flow": "string",
"eligibleCount": 1,
"blockingGate": "string",
"blockingReason": "string"
}
],
"preview": {
"flow": "string",
"preview": "string",
"hasImpact": true,
"cascadeFlows": [
"string"
],
"campaignStatus": "string",
"sendingStatus": "string",
"emailsSent": 1,
"message": "string",
"impact": {
"qualificationResponses": 1,
"qualifiedFlags": 1,
"segmentationReasoning": 1,
"segmentationAssignmentsKept": 1,
"segmentationAssignmentsReset": 1,
"personalizationLines": 1,
"personalizationLeads": 1,
"rewritingLines": 1,
"rewritingLeads": 1
}
}
}
}Reset an AI flow and the flows that depend on it
/v1/campaigns/{campaignId}/ai-runs/resetai-runs.resetA campaign outside the workspace is not_found before the reset. Requires Campaigns edit access (owners, admins and editors by default).
Parameters
400 workspace_required).Request body
application/json, optional.
Response 200
data is a AiRunResetModel.
curl -X POST "https://api.ken.so/v1/campaigns/812/ai-runs/reset" \
-H "Authorization: Bearer $KEN_API_KEY" \
-H "Ken-Version: 2026-11-02" \
-H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"flow": "string"
}'{
"success": true,
"data": {
"flows": [
"string"
],
"batchesCancelled": 1,
"runsCancelled": 1,
"aiResponseLinesDeleted": 1,
"aiResponsesDeleted": 1,
"outreachStatusesRemoved": 1,
"qualificationResponsesDeleted": 1,
"segmentationReasoningDeleted": 1,
"contactsQualificationReset": 1,
"contactsSegmentReset": 1,
"contactsReset": 1,
"contactsSkipped": [
{
"contactId": 812,
"reason": "string"
}
]
}
}Reset personalization or rewriting for chosen contacts of a campaign
/v1/campaigns/{campaignId}/ai-runs/reset-contactsai-runs.reset-contactsDEV-3008. Only the listed contacts (1 to 500 ids) are reset, for the flow and the flows that depend on it; nothing campaign-wide is cancelled. Ids that are not contacts of the campaign, contacts already sent or exported, contacts with a send queued, and contacts inside an active batch come back in contactsSkipped and are not touched. A campaign outside the workspace is not_found. This is its own route rather than a field on reset: a build without it answers 404, while a build before DEV-3008 would ignore a contactIds field on reset and reset the whole campaign.
Parameters
400 workspace_required).Request body
application/json, optional.
Response 200
data is a AiRunResetModel.
curl -X POST "https://api.ken.so/v1/campaigns/812/ai-runs/reset-contacts" \
-H "Authorization: Bearer $KEN_API_KEY" \
-H "Ken-Version: 2026-11-02" \
-H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"flow": "string",
"contactIds": [
812
]
}'{
"success": true,
"data": {
"flows": [
"string"
],
"batchesCancelled": 1,
"runsCancelled": 1,
"aiResponseLinesDeleted": 1,
"aiResponsesDeleted": 1,
"outreachStatusesRemoved": 1,
"qualificationResponsesDeleted": 1,
"segmentationReasoningDeleted": 1,
"contactsQualificationReset": 1,
"contactsSegmentReset": 1,
"contactsReset": 1,
"contactsSkipped": [
{
"contactId": 812,
"reason": "string"
}
]
}
}