Open appvitae.ai

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:

  • publicId reuses a Contact already accessible to the key owner’s organization.
  • externalId creates or reuses a Contact under the tuple (organization, source, externalId). fullName is 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.