AcquisityDeveloper Docs
Practical guides

Import your own CSV leads

Map a CSV to campaign lead objects, batch it safely, and verify personalized values.

Use Add campaign leads to import leads you already have. The API accepts JSON objects, not a CSV upload. This matches the common bulk-import pattern: your client reads CSV rows, maps selected columns, then submits leads in batches.

Use a workspace API key with campaigns:write to import leads. The full workflow also needs workspaces:read to choose a workspace, campaigns:read to inspect the draft campaign, and campaignLeads:read to read back the imported custom values.

Choose the mapping before upload

CSV columnLead fieldSequence use
EmailemailRecipient identity
First namefirstName{{firstName}}
Companycompany{{companyName}}
Opening linecustomVariables.icebreaker{{icebreaker}}
ReasoncustomVariables.reasonToReachOut{{reasonToReachOut}}

The standard mapping can also include lastName, title, industry, phone, linkedin, social profiles, company size, address fields, website, companyDescription, and tags. Add a supported custom key by supplying it in customVariables for each lead. There is no variable-definition or catalog call. Custom values must be strings. Use bare keys such as icebreaker, with no braces or leading/trailing spaces. Placeholder matching is case-sensitive: icebreaker supplies {{icebreaker}}, not {{Icebreaker}}.

Reserved custom-variable names

The API rejects these names in customVariables, regardless of letter case. Map supported standard fields at the lead object's top level instead.

GroupReserved names
Standard lead fieldsfirstName, lastName, email, industry, company, title, phone, linkedin, twitter, facebook, instagram, employees, street, city, state, postalCode, country, website, companyDescription, tags
Built-in and structural fieldsname, pipelineId, stageId, espCode, esgCode, companyName, jobTitle, sendingAccountName, sendingAccountFirstName, accountSignature
System and prototype namespreview_env, __proto__, prototype, constructor
Exported campaign contextAcquisity Target Niche, Acquisity Target Industry, Acquisity Target Job Titles, Acquisity Locations, Acquisity Structured Locations, Acquisity Company Size, Acquisity Campaign Method, Acquisity SuperSearch Filters

Empty names, names containing { or }, and names with leading or trailing spaces are also rejected.

Blank and missing values

An explicit empty string is preserved: "icebreaker": "" makes {{icebreaker}} render as blank in the sequence preview. It does not generate a fallback opening line. Spaces and line breaks in non-empty custom values are preserved too.

If you omit the key, the preview leaves an unresolved placeholder such as {{icebreaker}} visible; it does not invent a value. Supply fallback copy yourself when mapping the CSV, or remove the placeholder from that sequence. Preview the sample recipients and resolve missing values before launching.

Do not silently ignore research columns. Show the chosen mapping and flag columns you have not mapped before submitting a batch. The example below stops on unmapped columns unless you explicitly allow them.

Batch and retry safely

Each request accepts 1–1,000 leads and must remain within 1 MiB once serialized as the complete MCP tool envelope. Long personalized values can require fewer than 1,000 rows. Use one stable idempotencyKey for each batch and reuse it with the identical body after a timeout. Changed content needs a new key.

For example, 5,000 short rows fit as five 1,000-row batches. A CSV with long opening lines may produce more batches because the byte limit takes priority.

Download the standard-library Python example. It reads UTF-8 CSV with or without a BOM, supports quoted commas and newlines, preserves custom-variable whitespace and blank strings, prints the mapping and request plan, and does not send requests by default:

curl -O https://developer.acquisity.ai/developer/campaign-agent/examples/import_campaign_leads.py
python3 import_campaign_leads.py \
  prospects.csv --workspace-id workspace_id --campaign-id campaign_id

After reviewing the plan, execute it explicitly. The script initializes MCP, submits each batch, and polls get_operation with bounded request and poll timeouts. Its 30-second request timeout leaves room for MCP's server-side operation polling; its five-minute poll timeout controls later checks. It reads the key from the environment and never prints it or lead values:

export ACQUISITY_API_KEY='your workspace API key'
python3 import_campaign_leads.py \
  prospects.csv --workspace-id workspace_id --campaign-id campaign_id --execute

Keep each printed idempotency key for retries. If polling times out, retain the operation IDs and re-poll with get_operation; do not submit the batch again with a new key.

If the operation itself returns needs_attention with dispatchStatus: "timed_out", provider uploading may still be running. A confirmed late completion can update the same operation to succeeded with final counts. Check that operation again later and inspect campaign leads before deciding whether any further action is needed.

Read the completed result

FieldMeaning
receivedRows supplied in this batch.
linkedNew lead rows linked to this Acquisity campaign. Reusing a workspace lead for a new campaign counts as linked.
uploadedLeads the provider confirmed as uploaded.
duplicatesRepeated input rows, existing campaign leads, and provider-reported duplicates.
invalidInvalid email rows and provider-reported invalid or incomplete leads.
skippedOther exclusions, including cross-campaign protection, do-not-contact rules, Mimecast and provider blocklists.

Linking happens before provider filtering. A locally linked lead may later be skipped, so do not add linked to the outcome counts. For example, a batch containing one lead on the do-not-contact list can finish with:

{
  "received": 1,
  "linked": 1,
  "uploaded": 0,
  "duplicates": 0,
  "invalid": 0,
  "skipped": 1,
  "dispatchStatus": "completed"
}

Diagnostics include up to 100 rejectedRows and 500 warnings; their sourceIndex is the zero-based row index within the submitted batch. These bounded arrays are not a complete row-by-row provider report. Inspect the counts and available diagnostics before deciding whether to correct and resubmit a new batch.

Verify the personalized values

After operations finish, use list_campaign_leads or get_campaign_lead to confirm customVariables on a small sample. Then attach or review a sequence that uses the exact placeholders, for example {{firstName}}, {{companyName}}, {{icebreaker}}, and {{reasonToReachOut}}.

Readback also preserves existing string custom variables created through the spreadsheet importer, including names such as jobTitle that new API imports reserve. Internal campaign context and unsafe variable names are excluded. Upload validation does not remove previously saved values.

Adding leads to a draft does not launch it. Adding leads to an active campaign can enroll them in its live sequence and send email under the campaign's existing settings. The endpoint does not launch, resume, or reactivate a campaign.

When using MCP

MCP receives lead objects, not a local filesystem path. Claude Code can read a CSV through its authorized local file tools before calling MCP. Claude Desktop needs an accessible attachment or file tool, or you can provide the already mapped objects. MCP tool details and a complete assistant prompt show the same workflow.

On this page