Documentation / Contacts & audience sync
BUILD

Contacts & audience sync

Add contacts from your backend, import an audience, and keep optional marketing consent with its evidence.

Create or update by email

POST /v1/contacts upserts a contact by normalized email within the key’s workspace. Use contacts:write. Marketing consent is optional: omitting it preserves existing consent; a new contact defaults to no consent.

curl -X POST "$BASE_URL/api/v1/contacts" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@example.com",
    "name": "Alex Morgan",
    "attributes": {"customer_id": "cust_123"},
    "marketing_consent": true,
    "consent_source": "signup-form-v2: explicit opt-in on 2026-11-05"
  }'
Field / scopeDescriptionRequirement / access
emailValid email address; normalized to lowercaseRequired
nameContact nameOptional
attributesCustom JSON attributes; replaced when suppliedOptional
marketing_consentExplicit true to grant, false to revokeOptional
consent_sourceEvidence/source of opt-in; required when trueConditional

Consent is a separate choice

Leave marketing_consent and consent_source out when you are only syncing a customer record. Do not interpret purchases, account creation, or a sales inquiry as marketing consent. The service records consent events with the actor, source, and server timestamp. Follow the Collect consent in your app guide at /docs/checkbox-consent for checkbox markup and a server-side integration example.

Update or revoke

PATCH /v1/contacts/:id can change name, attributes, or consent. Explicit false revokes consent and adds a marketing suppression. Recording true later does not silently remove an existing suppression; inspect Suppressions before assuming marketing is enabled.

{ "marketing_consent": false, "consent_source": "customer-preferences" }

Bulk imports

POST /v1/contacts/import accepts up to 1,000 contact objects in contacts. The import is transactional: an invalid item rolls back the batch. For large audiences, send separate bounded batches and retain progress in your application.

{ "contacts": [
  { "email": "alex@example.com", "name": "Alex" },
  { "email": "sam@example.com", "marketing_consent": true,
    "consent_source": "newsletter-form: explicit opt-in" }
] }

Organize lists

Create a list with POST /v1/lists and {name,contact_ids?}. Add members with POST /v1/lists/:id/contacts and {contact_ids:[...]}. API-key tenant scope applies throughout. Lists organize the audience; membership alone does not grant consent.

Building something interesting?We’d love to help