Add every WhatsApp opt-in to your ActiveCampaign contact list, search existing contacts before personalising a message, and trigger automations from WhatsApp, all without manually importing a CSV.
Before following this guide, read the External API Request step foundation guide. It covers every field in the step interface so this guide can focus on ActiveCampaign-specific values.
ActiveCampaign uses Api-Token: YOUR_TOKEN as the header name, not Authorization. The base URL is also unique to your account: https://YOURACCOUNTNAME.api-us1.com/api/3/. Both are found in Settings → Developer. Wrong subdomain means no connection.
https://youraccountname.api-us1.com.
This is your base URL for all calls.Api-Token header on every request.ActiveCampaign API v3: developers.activecampaign.com
List IDs in ActiveCampaign are integers. You can also find them in the URL when you view a list in the UI. The API confirms them reliably.
{{
"lists": [
{{
"id": "2",
"name": "WhatsApp Opt-Ins",
"subscriber_count": "1843"
}}
],
"meta": {{"total": "1"}}
}}
Copy: lists[0].id = "2" (use as integer when subscribing)Before adding a contact, check whether they already exist. Pass the email as a query parameter.
{{
"contacts": [
{{
"id": "21",
"email": "priya@example.com",
"firstName": "Priya",
"lastName": "Sharma",
"phone": "+919820000001"
}}
],
"meta": {{"total": "1"}}
}}
Map: contacts[0].id -> contact_id
contacts[0].firstName -> contact_name
If contacts array is empty, the contact does not exist -> run Step 4.Body:
{{
"contact": {{
"email": "{{customer_email}}",
"firstName": "{{customer_name}}",
"phone": "{{customer_phone}}"
}}
}}
Response (HTTP 201):
{{
"contact": {{
"id": "21",
"email": "priya@example.com",
"firstName": "Priya"
}}
}}
Map: contact.id -> contact_id
Note: response wraps contact in a 'contact' key (singular),
not 'contacts' (plural) like the search endpoint.The search endpoint returns a contacts array: contacts[0].id. The create endpoint returns a single contact object: contact.id. These are different keys. Use the right path for each step.
With
{{contact_id}} from Step 3 or 4, and your list ID,
subscribe them:
Body:
{{
"contactList": {{
"list": 2,
"contact": "{{contact_id}}",
"status": 1
}}
}}
Response (HTTP 200):
{{
"contactList": {{
"contact": "21",
"list": "2",
"status": "1"
}}
}}
status: 1 = subscribed
status: 2 = unsubscribed
status: 0 = unknown
Idempotent: re-subscribing an already-subscribed contact returns 200 again.Response field mapping:
| Variable | Source | Example value |
|---|---|---|
| contact_id | Step 3: contacts[0].id or Step 4: contact.id | 21 |
| contact_name | Step 3: contacts[0].firstName | Priya |
| list_id | Step 2: lists[0].id (use as integer) | 2 |
Store contact_id and list_id as flow variables to avoid re-fetching on every run.
Trigger: Customer messages 'SUBSCRIBE' on WhatsApp.
Captured: {{customer_email}} = priya@example.com
{{customer_name}} = Priya Sharma
{{customer_phone}} = +919820000001
Step 1 — Search contact:
GET https://youraccountname.api-us1.com/api/3/contacts
?email=priya@example.com
Api-Token: YOUR_TOKEN
Response: meta.total = 0 (not found)
Conditions: if total > 0 -> use contacts[0].id as contact_id, skip to Step 3
if total = 0 -> run Step 2
Step 2 — Create contact:
POST https://youraccountname.api-us1.com/api/3/contacts
Body: {{"contact":{{"email":"priya@example.com","firstName":"Priya",
"phone":"+919820000001"}}}}
Response: contact.id = '21'
Stored as {{contact_id}}.
Step 3 — Subscribe to list:
POST https://youraccountname.api-us1.com/api/3/contactLists
Body: {{"contactList":{{"list":2,"contact":"{{contact_id}}","status":1}}}}
Response: HTTP 200, contactList.status = '1'
Bot replies:
'You are now subscribed, Priya! You will receive our updates
at priya@example.com.'| Symptom | Likely cause | Fix |
|---|---|---|
| 401 Unauthorized | Wrong header name | Use Api-Token as the header name, not Authorization. Confirm the token value from Settings → Developer. |
| Connection refused or timeout | Wrong account URL | Your base URL is account-specific. Copy it from Settings → Developer exactly. A wrong subdomain (e.g. .api-us2.com vs .api-us1.com) returns a connection error. |
| contacts array empty | Contact not in ActiveCampaign | The email does not exist. Proceed to Step 4 to create the contact. |
| contact vs contacts confusion | Different response shapes | GET /contacts returns contacts[] array. POST /contacts returns contact singular object. Use contacts[0].id for search results and contact.id for create response. |
| 422 on contactLists | Contact ID or list ID is wrong type | The contact field accepts a string or integer. The list field must be an integer (2, not '2'). Confirm both IDs are valid. |
| 429 Too Many Requests | Rate limit exceeded | Check the Retry-After header and wait before retrying. For a WhatsApp bot doing single lookups, this limit is very generous. |
Mailchimp API: data centre prefix, anystring auth, subscriber sync.
Read guide →Klaviyo API: Klaviyo-API-Key header, JSON:API format, list subscription.
Read guide →Free trial, no credit card. If you get stuck, we answer live on WhatsApp.