curl --request POST \
--url https://brew.new/api/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "jane@example.com",
"firstName": "Jane",
"customFields": {
"plan": "enterprise"
},
"consent": {
"source": "form",
"capturedAt": "2026-04-08T12:00:00.000Z",
"policyVersion": "2026-03",
"evidence": "newsletter checkbox on /signup"
}
}
'{
"summary": {
"inserted": 2,
"updated": 0,
"failed": 0
},
"fieldsCreated": [],
"errors": [],
"warnings": []
}Create or update contacts
Upserts a single contact OR a batch ({ contacts: [...] }, up to 1000 rows). Unknown custom fields auto-create field definitions on the brand, typed from the batch (native booleans/numbers, ISO-date strings → date, anything else string).
Custom-field values are coerced to the definition’s type (dates → epoch ms, "1,234" → 1234, yes/no → booleans). A value that cannot be coerced ("$49" in a number field) is a 409 FIELD_TYPE_MISMATCH on a single upsert and a per-row errors[] entry (code: FIELD_TYPE_MISMATCH, field) in a batch — the other rows still land.
Optional consent: { source: "api" | "form" | "import", capturedAt?, policyVersion?, evidence? } records marketing consent provenance on the contact (per row, or once at batch level as the default). It never changes subscribed. An inline marketing send later needs the contact to be subscribed, and warns when no record exists.
Single: 201 with { contact, created, fieldsCreated, warnings }. Batch: 200 with { summary, fieldsCreated, errors, warnings } — or 207 when some rows failed (per-row errors in errors[]).
curl --request POST \
--url https://brew.new/api/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "jane@example.com",
"firstName": "Jane",
"customFields": {
"plan": "enterprise"
},
"consent": {
"source": "form",
"capturedAt": "2026-04-08T12:00:00.000Z",
"policyVersion": "2026-03",
"evidence": "newsletter checkbox on /signup"
}
}
'{
"summary": {
"inserted": 2,
"updated": 0,
"failed": 0
},
"fieldsCreated": [],
"errors": [],
"warnings": []
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Headers
Optional idempotency key for safe retries. Reusing the same key with the same request body returns the original response for 24 hours.
1 - 100The brand this request acts on. REQUIRED for organization-scoped credentials (otherwise 400 BRAND_ID_REQUIRED — there is no default brand); list ids with GET /v1/brands. Brand-scoped credentials may omit it, and sending a different brand returns 403 BRAND_SCOPE_MISMATCH. A brand outside your organization returns 404 BRAND_NOT_FOUND.
1 - 64Body
- Option 1
- Option 2
1Show child attributes
Show child attributes
Marketing consent provenance to record on this contact. Optional at creation; an inline marketing send to this address later needs the contact to be subscribed and warns when no record exists.
Show child attributes
Show child attributes
Optional deliverability check on ingestion. When true, each address is validated with the provider (2 credits per address, charged on success) and the verdict is saved to the contact’s validationStatus. Submissions above the inline cap (100 addresses) upsert first and validate as a background job, returning a validationJobId.
Was this page helpful?