Kenso

AI runs

Queue AI runs over a campaign's contacts, check eligibility and reset flows or chosen contacts.

AI5 endpointsKen-Version: 2026-11-02AuthenticationWorkspacesErrors

Returns 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

Name
In
Type
Required
Description
campaignId
In: path
integer (int64)
required
Ken-Version
In: header
string, one of: 2026-11-02, 1.1
required
The API version: a dated version (YYYY-MM-DD), or 1.1, another name for the first dated version. Without it the request uses your API key's pinned version; an unpinned key reaches the legacy shape of the same path. 1.0 selects the legacy shape explicitly.
X-Ken-Workspace-Id
In: header
integer (int64)
optional
Workspace to act in. Required when your credential can reach more than one workspace (otherwise 400 workspace_required).

Request body

application/json, optional.

Field
Type
Required
Description
flow
string
optional
batchSize
integer (int32)
optional
processAll
boolean
optional
segmentId
integer (int64)
optional
provider
string
optional
model
string
optional
dryRun
boolean
optional
True validates the run and returns what it would process (200), without queueing it, reserving quota or spending credits. Default false.

Response 200

Also answers 202. data is a AiRunDryRunModel.

Field
Type
Description
dryRun
boolean
campaignId
integer (int64)
flow
string
segmentId
integer (int64)
processAll
boolean
provider
string
model
string
batchSize
integer (int32)
Contacts one run submits: the requested batch, else the flow's limit (process all) or 500.
eligibleCount
integer (int32)
Contacts eligible for the flow now, in the segment when one is set.
projectedContactCount
integer (int32)
The lower of eligibleCount and batchSize: an upper bound on what the run processes.
blockingGate
string
blockingReason
string
Request
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
}'
Response 200
{
  "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"
  }
}

Read one AI run

GET/v1/ai-runs/{aiRunId}ai-runs.get

Try it on developer.ken.so

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

Name
In
Type
Required
Description
aiRunId
In: path
string (uuid)
required
Ken-Version
In: header
string, one of: 2026-11-02, 1.1
required
The API version: a dated version (YYYY-MM-DD), or 1.1, another name for the first dated version. Without it the request uses your API key's pinned version; an unpinned key reaches the legacy shape of the same path. 1.0 selects the legacy shape explicitly.
X-Ken-Workspace-Id
In: header
integer (int64)
optional
Workspace to act in. Required when your credential can reach more than one workspace (otherwise 400 workspace_required).

Response 200

data is a AiRunModel.

Field
Type
Description
id
string
status
string
flow
string
campaignId
integer (int64)
segmentId
integer (int64)
provider
string
model
string
createdAt
string (date-time)
startedAt
string (date-time)
completedAt
string (date-time)
contactsProcessed
integer (int32)
batchId
string
error
string
Request
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"
Response 200
{
  "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

GET/v1/campaigns/{campaignId}/ai-runs/eligibilityai-runs.get-eligibility

Try it on developer.ken.so

Replaces 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

Name
In
Type
Required
Description
campaignId
In: path
integer (int64)
required
SegmentId
In: query
integer (int64)
optional
Flow
In: query
string
optional
Preview
In: query
string
optional
Ken-Version
In: header
string, one of: 2026-11-02, 1.1
required
The API version: a dated version (YYYY-MM-DD), or 1.1, another name for the first dated version. Without it the request uses your API key's pinned version; an unpinned key reaches the legacy shape of the same path. 1.0 selects the legacy shape explicitly.
X-Ken-Workspace-Id
In: header
integer (int64)
optional
Workspace to act in. Required when your credential can reach more than one workspace (otherwise 400 workspace_required).

Response 200

data is a AiRunEligibilityModel.

Field
Type
Description
campaignId
integer (int64)
flows
array of AiRunFlowEligibilityModel
preview
AiRunInvalidationPreviewModel
Request
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"
Response 200
{
  "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

POST/v1/campaigns/{campaignId}/ai-runs/resetai-runs.reset

Try it on developer.ken.so

A campaign outside the workspace is not_found before the reset. Requires Campaigns edit access (owners, admins and editors by default).

Parameters

Name
In
Type
Required
Description
campaignId
In: path
integer (int64)
required
Ken-Version
In: header
string, one of: 2026-11-02, 1.1
required
The API version: a dated version (YYYY-MM-DD), or 1.1, another name for the first dated version. Without it the request uses your API key's pinned version; an unpinned key reaches the legacy shape of the same path. 1.0 selects the legacy shape explicitly.
X-Ken-Workspace-Id
In: header
integer (int64)
optional
Workspace to act in. Required when your credential can reach more than one workspace (otherwise 400 workspace_required).

Request body

application/json, optional.

Field
Type
Required
Description
flow
string
optional

Response 200

data is a AiRunResetModel.

Field
Type
Description
flows
array of string
batchesCancelled
integer (int32)
runsCancelled
integer (int32)
aiResponseLinesDeleted
integer (int32)
aiResponsesDeleted
integer (int32)
outreachStatusesRemoved
integer (int32)
qualificationResponsesDeleted
integer (int32)
segmentationReasoningDeleted
integer (int32)
contactsQualificationReset
integer (int32)
contactsSegmentReset
integer (int32)
contactsReset
integer (int32)
Contacts a reset-contacts call cleared. Omitted on a campaign-wide reset.
contactsSkipped
array of AiRunResetSkippedContactModel
Named contacts a reset-contacts call left untouched. Omitted on a campaign-wide reset.
Request
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"
}'
Response 200
{
  "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

POST/v1/campaigns/{campaignId}/ai-runs/reset-contactsai-runs.reset-contacts

Try it on developer.ken.so

DEV-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

Name
In
Type
Required
Description
campaignId
In: path
integer (int64)
required
Ken-Version
In: header
string, one of: 2026-11-02, 1.1
required
The API version: a dated version (YYYY-MM-DD), or 1.1, another name for the first dated version. Without it the request uses your API key's pinned version; an unpinned key reaches the legacy shape of the same path. 1.0 selects the legacy shape explicitly.
X-Ken-Workspace-Id
In: header
integer (int64)
optional
Workspace to act in. Required when your credential can reach more than one workspace (otherwise 400 workspace_required).

Request body

application/json, optional.

Field
Type
Required
Description
flow
string
optional
personalization or rewriting.
contactIds
array of integer (int64)
optional
1 to 500 contact ids of the campaign.

Response 200

data is a AiRunResetModel.

Field
Type
Description
flows
array of string
batchesCancelled
integer (int32)
runsCancelled
integer (int32)
aiResponseLinesDeleted
integer (int32)
aiResponsesDeleted
integer (int32)
outreachStatusesRemoved
integer (int32)
qualificationResponsesDeleted
integer (int32)
segmentationReasoningDeleted
integer (int32)
contactsQualificationReset
integer (int32)
contactsSegmentReset
integer (int32)
contactsReset
integer (int32)
Contacts a reset-contacts call cleared. Omitted on a campaign-wide reset.
contactsSkipped
array of AiRunResetSkippedContactModel
Named contacts a reset-contacts call left untouched. Omitted on a campaign-wide reset.
Request
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
  ]
}'
Response 200
{
  "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"
      }
    ]
  }
}