Jobs
Long-running work such as imports, exports, health checks and crawls: list the jobs of one type, read one and cancel it.
Requires type because ids collide across sources. Scraping types are crawl, extraction, and batch_scrape. Optional status is a comma-separated OR filter. A knowledge_export list holds only the caller's own exports. A migration list is newest first (the cursor is an upper bound); every other type lists oldest first. Read-only. Each type also needs what its own routes need: crawl, extraction and batch_scrape Search view, domain_health Delivery view, contact_import, lead_scoring and enrichment Campaigns view, contact_export Campaigns view and the Export feature, knowledge_export Knowledge view, and migration workspace Admin (the Integrations feature). Without it the call is 403 permission_denied.
Parameters
contact_import or domain_health.queued, running, succeeded, failed, canceled.pagination.nextCursor.400 workspace_required).Response 200
data is a list of JobModel. Each item has these fields. The response also carries pagination.
inbox_import job. Passwords are never returned.contact_import job with target people: each row created a person, updated (matched) one, or was skipped (no email, LinkedIn URL, or name with a business company domain, or intake refused it).contact_import job skipped, by reason. The duplicate counts name the rule that dropped the row: a contact already in this campaign, a contact in another campaign of the workspace (the campaign dedups against other campaigns), an earlier row of the same import, or the campaign's recently-emailed window.curl -X GET "https://api.ken.so/v1/jobs" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": [
{
"id": 812,
"type": "string",
"status": "string",
"progress": {
"completed": 1,
"total": 1
},
"resource": {
"type": "string",
"id": 812
},
"createdAt": "2026-09-23T15:02:11Z",
"downloadUrl": "https://example.com",
"error": {
"code": "string",
"message": "string"
},
"result": null,
"domainsListed": 1,
"excludedLibraryContacts": 1,
"inboxImport": {
"connectedCount": 1,
"failedCount": 1,
"skippedCount": 1,
"startedAt": "2026-09-23T15:02:11Z",
"updatedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z",
"rows": [
{
"index": 1,
"email": "[email protected]",
"status": "string",
"inboxId": 812,
"errorCode": "string",
"message": "string"
}
]
},
"personImport": {
"source": "string",
"createdCount": 1,
"updatedCount": 1,
"skippedCount": 1,
"startedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z"
},
"skipped": {
"duplicateInCampaign": 1,
"duplicateInOtherCampaign": 1,
"duplicateInSource": 1,
"alreadyContacted": 1,
"blocklist": 1,
"failedValidation": 1,
"salesNavigatorProfileUnresolved": 1,
"other": 1,
"missingKey": 1,
"invalidEmail": 1,
"noMx": 1,
"catchAll": 1,
"personalEmail": 1,
"roleAddress": 1,
"companyContacted": 1,
"companyCap": 1
}
}
],
"pagination": {
"nextCursor": "eyJpZCI6ODE1fQ",
"hasMore": true,
"total": 1,
"isTotalCapped": true
}
}Requires type because the same numeric id can exist in more than one source. An inbox_import job carries inboxImport with counts and per-row results; one with no progress for 10 minutes reads failed with error.codeinterrupted. A missing or cross-workspace id is not_found, and so is another member's knowledge_export. Read-only. Each type needs what its own routes need, as on the list; without it the call is 403 permission_denied. A scraping job's status read sets X-Ken-Web-Source and X-Ken-Credits-Charged: Kenso's key charges a job as its reads report progress. A crawl or batch scrape returns one Firecrawl status page (about 10 MB at most) per read: when output.next is not null, pass it back as cursor to read the following page. Other types ignore cursor. A crawl or batch scrape read draws from the Heavy rate-limit class, like the legacy status routes it replaces.
Parameters
400 workspace_required).Response 200
data is a JobModel.
inbox_import job. Passwords are never returned.contact_import job with target people: each row created a person, updated (matched) one, or was skipped (no email, LinkedIn URL, or name with a business company domain, or intake refused it).contact_import job skipped, by reason. The duplicate counts name the rule that dropped the row: a contact already in this campaign, a contact in another campaign of the workspace (the campaign dedups against other campaigns), an earlier row of the same import, or the campaign's recently-emailed window.curl -X GET "https://api.ken.so/v1/jobs/812" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"id": 812,
"type": "string",
"status": "string",
"progress": {
"completed": 1,
"total": 1
},
"resource": {
"type": "string",
"id": 812
},
"createdAt": "2026-09-23T15:02:11Z",
"downloadUrl": "https://example.com",
"error": {
"code": "string",
"message": "string"
},
"result": null,
"domainsListed": 1,
"excludedLibraryContacts": 1,
"inboxImport": {
"connectedCount": 1,
"failedCount": 1,
"skippedCount": 1,
"startedAt": "2026-09-23T15:02:11Z",
"updatedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z",
"rows": [
{
"index": 1,
"email": "[email protected]",
"status": "string",
"inboxId": 812,
"errorCode": "string",
"message": "string"
}
]
},
"personImport": {
"source": "string",
"createdCount": 1,
"updatedCount": 1,
"skippedCount": 1,
"startedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z"
},
"skipped": {
"duplicateInCampaign": 1,
"duplicateInOtherCampaign": 1,
"duplicateInSource": 1,
"alreadyContacted": 1,
"blocklist": 1,
"failedValidation": 1,
"salesNavigatorProfileUnresolved": 1,
"other": 1,
"missingKey": 1,
"invalidEmail": 1,
"noMx": 1,
"catchAll": 1,
"personalEmail": 1,
"roleAddress": 1,
"companyContacted": 1,
"companyCap": 1
}
}
}Cancel a job when its type supports cancellation
/v1/jobs/{jobId}/canceljobs.cancelRequires Search edit, plus what the type's own routes need, as on the list (a migration job: workspace Admin). Crawl and batch scrape cancel the stored Firecrawl id. A migration job cancels like POST /v1/migration-runs/{runId}/cancel: it stays running until the engine stops it. Extraction and the other job types return 409 invalid_state. A finished scraping or migration job does too. A missing job is not_found, and so is another member's knowledge_export.
Parameters
400 workspace_required).Response 200
data is a JobModel.
inbox_import job. Passwords are never returned.contact_import job with target people: each row created a person, updated (matched) one, or was skipped (no email, LinkedIn URL, or name with a business company domain, or intake refused it).contact_import job skipped, by reason. The duplicate counts name the rule that dropped the row: a contact already in this campaign, a contact in another campaign of the workspace (the campaign dedups against other campaigns), an earlier row of the same import, or the campaign's recently-emailed window.curl -X POST "https://api.ken.so/v1/jobs/812/cancel" \ -H "Authorization: Bearer $KEN_API_KEY" \ -H "Ken-Version: 2026-11-02" \ -H "X-Ken-Workspace-Id: $KEN_WORKSPACE_ID"
{
"success": true,
"data": {
"id": 812,
"type": "string",
"status": "string",
"progress": {
"completed": 1,
"total": 1
},
"resource": {
"type": "string",
"id": 812
},
"createdAt": "2026-09-23T15:02:11Z",
"downloadUrl": "https://example.com",
"error": {
"code": "string",
"message": "string"
},
"result": null,
"domainsListed": 1,
"excludedLibraryContacts": 1,
"inboxImport": {
"connectedCount": 1,
"failedCount": 1,
"skippedCount": 1,
"startedAt": "2026-09-23T15:02:11Z",
"updatedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z",
"rows": [
{
"index": 1,
"email": "[email protected]",
"status": "string",
"inboxId": 812,
"errorCode": "string",
"message": "string"
}
]
},
"personImport": {
"source": "string",
"createdCount": 1,
"updatedCount": 1,
"skippedCount": 1,
"startedAt": "2026-09-23T15:02:11Z",
"completedAt": "2026-09-23T15:02:11Z"
},
"skipped": {
"duplicateInCampaign": 1,
"duplicateInOtherCampaign": 1,
"duplicateInSource": 1,
"alreadyContacted": 1,
"blocklist": 1,
"failedValidation": 1,
"salesNavigatorProfileUnresolved": 1,
"other": 1,
"missingKey": 1,
"invalidEmail": 1,
"noMx": 1,
"catchAll": 1,
"personalEmail": 1,
"roleAddress": 1,
"companyContacted": 1,
"companyCap": 1
}
}
}