Campaign contact import
Use POST /v1/campaigns/:campaignId/contacts/import when an external system has
prospects that should enter an existing Vitae campaign. One request validates,
resolves, and attaches the complete batch. If any row fails, Vitae commits none
of the contact or campaign changes.
Restricted API keys need both contacts_write and campaigns_write. An
unscoped key keeps normal user-parity access. The key owner must also own the
campaign and have the Outreach module enabled.
Request
Send 1–500 entries and a stable namespace for the calling system:
curl -X POST \
https://api.vitae.ai/v1/campaigns/CAMPAIGN_PUBLIC_ID/contacts/import \
-H "Authorization: Bearer vit_..." \
-H "Content-Type: application/json" \
-d '{
"source": "nts-external-tool",
"contacts": [
{ "publicId": "12db9339-1394-43f1-bb5f-51cc71bc2737" },
{
"externalId": "prospect-123",
"fullName": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+35620000000",
"jobTitle": "Engineering Director",
"locationRawAddress": "Valletta, Malta",
"websitesLinkedin": "https://linkedin.com/in/ada"
}
]
}'
Each entry has exactly one identity form:
publicIdreuses a Contact already accessible to the key owner’s organization.externalIdcreates or reuses a Contact under the tuple(organization, source, externalId).fullNameis required the first time.
Email is contact data, not identity. Vitae never merges two external identities
because their email addresses happen to match. Keep both source and
externalId stable across retries.
Response and retries
The standard response envelope contains counts plus one result per input, in input order:
{
"data": {
"campaignId": "CAMPAIGN_PUBLIC_ID",
"createdCount": 1,
"reusedCount": 1,
"attachedCount": 2,
"contacts": [
{ "inputIndex": 0, "publicId": "...", "action": "reused" },
{ "inputIndex": 1, "publicId": "...", "action": "created" }
]
}
}
The operation is replay-safe: a repeated external identity is reused, repeated
rows in one request are deduplicated, and a Contact already on the campaign is
not attached twice. Validation errors identify the failing contacts.N path.
For downstream status changes and replies, subscribe to outbound webhooks. For API-key behavior and the full scope catalog, see The Vitae API.
MCP
The same capability is available as import_campaign_contacts on the Vitae MCP
server. It always creates a pending approval first. No Contacts or campaign
memberships change until a user explicitly approves the action.