Kenso

Email sequences

A campaign's email sequence: read, replace or delete it, preview it for one contact and send a test.

Campaigns and sequences5 endpointsKen-Version: 2026-11-02AuthenticationWorkspacesErrors

Return the campaign email sequence

GET/v1/campaigns/{campaignId}/email-sequenceemail-sequences.get

Try it on developer.ken.so

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).

Response 200

data is a EmailSequenceModel.

Field
Type
Description
id
integer (int64)
campaignId
integer (int64)
segmentId
integer (int64)
name
string
isActive
boolean
steps
array of EmailSequenceStepModel
createdAt
string (date-time)
modifiedAt
string (date-time)
contentWarnings
array of EmailSequenceContentWarningDTO
Content-policy findings from this save. Only on replace responses that have findings; omitted otherwise.
Request
curl -X GET "https://api.ken.so/v1/campaigns/812/email-sequence" \
  -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": 2210,
    "campaignId": 4821,
    "segmentId": 1207,
    "name": "Engineering leaders",
    "isActive": true,
    "steps": [
      {
        "id": 8841,
        "stepOrder": 1,
        "variant": "a",
        "subject": "Onboarding new engineers at {{company}}",
        "body": "Hi {{first_name}},\n\nMost engineering teams lose a new hire's first two weeks to setup and access requests. We help teams like yours get that down to two days.\n\nWorth a 15-minute look next week?\n\n{{sender_first_name}}",
        "bodyFormat": "plain",
        "waitDays": 0,
        "isActive": true,
        "openTrackingEnabled": false,
        "handwrittenEnabled": false
      },
      {
        "id": 8842,
        "stepOrder": 2,
        "variant": "a",
        "subject": "",
        "body": "Hi {{first_name}}, following up on this. Happy to send a 2-minute video instead if that is easier.\n\n{{sender_first_name}}",
        "bodyFormat": "plain",
        "waitDays": 3,
        "isActive": true,
        "openTrackingEnabled": false,
        "handwrittenEnabled": false
      }
    ],
    "createdAt": "2026-09-23T15:02:11Z",
    "modifiedAt": "2026-10-14T09:41:27Z"
  }
}

Replace the campaign email sequence

PUT/v1/campaigns/{campaignId}/email-sequenceemail-sequences.replace

Try it on developer.ken.so

Creates the sequence when none exists. Requires SequenceEditAccess. A link the URL reputation check refuses is 400 validation_failed with one link_reputation_blocked detail per link: fieldsteps[i].subject or steps[i].body, rule, stepOrder and, for a saved step, stepId. Send expectedModifiedAt (the modifiedAt from your last read) so an edit made since is not overwritten.

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
name
string
optional
isActive
boolean
optional
steps
array of ReplaceEmailSequenceStepRequest
optional
expectedModifiedAt
string (date-time)
optional
Optional. The modifiedAt from your last read. When the live sequence changed since, the replace is refused with 409 conflict and detail code stale_write, so you do not overwrite someone else's edit.

Response 200

data is a EmailSequenceModel.

Field
Type
Description
id
integer (int64)
campaignId
integer (int64)
segmentId
integer (int64)
name
string
isActive
boolean
steps
array of EmailSequenceStepModel
createdAt
string (date-time)
modifiedAt
string (date-time)
contentWarnings
array of EmailSequenceContentWarningDTO
Content-policy findings from this save. Only on replace responses that have findings; omitted otherwise.
Request
curl -X PUT "https://api.ken.so/v1/campaigns/812/email-sequence" \
  -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 '{
  "name": "string",
  "isActive": true,
  "steps": [
    {
      "id": 812,
      "stepOrder": 1,
      "variant": "string",
      "name": "string",
      "subject": "string",
      "body": "string",
      "bodyFormat": "string",
      "waitDays": 1,
      "isActive": true,
      "openTrackingEnabled": true,
      "handwrittenEnabled": true,
      "handwrittenBackground": "string",
      "handwrittenIntro": "string",
      "studioTemplateVersionId": 812
    }
  ],
  "expectedModifiedAt": "2026-09-23T15:02:11Z"
}'
Response 200
{
  "success": true,
  "data": {
    "id": 2210,
    "campaignId": 4821,
    "segmentId": 1207,
    "name": "Engineering leaders",
    "isActive": true,
    "steps": [
      {
        "id": 8841,
        "stepOrder": 1,
        "variant": "a",
        "subject": "Onboarding new engineers at {{company}}",
        "body": "Hi {{first_name}},\n\nMost engineering teams lose a new hire's first two weeks to setup and access requests. We help teams like yours get that down to two days.\n\nWorth a 15-minute look next week?\n\n{{sender_first_name}}",
        "bodyFormat": "plain",
        "waitDays": 0,
        "isActive": true,
        "openTrackingEnabled": false,
        "handwrittenEnabled": false
      },
      {
        "id": 8842,
        "stepOrder": 2,
        "variant": "a",
        "subject": "",
        "body": "Hi {{first_name}}, following up on this. Happy to send a 2-minute video instead if that is easier.\n\n{{sender_first_name}}",
        "bodyFormat": "plain",
        "waitDays": 3,
        "isActive": true,
        "openTrackingEnabled": false,
        "handwrittenEnabled": false
      }
    ],
    "createdAt": "2026-09-23T15:02:11Z",
    "modifiedAt": "2026-10-14T09:41:27Z"
  }
}

Delete the campaign email sequence

DELETE/v1/campaigns/{campaignId}/email-sequenceemail-sequences.delete

Try it on developer.ken.so

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).

Response 204

Request
curl -X DELETE "https://api.ken.so/v1/campaigns/812/email-sequence" \
  -H "Authorization: Bearer $KEN_API_KEY" \
  -H "Ken-Version: 2026-11-02" \
  -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
Response 204
HTTP/1.1 204 No Content

Preview the campaign email sequence for one contact

GET/v1/campaigns/{campaignId}/email-sequence/previewemail-sequences.preview

Try it on developer.ken.so

Requires ReadAccess. Variable types are lowercase snake_case. Each step shows the text dispatch sends the contact: a contact routed to another language gets the approved localization with its language, English mirrorSubject and mirrorBody, and qaScore. language previews as a contact in that language instead. Pass contactId, or useRandom=true to preview a random contact of the campaign; with neither the call is 400 validation_failed on contactId. A contact outside the campaign, or a campaign with no contacts to pick, is 404 not_found. senderEmailId previews from that inbox of the campaign, as the test send sends from it: its names, signature and unsubscribe host fill the steps, and sender names it. Without it, or with an inbox the campaign does not use, the first sender by email fills them.

Parameters

Name
In
Type
Required
Description
campaignId
In: path
integer (int64)
required
contactId
In: query
integer (int64)
optional
useRandom
In: query
boolean
optional
language
In: query
string
optional
Preview as a contact in this language (fr-FR, de) gets the steps; omitted previews in the contact's own language. The campaign's source language previews the source text.
senderEmailId
In: query
integer (int64)
optional
The inbox to preview from, as the test send's senderEmailId: its names, signature and unsubscribe host fill the steps, and sender names it. An inbox that is not one of the campaign's, or none, previews from the first sender.
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 EmailSequencePreviewModel.

Field
Type
Description
steps
array of EmailSequencePreviewStepModel
contact
EmailSequencePreviewContactModel
sender
EmailSequencePreviewSenderModel
senders
array of EmailSequencePreviewSenderModel
Request
curl -X GET "https://api.ken.so/v1/campaigns/812/email-sequence/preview" \
  -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": {
    "steps": [
      {
        "stepNumber": 1,
        "subject": "string",
        "body": "string",
        "waitDays": 1,
        "variables": [
          {
            "name": "string",
            "type": "string",
            "value": "string",
            "placeholder": "string"
          }
        ],
        "language": "string",
        "mirrorSubject": "string",
        "mirrorBody": "string",
        "qaScore": 1
      }
    ],
    "contact": {
      "id": 812,
      "fullName": "string",
      "company": "string",
      "title": "string",
      "email": "[email protected]"
    },
    "sender": {
      "id": 812,
      "email": "[email protected]",
      "status": "string",
      "warmupReputation": 1,
      "healthScore": 0.5
    },
    "senders": [
      {
        "id": 812,
        "email": "[email protected]",
        "status": "string",
        "warmupReputation": 1,
        "healthScore": 0.5
      }
    ]
  }
}

Send the campaign email sequence to test recipients

POST/v1/campaigns/{campaignId}/email-sequence/test-sendemail-sequences.test-send

Try it on developer.ken.so

targetEmails takes at most 5 valid addresses, none on the workspace blocklist or hard bounced. Once the campaign has inboxes, senderEmailId must be one of them. Each recipient x step counts against the workspace's daily test-send emails (429 when used up). Requires SequenceEditAccess. Each step goes out as dispatch sends it to the contact, in the contact's language or in language; notes names a step sent as an approved translation.

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
contactId
integer (int64)
optional
targetEmails
string
optional
senderEmailId
integer (int64)
optional
language
string
optional
Send as a contact in this language (fr-FR, de) gets the steps; omitted sends in the contact's own language. The campaign's source language sends the source text.

Response 200

data is a EmailSequenceTestSendModel.

Field
Type
Description
stepsSent
integer (int32)
emailsAttempted
integer (int32)
emailsSent
integer (int32)
sentTo
array of string
failures
array of string
notes
array of string
Sends that went out differently from the step as written, e.g. a handwritten letter sent as text.
Request
curl -X POST "https://api.ken.so/v1/campaigns/812/email-sequence/test-send" \
  -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 '{
  "contactId": 812,
  "targetEmails": "string",
  "senderEmailId": 812,
  "language": "string"
}'
Response 200
{
  "success": true,
  "data": {
    "stepsSent": 1,
    "emailsAttempted": 1,
    "emailsSent": 1,
    "sentTo": [
      "string"
    ],
    "failures": [
      "string"
    ],
    "notes": [
      "string"
    ]
  }
}