Kenso

Contacts

List, search, read, update and remove contacts, add them to campaigns and change many in one call.

Contacts and lists13 endpointsKen-Version: 2026-11-02AuthenticationWorkspacesErrors

Each contact goes back into the campaign and segment it was deleted from, with its emails and fields unchanged. Nothing is charged and no enrichment re-runs. A restored contact is live again, so the planner of an active campaign can schedule its next email steps. LinkedIn steps the scheduler stopped while the contact was deleted (suppressed_contact_deleted) stay stopped. Any soft-deleted contact comes back, a lead the end client removed in a lead-list approval included, and that approval is not updated. succeeded lists the ids this call restored and restored counts them. Each other id is in failed with not_deleted (the contact is live, nothing changed; skipped counts these), not_found (missing or not in this workspace), or internal_error (retry that id). A retry is safe: an id an earlier call already restored comes back not_deleted. Requires campaigns edit, matching bulk-delete.

Parameters

Name
In
Type
Required
Description
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
ids
array of integer (int64)
optional
1 to 1,000 contact ids in the workspace.

Response 200

data is a ContactBulkRestoreResult.

Field
Type
Description
restored
integer (int32)
How many contacts this call restored: the length of Ken.Scraper.API.Canonical.ContactBulkRestoreResult.Succeeded.
skipped
integer (int32)
How many ids were not deleted, so nothing changed: the not_deleted entries of Ken.Scraper.API.Canonical.ContactBulkRestoreResult.Failed.
succeeded
array of integer (int64)
failed
array of ContactBulkFailure
Request
curl -X POST "https://api.ken.so/v1/contacts/bulk-restore" \
  -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 '{
  "ids": [
    812
  ]
}'
Response 200
{
  "success": true,
  "data": {
    "restored": 1,
    "skipped": 1,
    "succeeded": [
      1
    ],
    "failed": [
      {
        "id": 812,
        "index": 1,
        "error": {
          "code": "string",
          "message": "string"
        }
      }
    ]
  }
}

Add up to 1,000 contacts to a campaign

POST/v1/campaigns/{campaignId}/contactscontacts.create

Try it on developer.ken.so

Uses the existing campaign lead upsert, in batches of 100. Accepted and updated rows are succeeded. A later batch can fail without rolling back earlier rows; the upsert is safe to retry. customImageUrl sets the lead's own image (an empty string clears it): a malformed URL fails its row before anything is written, and a refused image (campaign images off for the workspace) fails its row with the contact's id, the contact itself saved. customVariables is an object of string values merged into the contact's custom variables (a null or empty value clears that key on an existing contact); a key new to the campaign joins its catalog with AI context off. A blank, unusable or reserved key, or more than 50 keys, fails its row before anything is written; a batch that would take the campaign past 50 custom variables fails every row in it. emailValidity (optional, safe for an email you already verified) qualifies a new contact on import in a campaign without AI qualification, spending one contact allowance; omitted keeps a stored verdict on an existing email. Requires WriteAccess. results has one item per row in request order: index, status (created, updated, skipped or failed), id when saved, and for a skipped or failed row a reason (duplicate, blocklisted, already_contacted, limit_reached, invalid_email, excluded, validation or other) and a message. A saved row has sendable, false when the contact will not send (added unqualified with no contact allowance left, or do-not-contact).

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
contacts
array of CreateCampaignContactRequest
optional

Response 200

data is a ContactBulkResult.

Field
Type
Description
succeeded
array of integer (int64)
failed
array of ContactBulkFailure
results
array of ContactCreateRowResult
One item per input row of contacts.create, in request order. Only that route fills it, so it is absent from the JSON of bulk-delete, bulk-pause and bulk-resume.
Request
curl -X POST "https://api.ken.so/v1/campaigns/812/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 '{
  "contacts": [
    {
      "email": "[email protected]",
      "firstName": "string",
      "lastName": "string",
      "title": "string",
      "linkedInUrl": "https://example.com",
      "companyName": "string",
      "companyDomain": "example.com",
      "customImageUrl": "https://example.com",
      "customVariables": {
        "key": "string"
      },
      "emailValidity": "unknown"
    }
  ]
}'
Response 200
{
  "success": true,
  "data": {
    "succeeded": [
      1
    ],
    "failed": [
      {
        "id": 812,
        "index": 1,
        "error": {
          "code": "string",
          "message": "string"
        }
      }
    ],
    "results": [
      {
        "index": 1,
        "status": "string",
        "id": 812,
        "reason": "string",
        "message": "string",
        "sendable": true
      }
    ]
  }
}

Ordered by id. Requires ReadAccess. Each contact on the page counts one row of the workspace's apiReadRows quota; past it, 429 quota_exceeded with Retry-After.

Parameters

Name
In
Type
Required
Description
CampaignId
In: query
integer (int64)
optional
CompanyId
In: query
integer (int64)
optional
SegmentId
In: query
integer (int64)
optional
Email
In: query
string
optional
Q
In: query
string
optional
IsQualified
In: query
boolean
optional
DoNotContact
In: query
boolean
optional
CreatedAfter
In: query
string (date-time)
optional
CreatedBefore
In: query
string (date-time)
optional
Limit
In: query
integer (int32)
optional
Cursor
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 list of ContactModel. Each item has these fields. The response also carries pagination.

Field
Type
Description
id
integer (int64)
campaignId
integer (int64)
segmentId
integer (int64)
campaignListId
integer (int64)
companyId
integer (int64)
companyName
string
companyEmailName
string
firstName
string
lastName
string
email
string
linkedInUrl
string
profileUrn
string
isOpenLink
boolean
jsonGeoLocation
string
title
string
isQualified
boolean
doNotContact
boolean
sequenceStoppedAt
string (date-time)
When this contact's sequence in its campaign was stopped (not DNC). Absent while it runs.
sequenceStopReason
string
Why the sequence stopped, as stored: paused (a member paused the lead), account_play, or a later value. Absent while it runs.
isDecisionMaker
boolean
qualityScore
integer (int32)
qualityReasoning
string
profileImage
string
qualificationResponse
string
segmentationReasoning
string
experiencesJson
string
customVariables
map of string
emailsSent
integer (int32)
clicks
integer (int32)
replies
integer (int32)
unsubscribes
integer (int32)
bounces
integer (int32)
positiveReplies
integer (int32)
connectionRequestsSent
integer (int32)
LinkedIn connection requests sent to this contact (confirmed only). The four LinkedIn counters are omitted when the contact has no LinkedIn lane and no confirmed LinkedIn send, like the email stats.
connectionsAccepted
integer (int32)
LinkedIn lanes where the contact accepted Kenso's connection request.
linkedInMessagesSent
integer (int32)
LinkedIn messages and InMails sent to this contact (confirmed only).
linkedInReplies
integer (int32)
LinkedIn lanes where the contact replied on LinkedIn.
headline
string
location
string
city
string
state
string
country
string
countryIso2
string
companyLogo
string
emailEnrichmentProviders
array of string
linkedInUrlNameMismatch
boolean
segmentName
string
missingRequiredVariableKeys
array of string
emailValidity
string, one of: unknown, invalid, risky, verified, safe, catch_all, verification_error
emailFromKenPool
boolean
True when the email Ken.Scraper.API.Canonical.ContactModel.EmailValidity describes came from Kenso's email database, free. Absent without a Safe or Verified email.
companyDomain
string
companySize
string
companyType
string
companyIndustry
string
companyLocation
string
companyTechnologies
string
companyLinkedInUrl
string
companyLinkedInIdent
string
companyEmployeeCount
integer (int32)
companySpecialities
string
companyDescription
string
companyCrunchBaseUrl
string
companyJsonLocation
string
companyFoundedYear
integer (int32)
headquartersCity
string
headquartersState
string
headquartersCountry
string
seniorityLevel
string
function
string
yearsOfExperience
number (float)
languages
string
outreachLanguage
string
The locale Ken writes to this contact in (fr-FR; a bare ISO 639-1 code for a language with no market pack). Omitted until Ken resolves one. PATCH sets it by hand and locks it.
outreachFormality
string, one of: formal, informal
outreachLanguageSource
string, one of: manual, reply, declared, country, hq, default
outreachLanguageConfidence
number (float)
0 to 1: 1 for a language a person set, 0 for the campaign's source language.
outreachLanguageLocked
boolean
True when a person set the language: no import, enrichment or reply replaces it.
phoneNumber
string
phoneNumberValidity
string, one of: unknown, invalid, risky, verified, valid
phoneCountryAssumed
boolean
True when Kenso added the phone number's country code itself: a number typed without one was read as a US number (+1). Unverified; the app shows a warning next to it.
lastJobTitle
string
lastJobDescription
string
employmentType
string
employmentLocation
string
summary
string
skills
string
education
string
createdAt
string (date-time)
updatedAt
string (date-time)
accountPlay
ContactAccountPlayModel
A contact's account-play status in its campaign.
securityGateway
ContactSecurityGatewayModel
The email security gateway in front of a contact's email.
mailboxProvider
ContactMailboxProviderModel
The host of a contact's mailbox (for example Google Workspace or Microsoft 365).
Request
curl -X GET "https://api.ken.so/v1/contacts" \
  -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": 90125,
      "campaignId": 4821,
      "segmentId": 1207,
      "campaignListId": 3305,
      "companyId": 22841,
      "companyName": "Northwind Labs",
      "companyEmailName": "Northwind",
      "firstName": "Amanda",
      "lastName": "Chen",
      "email": "[email protected]",
      "linkedInUrl": "https://www.linkedin.com/in/amanda-chen-northwind",
      "title": "VP of Engineering",
      "isQualified": true,
      "doNotContact": false,
      "isDecisionMaker": true,
      "qualityScore": 86,
      "qualityReasoning": "Leads a 40-person engineering team at a 120-person B2B SaaS company that is hiring engineers.",
      "segmentationReasoning": "VP of Engineering, which matches the Engineering leaders segment.",
      "customVariables": {
        "open_roles": "12 engineering roles"
      },
      "emailsSent": 2,
      "clicks": 0,
      "replies": 1,
      "unsubscribes": 0,
      "bounces": 0,
      "positiveReplies": 1,
      "headline": "VP of Engineering at Northwind Labs",
      "location": "Austin, Texas, United States",
      "city": "Austin",
      "state": "Texas",
      "country": "United States",
      "countryIso2": "US",
      "companyLogo": "https://cdn.example.com/companies/22841/logo.png",
      "linkedInUrlNameMismatch": false,
      "segmentName": "Engineering leaders",
      "emailValidity": "safe",
      "emailFromKenPool": false,
      "companyDomain": "northwind.example.com",
      "companySize": "51-200",
      "companyIndustry": "Software Development",
      "companyLocation": "Austin, Texas, United States",
      "companyEmployeeCount": 120,
      "companyDescription": "Northwind Labs builds developer onboarding software for engineering teams.",
      "companyFoundedYear": 2017,
      "headquartersCity": "Austin",
      "headquartersState": "Texas",
      "headquartersCountry": "United States",
      "seniorityLevel": "VP",
      "function": "Engineering",
      "yearsOfExperience": 14,
      "outreachLanguage": "en-US",
      "outreachLanguageSource": "country",
      "outreachLanguageConfidence": 0.8,
      "outreachLanguageLocked": false,
      "phoneCountryAssumed": false,
      "createdAt": "2026-09-23T15:02:11Z",
      "updatedAt": "2026-10-13T16:22:05Z"
    }
  ],
  "pagination": {
    "nextCursor": "eyJpZCI6ODE1fQ",
    "hasMore": true,
    "total": 1,
    "isTotalCapped": true
  }
}

Same filters as the list, plus more than one campaignIds value, lead filters groups, readyOnly, and contactIds. q is the free-text search. Ordered by id unless sort is set, in which case the cursor is an offset into that order. No total count. readyOnly requires exactly one campaign id. securityGateway and mailboxProvider filter on the contact's first Safe or Verified email and need security gateways on the workspace (400 otherwise). Requires ReadAccess. Pages count on apiReadRows like the list.

Parameters

Name
In
Type
Required
Description
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
campaignIds
array of integer (int64)
optional
companyId
integer (int64)
optional
segmentId
integer (int64)
optional
email
string
optional
q
string
optional
isQualified
boolean
optional
doNotContact
boolean
optional
createdAfter
string (date-time)
optional
createdBefore
string (date-time)
optional
filters
array of FilterGroup
optional
readyOnly
boolean
optional
contactIds
array of integer (int64)
optional
securityGateway
array of string
optional
Contacts whose first Safe or Verified email sits behind one of these security gateways. Catalog slugs (for example proofpoint), none or pending; at most 100 values, OR'ed. Needs security gateways on the workspace.
mailboxProvider
array of string
optional
Contacts whose mailbox host is one of these. Catalog slugs (for example google_workspace), unknown or pending; at most 100 values, OR'ed. Needs security gateways on the workspace.
sort
string
optional
Allowlisted lead column. Same names as POST /v1/leads.
direction
string
optional
asc or desc. Empty means ascending.
limit
integer (int32)
optional
cursor
string
optional

Response 200

data is a list of ContactModel. Each item has these fields. The response also carries pagination.

Field
Type
Description
id
integer (int64)
campaignId
integer (int64)
segmentId
integer (int64)
campaignListId
integer (int64)
companyId
integer (int64)
companyName
string
companyEmailName
string
firstName
string
lastName
string
email
string
linkedInUrl
string
profileUrn
string
isOpenLink
boolean
jsonGeoLocation
string
title
string
isQualified
boolean
doNotContact
boolean
sequenceStoppedAt
string (date-time)
When this contact's sequence in its campaign was stopped (not DNC). Absent while it runs.
sequenceStopReason
string
Why the sequence stopped, as stored: paused (a member paused the lead), account_play, or a later value. Absent while it runs.
isDecisionMaker
boolean
qualityScore
integer (int32)
qualityReasoning
string
profileImage
string
qualificationResponse
string
segmentationReasoning
string
experiencesJson
string
customVariables
map of string
emailsSent
integer (int32)
clicks
integer (int32)
replies
integer (int32)
unsubscribes
integer (int32)
bounces
integer (int32)
positiveReplies
integer (int32)
connectionRequestsSent
integer (int32)
LinkedIn connection requests sent to this contact (confirmed only). The four LinkedIn counters are omitted when the contact has no LinkedIn lane and no confirmed LinkedIn send, like the email stats.
connectionsAccepted
integer (int32)
LinkedIn lanes where the contact accepted Kenso's connection request.
linkedInMessagesSent
integer (int32)
LinkedIn messages and InMails sent to this contact (confirmed only).
linkedInReplies
integer (int32)
LinkedIn lanes where the contact replied on LinkedIn.
headline
string
location
string
city
string
state
string
country
string
countryIso2
string
companyLogo
string
emailEnrichmentProviders
array of string
linkedInUrlNameMismatch
boolean
segmentName
string
missingRequiredVariableKeys
array of string
emailValidity
string, one of: unknown, invalid, risky, verified, safe, catch_all, verification_error
emailFromKenPool
boolean
True when the email Ken.Scraper.API.Canonical.ContactModel.EmailValidity describes came from Kenso's email database, free. Absent without a Safe or Verified email.
companyDomain
string
companySize
string
companyType
string
companyIndustry
string
companyLocation
string
companyTechnologies
string
companyLinkedInUrl
string
companyLinkedInIdent
string
companyEmployeeCount
integer (int32)
companySpecialities
string
companyDescription
string
companyCrunchBaseUrl
string
companyJsonLocation
string
companyFoundedYear
integer (int32)
headquartersCity
string
headquartersState
string
headquartersCountry
string
seniorityLevel
string
function
string
yearsOfExperience
number (float)
languages
string
outreachLanguage
string
The locale Ken writes to this contact in (fr-FR; a bare ISO 639-1 code for a language with no market pack). Omitted until Ken resolves one. PATCH sets it by hand and locks it.
outreachFormality
string, one of: formal, informal
outreachLanguageSource
string, one of: manual, reply, declared, country, hq, default
outreachLanguageConfidence
number (float)
0 to 1: 1 for a language a person set, 0 for the campaign's source language.
outreachLanguageLocked
boolean
True when a person set the language: no import, enrichment or reply replaces it.
phoneNumber
string
phoneNumberValidity
string, one of: unknown, invalid, risky, verified, valid
phoneCountryAssumed
boolean
True when Kenso added the phone number's country code itself: a number typed without one was read as a US number (+1). Unverified; the app shows a warning next to it.
lastJobTitle
string
lastJobDescription
string
employmentType
string
employmentLocation
string
summary
string
skills
string
education
string
createdAt
string (date-time)
updatedAt
string (date-time)
accountPlay
ContactAccountPlayModel
A contact's account-play status in its campaign.
securityGateway
ContactSecurityGatewayModel
The email security gateway in front of a contact's email.
mailboxProvider
ContactMailboxProviderModel
The host of a contact's mailbox (for example Google Workspace or Microsoft 365).
Request
curl -X POST "https://api.ken.so/v1/contacts/search" \
  -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 '{
  "campaignIds": [
    812
  ],
  "companyId": 812,
  "segmentId": 812,
  "email": "[email protected]",
  "q": "string",
  "isQualified": true,
  "doNotContact": true,
  "createdAfter": "2026-09-23T15:02:11Z",
  "createdBefore": "2026-09-23T15:02:11Z",
  "filters": [
    {
      "operator": 0,
      "conditions": {
        "field": "string",
        "operator": 0,
        "value": null,
        "dataType": 0
      },
      "children": [
        {}
      ]
    }
  ],
  "readyOnly": true,
  "contactIds": [
    812
  ],
  "securityGateway": [
    "string"
  ],
  "mailboxProvider": [
    "string"
  ],
  "sort": "string",
  "direction": "string",
  "limit": 1,
  "cursor": "eyJpZCI6ODE1fQ"
}'
Response 200
{
  "success": true,
  "data": [
    {
      "id": 90125,
      "campaignId": 4821,
      "segmentId": 1207,
      "campaignListId": 3305,
      "companyId": 22841,
      "companyName": "Northwind Labs",
      "companyEmailName": "Northwind",
      "firstName": "Amanda",
      "lastName": "Chen",
      "email": "[email protected]",
      "linkedInUrl": "https://www.linkedin.com/in/amanda-chen-northwind",
      "title": "VP of Engineering",
      "isQualified": true,
      "doNotContact": false,
      "isDecisionMaker": true,
      "qualityScore": 86,
      "qualityReasoning": "Leads a 40-person engineering team at a 120-person B2B SaaS company that is hiring engineers.",
      "segmentationReasoning": "VP of Engineering, which matches the Engineering leaders segment.",
      "customVariables": {
        "open_roles": "12 engineering roles"
      },
      "emailsSent": 2,
      "clicks": 0,
      "replies": 1,
      "unsubscribes": 0,
      "bounces": 0,
      "positiveReplies": 1,
      "headline": "VP of Engineering at Northwind Labs",
      "location": "Austin, Texas, United States",
      "city": "Austin",
      "state": "Texas",
      "country": "United States",
      "countryIso2": "US",
      "companyLogo": "https://cdn.example.com/companies/22841/logo.png",
      "linkedInUrlNameMismatch": false,
      "segmentName": "Engineering leaders",
      "emailValidity": "safe",
      "emailFromKenPool": false,
      "companyDomain": "northwind.example.com",
      "companySize": "51-200",
      "companyIndustry": "Software Development",
      "companyLocation": "Austin, Texas, United States",
      "companyEmployeeCount": 120,
      "companyDescription": "Northwind Labs builds developer onboarding software for engineering teams.",
      "companyFoundedYear": 2017,
      "headquartersCity": "Austin",
      "headquartersState": "Texas",
      "headquartersCountry": "United States",
      "seniorityLevel": "VP",
      "function": "Engineering",
      "yearsOfExperience": 14,
      "outreachLanguage": "en-US",
      "outreachLanguageSource": "country",
      "outreachLanguageConfidence": 0.8,
      "outreachLanguageLocked": false,
      "phoneCountryAssumed": false,
      "createdAt": "2026-09-23T15:02:11Z",
      "updatedAt": "2026-10-13T16:22:05Z"
    }
  ],
  "pagination": {
    "nextCursor": "eyJpZCI6ODE1fQ",
    "hasMore": true,
    "total": 1,
    "isTotalCapped": true
  }
}

Parameters

Name
In
Type
Required
Description
contactId
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 ContactModel.

Field
Type
Description
id
integer (int64)
campaignId
integer (int64)
segmentId
integer (int64)
campaignListId
integer (int64)
companyId
integer (int64)
companyName
string
companyEmailName
string
firstName
string
lastName
string
email
string
linkedInUrl
string
profileUrn
string
isOpenLink
boolean
jsonGeoLocation
string
title
string
isQualified
boolean
doNotContact
boolean
sequenceStoppedAt
string (date-time)
When this contact's sequence in its campaign was stopped (not DNC). Absent while it runs.
sequenceStopReason
string
Why the sequence stopped, as stored: paused (a member paused the lead), account_play, or a later value. Absent while it runs.
isDecisionMaker
boolean
qualityScore
integer (int32)
qualityReasoning
string
profileImage
string
qualificationResponse
string
segmentationReasoning
string
experiencesJson
string
customVariables
map of string
emailsSent
integer (int32)
clicks
integer (int32)
replies
integer (int32)
unsubscribes
integer (int32)
bounces
integer (int32)
positiveReplies
integer (int32)
connectionRequestsSent
integer (int32)
LinkedIn connection requests sent to this contact (confirmed only). The four LinkedIn counters are omitted when the contact has no LinkedIn lane and no confirmed LinkedIn send, like the email stats.
connectionsAccepted
integer (int32)
LinkedIn lanes where the contact accepted Kenso's connection request.
linkedInMessagesSent
integer (int32)
LinkedIn messages and InMails sent to this contact (confirmed only).
linkedInReplies
integer (int32)
LinkedIn lanes where the contact replied on LinkedIn.
headline
string
location
string
city
string
state
string
country
string
countryIso2
string
companyLogo
string
emailEnrichmentProviders
array of string
linkedInUrlNameMismatch
boolean
segmentName
string
missingRequiredVariableKeys
array of string
emailValidity
string, one of: unknown, invalid, risky, verified, safe, catch_all, verification_error
emailFromKenPool
boolean
True when the email Ken.Scraper.API.Canonical.ContactModel.EmailValidity describes came from Kenso's email database, free. Absent without a Safe or Verified email.
companyDomain
string
companySize
string
companyType
string
companyIndustry
string
companyLocation
string
companyTechnologies
string
companyLinkedInUrl
string
companyLinkedInIdent
string
companyEmployeeCount
integer (int32)
companySpecialities
string
companyDescription
string
companyCrunchBaseUrl
string
companyJsonLocation
string
companyFoundedYear
integer (int32)
headquartersCity
string
headquartersState
string
headquartersCountry
string
seniorityLevel
string
function
string
yearsOfExperience
number (float)
languages
string
outreachLanguage
string
The locale Ken writes to this contact in (fr-FR; a bare ISO 639-1 code for a language with no market pack). Omitted until Ken resolves one. PATCH sets it by hand and locks it.
outreachFormality
string, one of: formal, informal
outreachLanguageSource
string, one of: manual, reply, declared, country, hq, default
outreachLanguageConfidence
number (float)
0 to 1: 1 for a language a person set, 0 for the campaign's source language.
outreachLanguageLocked
boolean
True when a person set the language: no import, enrichment or reply replaces it.
phoneNumber
string
phoneNumberValidity
string, one of: unknown, invalid, risky, verified, valid
phoneCountryAssumed
boolean
True when Kenso added the phone number's country code itself: a number typed without one was read as a US number (+1). Unverified; the app shows a warning next to it.
lastJobTitle
string
lastJobDescription
string
employmentType
string
employmentLocation
string
summary
string
skills
string
education
string
createdAt
string (date-time)
updatedAt
string (date-time)
accountPlay
ContactAccountPlayModel
A contact's account-play status in its campaign.
securityGateway
ContactSecurityGatewayModel
The email security gateway in front of a contact's email.
mailboxProvider
ContactMailboxProviderModel
The host of a contact's mailbox (for example Google Workspace or Microsoft 365).
Request
curl -X GET "https://api.ken.so/v1/contacts/812" \
  -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": 90125,
    "campaignId": 4821,
    "segmentId": 1207,
    "campaignListId": 3305,
    "companyId": 22841,
    "companyName": "Northwind Labs",
    "companyEmailName": "Northwind",
    "firstName": "Amanda",
    "lastName": "Chen",
    "email": "[email protected]",
    "linkedInUrl": "https://www.linkedin.com/in/amanda-chen-northwind",
    "title": "VP of Engineering",
    "isQualified": true,
    "doNotContact": false,
    "isDecisionMaker": true,
    "qualityScore": 86,
    "qualityReasoning": "Leads a 40-person engineering team at a 120-person B2B SaaS company that is hiring engineers.",
    "segmentationReasoning": "VP of Engineering, which matches the Engineering leaders segment.",
    "customVariables": {
      "open_roles": "12 engineering roles"
    },
    "emailsSent": 2,
    "clicks": 0,
    "replies": 1,
    "unsubscribes": 0,
    "bounces": 0,
    "positiveReplies": 1,
    "headline": "VP of Engineering at Northwind Labs",
    "location": "Austin, Texas, United States",
    "city": "Austin",
    "state": "Texas",
    "country": "United States",
    "countryIso2": "US",
    "companyLogo": "https://cdn.example.com/companies/22841/logo.png",
    "linkedInUrlNameMismatch": false,
    "segmentName": "Engineering leaders",
    "emailValidity": "safe",
    "emailFromKenPool": false,
    "companyDomain": "northwind.example.com",
    "companySize": "51-200",
    "companyIndustry": "Software Development",
    "companyLocation": "Austin, Texas, United States",
    "companyEmployeeCount": 120,
    "companyDescription": "Northwind Labs builds developer onboarding software for engineering teams.",
    "companyFoundedYear": 2017,
    "headquartersCity": "Austin",
    "headquartersState": "Texas",
    "headquartersCountry": "United States",
    "seniorityLevel": "VP",
    "function": "Engineering",
    "yearsOfExperience": 14,
    "outreachLanguage": "en-US",
    "outreachLanguageSource": "country",
    "outreachLanguageConfidence": 0.8,
    "outreachLanguageLocked": false,
    "phoneCountryAssumed": false,
    "createdAt": "2026-09-23T15:02:11Z",
    "updatedAt": "2026-10-13T16:22:05Z"
  }
}

Update fields on one contact

PATCH/v1/contacts/{contactId}contacts.update

Try it on developer.ken.so

Absent fields stay unchanged. Null clears a string.

Requires WriteAccess, matching the legacy contact update. outreachLanguage (a locale whose language has a market pack or a country entry, such as fr-FR, es-MX or de) sets the language by hand: source manual, confidence 1, locked, so no import, enrichment or reply replaces it. Null clears and unlocks it. customVariables (an object of string or null values) merges into the contact's custom variables with the campaign lead upsert's rules: a string sets a key, null or an empty string removes it, keys not sent stay, and a new key joins the campaign's catalog. Absent leaves the bag unchanged; the object itself cannot be null. customImageUrl (a public https URL of at most 2048 characters) is the lead's own image, shown in emails as {{custom_image}}: Kenso downloads and hosts it. Empty or null clears it. It needs campaign images on the workspace; otherwise the update is a 400 validation_failed.

Parameters

Name
In
Type
Required
Description
contactId
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, required.

Field
Type
Required
Description
firstName
string
optional
First name. Null clears it.
lastName
string
optional
Last name. Null clears it.
title
string
optional
Job title. Null clears it.
linkedInUrl
string
optional
LinkedIn profile URL. Null clears it.
location
string
optional
Location. Null clears it.
email
string
optional
Primary email address. Null removes it; an invalid address is refused.
isQualified
boolean
optional
Whether the contact is qualified. Must be true or false: null is refused.
doNotContact
boolean
optional
Whether the contact must never be contacted. Must be true or false: null is refused.
outreachLanguage
string
optional
Outreach language set by hand: a locale whose language has a market pack or a country entry, such as fr-FR, es-MX or de. It is locked, so no import, enrichment or reply replaces it. Null clears and unlocks it.
customVariables
map of string
optional
Merge into the contact's custom variables. A null or empty value removes that key. Omit to leave them unchanged.
customImageUrl
string
optional
The lead's own image, shown in emails as {{custom_image}}: a public https URL of at most 2048 characters that Kenso downloads and hosts. Empty or null clears it. Needs campaign images on the workspace.

Response 200

data is a ContactModel.

Field
Type
Description
id
integer (int64)
campaignId
integer (int64)
segmentId
integer (int64)
campaignListId
integer (int64)
companyId
integer (int64)
companyName
string
companyEmailName
string
firstName
string
lastName
string
email
string
linkedInUrl
string
profileUrn
string
isOpenLink
boolean
jsonGeoLocation
string
title
string
isQualified
boolean
doNotContact
boolean
sequenceStoppedAt
string (date-time)
When this contact's sequence in its campaign was stopped (not DNC). Absent while it runs.
sequenceStopReason
string
Why the sequence stopped, as stored: paused (a member paused the lead), account_play, or a later value. Absent while it runs.
isDecisionMaker
boolean
qualityScore
integer (int32)
qualityReasoning
string
profileImage
string
qualificationResponse
string
segmentationReasoning
string
experiencesJson
string
customVariables
map of string
emailsSent
integer (int32)
clicks
integer (int32)
replies
integer (int32)
unsubscribes
integer (int32)
bounces
integer (int32)
positiveReplies
integer (int32)
connectionRequestsSent
integer (int32)
LinkedIn connection requests sent to this contact (confirmed only). The four LinkedIn counters are omitted when the contact has no LinkedIn lane and no confirmed LinkedIn send, like the email stats.
connectionsAccepted
integer (int32)
LinkedIn lanes where the contact accepted Kenso's connection request.
linkedInMessagesSent
integer (int32)
LinkedIn messages and InMails sent to this contact (confirmed only).
linkedInReplies
integer (int32)
LinkedIn lanes where the contact replied on LinkedIn.
headline
string
location
string
city
string
state
string
country
string
countryIso2
string
companyLogo
string
emailEnrichmentProviders
array of string
linkedInUrlNameMismatch
boolean
segmentName
string
missingRequiredVariableKeys
array of string
emailValidity
string, one of: unknown, invalid, risky, verified, safe, catch_all, verification_error
emailFromKenPool
boolean
True when the email Ken.Scraper.API.Canonical.ContactModel.EmailValidity describes came from Kenso's email database, free. Absent without a Safe or Verified email.
companyDomain
string
companySize
string
companyType
string
companyIndustry
string
companyLocation
string
companyTechnologies
string
companyLinkedInUrl
string
companyLinkedInIdent
string
companyEmployeeCount
integer (int32)
companySpecialities
string
companyDescription
string
companyCrunchBaseUrl
string
companyJsonLocation
string
companyFoundedYear
integer (int32)
headquartersCity
string
headquartersState
string
headquartersCountry
string
seniorityLevel
string
function
string
yearsOfExperience
number (float)
languages
string
outreachLanguage
string
The locale Ken writes to this contact in (fr-FR; a bare ISO 639-1 code for a language with no market pack). Omitted until Ken resolves one. PATCH sets it by hand and locks it.
outreachFormality
string, one of: formal, informal
outreachLanguageSource
string, one of: manual, reply, declared, country, hq, default
outreachLanguageConfidence
number (float)
0 to 1: 1 for a language a person set, 0 for the campaign's source language.
outreachLanguageLocked
boolean
True when a person set the language: no import, enrichment or reply replaces it.
phoneNumber
string
phoneNumberValidity
string, one of: unknown, invalid, risky, verified, valid
phoneCountryAssumed
boolean
True when Kenso added the phone number's country code itself: a number typed without one was read as a US number (+1). Unverified; the app shows a warning next to it.
lastJobTitle
string
lastJobDescription
string
employmentType
string
employmentLocation
string
summary
string
skills
string
education
string
createdAt
string (date-time)
updatedAt
string (date-time)
accountPlay
ContactAccountPlayModel
A contact's account-play status in its campaign.
securityGateway
ContactSecurityGatewayModel
The email security gateway in front of a contact's email.
mailboxProvider
ContactMailboxProviderModel
The host of a contact's mailbox (for example Google Workspace or Microsoft 365).
Request
curl -X PATCH "https://api.ken.so/v1/contacts/812" \
  -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 '{
  "firstName": "string",
  "lastName": "string",
  "title": "string",
  "linkedInUrl": "https://example.com",
  "location": "string",
  "email": "[email protected]",
  "isQualified": true,
  "doNotContact": true,
  "outreachLanguage": "string",
  "customVariables": {
    "key": "string"
  },
  "customImageUrl": "https://example.com"
}'
Response 200
{
  "success": true,
  "data": {
    "id": 90125,
    "campaignId": 4821,
    "segmentId": 1207,
    "campaignListId": 3305,
    "companyId": 22841,
    "companyName": "Northwind Labs",
    "companyEmailName": "Northwind",
    "firstName": "Amanda",
    "lastName": "Chen",
    "email": "[email protected]",
    "linkedInUrl": "https://www.linkedin.com/in/amanda-chen-northwind",
    "title": "VP of Engineering",
    "isQualified": true,
    "doNotContact": false,
    "isDecisionMaker": true,
    "qualityScore": 86,
    "qualityReasoning": "Leads a 40-person engineering team at a 120-person B2B SaaS company that is hiring engineers.",
    "segmentationReasoning": "VP of Engineering, which matches the Engineering leaders segment.",
    "customVariables": {
      "open_roles": "12 engineering roles"
    },
    "emailsSent": 2,
    "clicks": 0,
    "replies": 1,
    "unsubscribes": 0,
    "bounces": 0,
    "positiveReplies": 1,
    "headline": "VP of Engineering at Northwind Labs",
    "location": "Austin, Texas, United States",
    "city": "Austin",
    "state": "Texas",
    "country": "United States",
    "countryIso2": "US",
    "companyLogo": "https://cdn.example.com/companies/22841/logo.png",
    "linkedInUrlNameMismatch": false,
    "segmentName": "Engineering leaders",
    "emailValidity": "safe",
    "emailFromKenPool": false,
    "companyDomain": "northwind.example.com",
    "companySize": "51-200",
    "companyIndustry": "Software Development",
    "companyLocation": "Austin, Texas, United States",
    "companyEmployeeCount": 120,
    "companyDescription": "Northwind Labs builds developer onboarding software for engineering teams.",
    "companyFoundedYear": 2017,
    "headquartersCity": "Austin",
    "headquartersState": "Texas",
    "headquartersCountry": "United States",
    "seniorityLevel": "VP",
    "function": "Engineering",
    "yearsOfExperience": 14,
    "outreachLanguage": "en-US",
    "outreachLanguageSource": "country",
    "outreachLanguageConfidence": 0.8,
    "outreachLanguageLocked": false,
    "phoneCountryAssumed": false,
    "createdAt": "2026-09-23T15:02:11Z",
    "updatedAt": "2026-10-13T16:22:05Z"
  }
}

Soft-delete one contact

DELETE/v1/contacts/{contactId}contacts.delete

Try it on developer.ken.so

Requires WriteAccess, matching the legacy contact delete.

Parameters

Name
In
Type
Required
Description
contactId
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/contacts/812" \
  -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

Soft-delete up to 1,000 contacts by id

POST/v1/contacts/bulk-deletecontacts.bulk-delete

Try it on developer.ken.so

Requires ReadAccess, matching POST /v1/leads/bulk-delete. Ids outside the workspace are reported in failed and are not deleted.

Parameters

Name
In
Type
Required
Description
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
ids
array of integer (int64)
optional

Response 200

data is a ContactBulkResult.

Field
Type
Description
succeeded
array of integer (int64)
failed
array of ContactBulkFailure
results
array of ContactCreateRowResult
One item per input row of contacts.create, in request order. Only that route fills it, so it is absent from the JSON of bulk-delete, bulk-pause and bulk-resume.
Request
curl -X POST "https://api.ken.so/v1/contacts/bulk-delete" \
  -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 '{
  "ids": [
    812
  ]
}'
Response 200
{
  "success": true,
  "data": {
    "succeeded": [
      1
    ],
    "failed": [
      {
        "id": 812,
        "index": 1,
        "error": {
          "code": "string",
          "message": "string"
        }
      }
    ],
    "results": [
      {
        "index": 1,
        "status": "string",
        "id": 812,
        "reason": "string",
        "message": "string",
        "sendable": true
      }
    ]
  }
}

Pause the campaign sequence of up to 1,000 contacts by id

POST/v1/contacts/bulk-pausecontacts.bulk-pause

Needs the pause_leads feature on your workspace. Until it is on, calls answer 404.

Try it on developer.ken.so

Stops each contact's sequence in its campaign with the stop reason paused (sequenceStoppedAt and sequenceStopReason on the contact). This is not do-not-contact: the contact stays in other campaigns. Pending unsent steps of the paused contacts are cancelled. succeeded lists the ids that are paused after this call, a lead that was already paused included, so a retry is safe. Each other id is in failed with already_stopped (another stop reason holds it; the first stop wins), do_not_contact, not_found (not in this workspace, deleted, or not in a campaign), or internal_error (retry that id). Requires campaigns edit and launch (pausing stops live sends, as a campaign pause does) and the pause_leads feature; without it the route is 404.

Parameters

Name
In
Type
Required
Description
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
ids
array of integer (int64)
optional
1 to 1,000 contact ids in the workspace.

Response 200

data is a ContactBulkResult.

Field
Type
Description
succeeded
array of integer (int64)
failed
array of ContactBulkFailure
results
array of ContactCreateRowResult
One item per input row of contacts.create, in request order. Only that route fills it, so it is absent from the JSON of bulk-delete, bulk-pause and bulk-resume.
Request
curl -X POST "https://api.ken.so/v1/contacts/bulk-pause" \
  -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 '{
  "ids": [
    812
  ]
}'
Response 200
{
  "success": true,
  "data": {
    "succeeded": [
      1
    ],
    "failed": [
      {
        "id": 812,
        "index": 1,
        "error": {
          "code": "string",
          "message": "string"
        }
      }
    ],
    "results": [
      {
        "index": 1,
        "status": "string",
        "id": 812,
        "reason": "string",
        "message": "string",
        "sendable": true
      }
    ]
  }
}

Clears the stop only where sequenceStopReason is paused, then asks the planner to plan each affected campaign now. A held LinkedIn step picks up within the hour. succeeded lists the ids this call resumed. Each other id is in failed with not_paused (no stop, or a stop with another reason, which stays) or not_found. Requires campaigns edit and launch, as bulk-pause. Not behind pause_leads: clearing a pause is always safe, so leads paused before the flag was turned off can still be resumed.

Parameters

Name
In
Type
Required
Description
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
ids
array of integer (int64)
optional
1 to 1,000 contact ids in the workspace.

Response 200

data is a ContactBulkResult.

Field
Type
Description
succeeded
array of integer (int64)
failed
array of ContactBulkFailure
results
array of ContactCreateRowResult
One item per input row of contacts.create, in request order. Only that route fills it, so it is absent from the JSON of bulk-delete, bulk-pause and bulk-resume.
Request
curl -X POST "https://api.ken.so/v1/contacts/bulk-resume" \
  -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 '{
  "ids": [
    812
  ]
}'
Response 200
{
  "success": true,
  "data": {
    "succeeded": [
      1
    ],
    "failed": [
      {
        "id": 812,
        "index": 1,
        "error": {
          "code": "string",
          "message": "string"
        }
      }
    ],
    "results": [
      {
        "index": 1,
        "status": "string",
        "id": 812,
        "reason": "string",
        "message": "string",
        "sendable": true
      }
    ]
  }
}

Each item is a contactId plus any fields PATCH /v1/contacts/{contactId} accepts, with the same semantics: absent fields stay unchanged, null clears a string. Items run one at a time in request order. A contact outside the workspace is not_found and is not written. A field error fails only that item. results is in request order; status is updated, not_found, validation_failed, or failed. The whole request is rejected, with nothing written, when items is empty, has more than 1,000 entries, has an entry that is not an object or lacks a positive contactId, or repeats a contactId. An item's customVariables merges as on the single update. Requires WriteAccess, matching the single update.

Parameters

Name
In
Type
Required
Description
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, required.

Field
Type
Required
Description
items
array of BulkUpdateContactItemBody
optional
Required. The contacts to update, in request order.

Response 200

data is a ContactBulkUpdateResult.

Field
Type
Description
updated
integer (int32)
failed
integer (int32)
results
array of ContactBulkUpdateItemResult
Request
curl -X POST "https://api.ken.so/v1/contacts/bulk-update" \
  -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 '{
  "items": [
    {
      "firstName": "string",
      "lastName": "string",
      "title": "string",
      "linkedInUrl": "https://example.com",
      "location": "string",
      "email": "[email protected]",
      "isQualified": true,
      "doNotContact": true,
      "outreachLanguage": "string",
      "customVariables": {
        "key": "string"
      },
      "customImageUrl": "https://example.com",
      "contactId": 812
    }
  ]
}'
Response 200
{
  "success": true,
  "data": {
    "updated": 1,
    "failed": 1,
    "results": [
      {
        "contactId": 812,
        "status": "string",
        "error": "string"
      }
    ]
  }
}

List events for one contact

GET/v1/contacts/{contactId}/eventscontacts.events

Try it on developer.ken.so

Event ids are the stored event id, which is a string. Requires ReadAccess. A cursor is valid only with the same limit that issued it.

Parameters

Name
In
Type
Required
Description
contactId
In: path
integer (int64)
required
Type
In: query
string
optional
OccurredAfter
In: query
string (date-time)
optional
OccurredBefore
In: query
string (date-time)
optional
Limit
In: query
integer (int32)
optional
Cursor
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 list of ContactEventModel. Each item has these fields. The response also carries pagination.

Field
Type
Description
id
string
type
string
occurredAt
string (date-time)
campaignId
integer (int64)
kind
string
Which video activity a video_event row is, as its VideoEventKindNames wire name (video_viewed, video_played, video_watched, video_liked or video_cta_clicked). Set only on video_event rows; omitted on every other row.
Request
curl -X GET "https://api.ken.so/v1/contacts/812/events" \
  -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",
      "type": "string",
      "occurredAt": "2026-09-23T15:02:11Z",
      "campaignId": 812,
      "kind": "string"
    }
  ],
  "pagination": {
    "nextCursor": "eyJpZCI6ODE1fQ",
    "hasMore": true,
    "total": 1,
    "isTotalCapped": true
  }
}

List every scraped website page of the contact's company

GET/v1/contacts/{contactId}/website-pagescontacts.list-website-pages

Try it on developer.ken.so

One entry per distinct page with its markdown and scrape date. source is archive for pages copied from the Common Crawl archive (then scrapedAt is the archive capture date), live for pages Kenso scraped, and imported for content supplied in a CSV upload (then scrapedAt is the upload time). Ordered home, about, pricing, products, customer pages, careers, then the rest; newest first within a type. Pages without content are left out. At most 50 pages, not paginated. Requires ReadAccess.

Parameters

Name
In
Type
Required
Description
contactId
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 list of ContactWebsitePageModel. Each item has these fields. The response also carries pagination.

Field
Type
Description
pageType
string
Page type: home, about, pricing, contact, blog, blog_articles, services, products, testimonials, case_studies, results, portfolio, gallery, reviews, privacy_policy, terms_of_service, careers, team, or other. Clients tolerate unknown values.
url
string
The page URL. Omitted when unknown.
title
string
The page title. Omitted when unknown.
markdown
string
The page content as markdown, at most 20,000 characters.
scrapedAt
string (date-time)
When the page was captured: the archive capture date, the scrape time for live pages, or the upload time for imported pages.
source
string
archive (Common Crawl copy), live (scraped by Kenso), or imported (supplied in a CSV upload). Clients tolerate unknown values.
Request
curl -X GET "https://api.ken.so/v1/contacts/812/website-pages" \
  -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": [
    {
      "pageType": "string",
      "url": "https://example.com",
      "title": "string",
      "markdown": "string",
      "scrapedAt": "2026-09-23T15:02:11Z",
      "source": "string"
    }
  ],
  "pagination": {
    "nextCursor": "eyJpZCI6ODE1fQ",
    "hasMore": true,
    "total": 1,
    "isTotalCapped": true
  }
}