Inbox imports
Connect many sending inboxes at once.
Connect up to 500 SMTP/IMAP inboxes in the background and return the import job
/v1/inbox-importsinbox-imports.createNeeds the inbox_csv_import feature on your workspace. Until it is on, calls answer 404.
Body rows: 1 to 500 inboxes with the fields of POST /v1/inboxes. Each row runs the same connect (validation, consumer-host guard, white-label mailbox cap, live SMTP AUTH and IMAP LOGIN probe), four at a time. A row with a missing field fails, a row that reuses an earlier row's username on the same smtpHost or imapHost with another password fails, an email an earlier row has is skipped (duplicate_in_file), and an email the workspace already has is skipped (already_exists) unless that inbox is a disconnected SMTP inbox, which the row reconnects. A row logs in before its inbox is created, so a failed probe creates no inbox, and email_in_use (another inbox in Kenso holds the address) is only reported after a successful login. firstName, lastName and dailySendLimit are not applied; set them with PATCH /v1/inboxes/{inboxId}. Returns 202 with the job. Poll GET /v1/jobs/{jobId}?type=inbox_import for inboxImport counts and rows; a job with no progress for 10 minutes fails with error.codeinterrupted. Passwords are never stored or returned. A workspace can have 2 imports queued or running at once; a third gets 409 conflict. Each row that passes the up-front checks takes one unit of a 1,000 per workspace per rolling day limit; an import over it gets 429 quota_exceeded with Retry-After, and nothing runs. Requires delivery edit, the same as POST /v1/inboxes, and the inbox_csv_import feature flag: while it is off for the caller the route answers 404 not_found. Both are checked again before each row: once the flag is off, or the caller is no longer a member with delivery edit, the import stops, every row it had not started is skipped with feature_disabled or forbidden, and the job fails with that error.code.
Parameters
400 workspace_required).Request body
application/json, optional.
Response 202
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/inbox-imports" \
-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 '{
"rows": [
{
"email": "[email protected]",
"firstName": "string",
"lastName": "string",
"username": "string",
"password": "string",
"smtpHost": "string",
"smtpPort": 1,
"imapHost": "string",
"imapPort": 1,
"smtpStartTls": true,
"dailySendLimit": 1
}
]
}'{
"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
}
}
}