Agent team
Actions taken by Kenso's agent team, with undo, and its runway.
Return how long each live campaign's leads last and whether its inboxes can carry the send
/v1/agent-team/runwayagent-team.get-runwayNeeds the agent_templates feature on your workspace. Until it is on, calls answer 404.
Without campaignId: the workspace's live campaigns (scheduled, sending, launching), newest first, at most 50. With it: that campaign in any status, 404 when it is not in the workspace. Per campaign: Autopilot, the leads waiting for their first email and the days they last at the last 7 days' rate, the daily cap against the healthy inboxes' limits, each inbox's 7-day sends and bounce rate and why it is sick, the latest placement test, and the email interested replies. Reads only. Campaigns view.
Parameters
400 workspace_required).Response 200
data is a RunwayModel.
curl -X GET "https://api.ken.so/v1/agent-team/runway" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"workspace": {
"targetInterestedPerWeek": 1,
"runwayDays": 1,
"clientWorkspace": true
},
"campaigns": [
{
"campaignId": 812,
"name": "string",
"sendingStatus": "string",
"autopilot": {
"on": true,
"audienceApproved": true,
"weeklyTopUpLimit": 1,
"toppedUpLast7Days": 1
},
"leads": {
"notStarted": 1,
"firstTouchesPerDay": 0.5,
"runwayDays": 1
},
"capacity": {
"dailyCap": 1,
"rampFloor": 1,
"inboxCapacity": 1,
"healthyInboxCapacity": 1,
"short": true
},
"inboxes": [
{
"inboxId": 812,
"email": "[email protected]",
"dailyLimit": 1,
"sends7d": 1,
"bounceRate7d": 0.5,
"sick": true,
"sickReason": "string"
}
],
"placement": {
"completedAt": "2026-09-23T15:02:11Z",
"inboxRate": 0.5,
"spamRate": 0.5
},
"replies": {
"interested7d": 1,
"unansweredInterested": 1
}
}
]
}
}Ask for one bounded change to a campaign: applied when it is inside Autopilot and the bounds, else proposed
/v1/agent-team/actionsagent-team.create-actionNeeds the agent_templates feature on your workspace. Until it is on, calls answer 404.
Body: kind (cap_move, inbox_drop, inbox_restore, top_up, placement_test or make_default), campaignId, args for the kind, reason (up to 500 characters) and an optional idempotencyKey (up to 120). Every outcome is 200 with status: applied (the change is made and recorded as the caller's, with actionId and the undo deadline), proposed (Autopilot is off, a standing rule holds the campaign, or the change is outside the bounds: a person decides it in Approvals, approvalId), or refused (code and message; nothing changed). A lead top-up runs as a person, so an API key with no user is refused needs_a_user, and it also needs Search view and the Import permission (else 403permission_denied). A retry with the same key, or without one the same request on the same UTC day, runs once; without a key a try that failed runs again. The tool that sent it (X-Ken-Client) names the caller in the history. Campaigns edit.
Parameters
400 workspace_required).Request body
application/json, optional.
cap_move, inbox_drop, inbox_restore, top_up, placement_test or make_default. Required.cap_move{ newCap }, inbox_drop{ inboxId, scope } (campaign, the default when scope is absent, or inbox for every campaign it sends on), inbox_restore{ inboxId }, top_up{ count } (the caller also needs Search view and the Import permission), placement_test{}, make_default{ experimentId }. Absent is {}.Response 200
data is a AgentActionOutcomeModel.
applied, proposed (a person decides it in Approvals) or refused.POST /v1/agent-team/actions/{actionId}/undo. Applied only.request_<id>, as /v1/approvals lists it. Proposed only.unknown_kind, kind_not_allowed, invalid_args, campaign_not_found, campaign_paused, workspace_suspended, apply_failed, needs_a_user, already_undone (the key's action was undone: send a new key) or busy (another change to the campaign is being checked: retry). Refused only.curl -X POST "https://api.ken.so/v1/agent-team/actions" \
-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 '{
"kind": "string",
"campaignId": 812,
"args": {
"key": {
"options": {
"propertyNameCaseInsensitive": true
},
"parent": {},
"root": {}
}
},
"reason": "string",
"idempotencyKey": "string"
}'{
"success": true,
"data": {
"status": "string",
"actionId": 812,
"undoUntil": "2026-09-23T15:02:11Z",
"summary": "string",
"approvalId": "string",
"why": "string",
"code": "string",
"message": "string"
}
}Undo an applied agent action inside its undo window
/v1/agent-team/actions/{actionId}/undoagent-team.undo-actionNeeds the agent_templates feature on your workspace. Until it is on, calls answer 404.
Puts back what the action changed, as the caller: the previous cap, the dropped inbox, the default before; a top-up removes only the leads that have not started (no email sent or queued). 404 when the action is not in the workspace; 400 when it is already undone or failed, its window has closed, or the undo would overwrite a newer change (the action stays in effect with the reason). Returns the action. Campaigns edit.
Parameters
400 workspace_required).Response 200
data is a AgentActionModel.
applied, undone, undo_failed or failed.curl -X POST "https://api.ken.so/v1/agent-team/actions/812/undo" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"actionId": 812,
"status": "string",
"undoneAt": "2026-09-23T15:02:11Z",
"keptLeads": 1
}
}Return what changed on the workspace's campaigns since a time, by default since the caller last asked
/v1/agent-team/changesagent-team.list-changesNeeds the agent_templates feature on your workspace. Until it is on, calls answer 404.
since (ISO 8601) defaults to the caller's previous call from the same tool (X-Ken-Client), else 7 days ago, and is never more than 30 days back. Without campaignId the change log covers the live campaigns (scheduled, sending, launching). Returns the change log rows (newest first, at most 200, truncated when there were more) with who made each and its requestId, the agent actions applied (an undone top-up with the leads it kept), the proposals open now and those decided in the window, and the standing rules in force. Reads only. Campaigns view; proposals is null unless the caller also has Campaigns edit and Approve, as Approvals.
Parameters
400 workspace_required).Response 200
data is a AgentTeamChangesModel.
curl -X GET "https://api.ken.so/v1/agent-team/changes" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"since": "2026-09-23T15:02:11Z",
"until": "2026-09-23T15:02:11Z",
"truncated": true,
"changes": [
{
"campaignId": 812,
"occurredAt": "2026-09-23T15:02:11Z",
"kind": "string",
"actor": {
"kind": "string",
"label": "string"
},
"requestId": "string",
"diff": {
"key": {
"options": {},
"parent": {},
"root": {}
}
}
}
],
"actions": [
{
"actionId": 812,
"kind": "string",
"campaignId": 812,
"status": "string",
"appliedAt": "2026-09-23T15:02:11Z",
"undoUntil": "2026-09-23T15:02:11Z",
"actorLabel": "string",
"authority": "string",
"keptLeads": 1
}
],
"proposals": {
"open": [
{
"id": "string",
"kind": "string",
"status": "string",
"campaignId": 812,
"title": "string",
"reason": "string",
"args": {
"key": {}
},
"urgent": true,
"requiresClientYes": true,
"requestedBy": {
"kind": "string",
"label": "string"
},
"createdAt": "2026-09-23T15:02:11Z",
"expiresAt": "2026-09-23T15:02:11Z",
"decidedAt": "2026-09-23T15:02:11Z",
"decidedBy": {
"userId": 812,
"name": "string"
},
"error": "string"
}
],
"decided": [
{
"id": "string",
"kind": "string",
"status": "string",
"campaignId": 812,
"title": "string",
"reason": "string",
"args": {
"key": {}
},
"urgent": true,
"requiresClientYes": true,
"requestedBy": {
"kind": "string",
"label": "string"
},
"createdAt": "2026-09-23T15:02:11Z",
"expiresAt": "2026-09-23T15:02:11Z",
"decidedAt": "2026-09-23T15:02:11Z",
"decidedBy": {
"userId": 812,
"name": "string"
},
"error": "string"
}
]
},
"standingRules": [
{
"ruleId": 812,
"campaignId": 812,
"text": "string",
"expiresAt": "2026-09-23T15:02:11Z",
"author": {
"userId": 812,
"name": "string"
}
}
]
}
}List the workspace's proposals, newest first
/v1/agent-team/proposalsagent-team.list-proposalsNeeds the agent_templates feature on your workspace. Until it is on, calls answer 404.
status: open (pending or awaiting the client) or one status as it reads now (a pending proposal past its expiry reads expired); campaignId; limit 1-100, default 25; cursor from the previous page. A status filter applies within each page, so a page can hold fewer than limit while pagination.hasMore is true. Campaigns edit and Approve, as Approvals.
Parameters
open (pending or awaiting the client), pending, awaiting_client, applied, rejected, expired, superseded or failed. Omit for every status.400 workspace_required).Response 200
data is a list of AgentProposalModel. Each item has these fields. The response also carries pagination.
request_<id>, the id /v1/approvals lists it under.pending, awaiting_client, applied, rejected, expired, superseded or failed, as it reads now.kindagent, user or api_key, and the label the card shows.curl -X GET "https://api.ken.so/v1/agent-team/proposals" \ -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",
"kind": "string",
"status": "string",
"campaignId": 812,
"title": "string",
"reason": "string",
"args": {
"key": {
"options": {
"propertyNameCaseInsensitive": true
},
"parent": {},
"root": {}
}
},
"urgent": true,
"requiresClientYes": true,
"requestedBy": {
"kind": "string",
"label": "string"
},
"createdAt": "2026-09-23T15:02:11Z",
"expiresAt": "2026-09-23T15:02:11Z",
"decidedAt": "2026-09-23T15:02:11Z",
"decidedBy": {
"userId": 812,
"name": "string"
},
"error": "string"
}
],
"pagination": {
"nextCursor": "eyJpZCI6ODE1fQ",
"hasMore": true,
"total": 1,
"isTotalCapped": true
}
}Propose a change a person decides in Approvals
/v1/agent-team/proposalsagent-team.create-proposalNeeds the agent_templates feature on your workspace. Until it is on, calls answer 404.
Body: kind, campaignId (omit for angle, standing_rule and client_update), title (up to 120 characters), reason (up to 1000), args for the kind, and optionally clientYes (the client's yes in their own words, up to 300; the approver still confirms it) and urgent. Returns 201 with the proposal, request_<id>, as /v1/approvals lists it. 400unknown_kind, kind_not_allowed or invalid_args with the reason (campaign_not_found when a campaign kind names no campaign); 404campaign_not_found when the campaign is not in the workspace, deleted or archived. The tool that sent it (X-Ken-Client) names the requester on the card. Campaigns edit.
Parameters
400 workspace_required).Request body
application/json, optional.
cap_move, schedule, pause, resume, top_up, audience, copy, inbox_drop, inbox_restore, make_default, placement_test, standing_rule, client_update or angle. Required.angle, standing_rule, client_update).{}.Response 201
data is a AgentProposalModel.
request_<id>, the id /v1/approvals lists it under.pending, awaiting_client, applied, rejected, expired, superseded or failed, as it reads now.kindagent, user or api_key, and the label the card shows.curl -X POST "https://api.ken.so/v1/agent-team/proposals" \
-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 '{
"kind": "string",
"campaignId": 812,
"title": "string",
"reason": "string",
"args": {
"key": {
"options": {
"propertyNameCaseInsensitive": true
},
"parent": {},
"root": {}
}
},
"clientYes": "string",
"urgent": true
}'{
"success": true,
"data": {
"id": "string",
"kind": "string",
"status": "string",
"campaignId": 812,
"title": "string",
"reason": "string",
"args": {
"key": {
"options": {
"propertyNameCaseInsensitive": true
},
"parent": {},
"root": {}
}
},
"urgent": true,
"requiresClientYes": true,
"requestedBy": {
"kind": "string",
"label": "string"
},
"createdAt": "2026-09-23T15:02:11Z",
"expiresAt": "2026-09-23T15:02:11Z",
"decidedAt": "2026-09-23T15:02:11Z",
"decidedBy": {
"userId": 812,
"name": "string"
},
"error": "string"
}
}