Contacts & lists

The examples use the BREVO_API_KEY and BREVO_BASE variables from the quickstart.

Contacts

curl -s -X POST "$BREVO_BASE/contacts" -H "api-key: $BREVO_API_KEY" \
  -H "content-type: application/json" -d '{
    "email": "jane@example.com",
    "attributes": { "FIRSTNAME": "Jane", "LASTNAME": "Martin" },
    "listIds": [3],
    "updateEnabled": true
  }'
  • POST /contacts creates a contact (201 {"id": 12}). It needs at least email, ext_id or the SMS attribute, otherwise 400 missing_parameter. If the email or ext_id is already known: 400 duplicate_parameter, unless "updateEnabled": true (update, 204). An unknown list in listIds or unlinkListIds returns 404 document_not_found.
  • GET/PUT/DELETE /contacts/{identifier}: the identifier is detected automatically (email, numeric id, phone, otherwise ext_id) or forced with ?identifierType=email_id|contact_id|ext_id|phone_id|whatsapp_id|landline_number_id. A digits-only value is read as a contact id: for a phone number, pass identifierType=phone_id or the + prefix. Unknown contact: 404 document_not_found. PUT and DELETE return 204.
  • GET /contacts: limit (default 50, max 1000) / offset, sort (desc by default, on creation date), modifiedSince, createdSince, listIds, ids, and filter: equals(FIRSTNAME,"Jane"), lessThan(AGE,30), greaterThan(AGE,30). On EMAIL, SMS and EXT_ID, only equals is applied. Response {"contacts": [...], "count": 42}.
  • POST /contacts/batch: up to 100 updates in one request ({"contacts": [...]}, above that 400 out_of_range). Each item is matched by id, email, ext_id or sms; attributes.EMAIL changes its address. Returns 204.

Adding a contact back to a list it had unsubscribed from resubscribes it.

Attributes

GET /contacts/attributes lists the built-in attributes (FIRSTNAME, LASTNAME, SMS, WHATSAPP, LANDLINE_NUMBER) and yours. Create one with POST /contacts/attributes/{category}/{name} (category normal, category, transactional, calculated or global), update or delete it on the same path with PUT / DELETE. A category attribute requires enumeration ([{"value": 1, "label": "Gold"}]).

Values are converted to their type, as Brevo does:

Type Stored as
float number ("31,5" → 31.5)
boolean boolean (true/false, 1/0 accepted)
date YYYY-MM-DD
category the enumeration label (numeric value or label accepted)
multiple-choice array of strings (array or comma-separated list)
text text

A value that does not match the type returns 400 invalid_parameter. A null value clears the attribute.

Lists and folders

A list always belongs to a folder (POST /contacts/folders {name} then POST /contacts/lists {name, folderId}, each returns 201 {"id"}). Deleting a folder deletes its lists. GET /contacts/folders and GET /contacts/lists are paginated (limit default 10, max 50); GET /contacts/lists/{id}/contacts accepts limit up to 500 and modifiedSince.

POST /contacts/lists/{id}/contacts/add (and /remove) accepts emails, ids or extIds (150 max, above that 400 out_of_range) and returns 201 with per-contact detail:

{ "contacts": { "success": ["jane@example.com"], "failure": ["unknown@example.com"], "total": 2 } }

/remove also accepts {"all": true} to empty the list; the response then carries a processId.

Import & export

  • POST /contacts/import: jsonBody ([{"email", "attributes"}]), fileBody (CSV with ; or ,, first line = attribute names, EMAIL column) or fileUrl; listIds or newList ({"listName", "folderId"}). Options: updateExistingContacts (default true), emptyContactsAttributes (an empty cell clears the attribute), emailBlacklist, smsBlacklist, notifyUrl. Returns 202 {"processId"}; follow it with GET /processes/{id} (invalid addresses and duplicates in info.import.invalid_emails and info.import.duplicate_email_id, comma-separated).
  • POST /contacts/export: customContactFilter is required, with actionForContacts (allContacts by default, subscribed, unsubscribed, unsubscribedPerList) and listId (required for unsubscribedPerList); exportAttributes picks the columns, notifyUrl receives the result. Returns 202 {"processId"}. The completed process exposes export_url, a signed link to the CSV (; separator, columns CONTACT_ID, EMAIL, then the attributes).

Double opt-in

POST /contacts/doubleOptinConfirmation expects email, includeListIds, templateId and redirectionUrl (http or https), plus optional excludeListIds and attributes. It sends template templateId with a confirmation link ({{ doubleoptin }} variable, valid 30 days; if the template lacks it, the link is appended to the message). Returns 201, or 204 if the contact already exists. The contact is created (or updated), added to includeListIds and removed from excludeListIds only on click, then redirected to redirectionUrl.