There is no app to install in Customer.io. You build both directions with standard Customer.io actions, so you decide exactly which requests run and which attributes change.
How the integration works
Flowella never calls the Customer.io API. In the optional inbound direction, Flowella only posts events to the webhook URL you give it, and your automation decides what to do with them.
Before you start
- A Flowella organisation with a connected WhatsApp number. See Setup sequence.
- An approved WhatsApp template in Flowella. See Templates.
- The Owner or Admin role in Flowella, to create an API key and, for the inbound sync, a webhook. See Team and roles.
- A Customer.io workspace where you can create campaigns or automations with webhook actions.
- Each person’s WhatsApp number stored on their Customer.io profile in international format, such as
+447700900123. See Phone number format. - Each person’s agreement to receive WhatsApp messages from you. Meta requires opt-in before you message anyone. See Meta’s opt-in guidance.
Send a WhatsApp template from Customer.io
1
Create a Flowella API key
In Flowella, go to Settings, then API keys, and create a live key with a name such as
Customer.io production. Copy it straight away: Flowella shows it only once. See API keys.2
Find the template and channel IDs
Call
GET /v1/templates?status=APPROVED with your key. Note the template’s id and wabaId, and the whatsappChannelId of the number you want to send from. See Multi-channel if you have several numbers.3
Add a webhook action to your campaign
In your Customer.io campaign workflow, add a Send and receive data action at the point where the WhatsApp message should go out. See Customer.io’s guide to webhook actions.
4
Set the request
Set the method to
POST and the URL to https://api.flowella.io/v1/templates/send. Add these headers:5
Write the request body
Paste this body and replace the IDs with your own. Change
customer.phone to the attribute that holds the person’s WhatsApp number.bodyParameters fills the template’s body variables in order: the first value goes into {{1}}, the second into {{2}}, and so on. Leave it as an empty list, [], if the template has no body variables.6
Test the action
Pick a test person with your own WhatsApp number and select Test response in Customer.io. Add an
Idempotency-Key header with a value you haven’t used before, such as test-2026-11-11-01, and change it for every test. See Repeat sends and idempotency.A 202 response with "status": "accepted" means Flowella queued the send. It doesn’t confirm delivery: check the template’s Statistics tab in Flowella for the result.7
Remove the test header and start the campaign
Delete the
Idempotency-Key header you used for testing, or replace it with a value that is different for every person. Then start the campaign.Template options you can set per person
The request body accepts these fields for each recipient. The API introduction and the OpenAPI document describe the full request.
For a template with an image, video or document header, you can set a public file link in
headerMedia.url at the top level of the body.
Repeat sends and idempotency
POST /v1/templates/send protects you from sending the same message twice, which matters because Customer.io retries a webhook action when it doesn’t get a 2xx response:
- Without an
Idempotency-Keyheader, Flowella uses a hash of the request body as the key. A byte-identical body within 7 days returns the originalidand sends nothing. Because each person’s body contains their own number, a normal campaign sends to everyone once. - With an
Idempotency-Keyheader, any request with the same key within 7 days returns the originalidand sends nothing, even if the body changed. - Separately, Flowella skips a second send of the same template to the same number within 15 seconds.
Idempotency-Key value each time.
Sending to a large segment
Each request from Customer.io queues one send, and Flowella delivers it in the background, retrying temporary WhatsApp errors. Two limits apply to large campaigns:- Meta’s messaging limit. Meta caps how many people you can message outside the 24-hour window in a moving 24 hours. See Messaging limits.
- Your API key’s rate limit. Requests over the limit return
429with aRetry-Afterheader. See Rate limits.
Sync inbound WhatsApp messages to Customer.io (optional)
This part sends each WhatsApp message a contact sends you to Customer.io, so you can update the person or start other campaigns from it.1
Create a webhook-triggered automation in Customer.io
Create a webhook-triggered campaign or automation and copy the webhook URL Customer.io generates. See Customer.io’s guide to webhook campaigns.
2
Add an outbound webhook in Flowella
In Flowella, go to Settings, then Webhooks, and select Add webhook. Paste the Customer.io URL into Endpoint URL, enter a signing secret, and tick only
message.received under Events. See Webhooks.3
Send sample data
Send yourself a WhatsApp template from Flowella and reply to it, so Customer.io receives a real Send test event in Flowella sends a
message.received event to build the workflow from. Each event looks like this:webhook.test event, which has no contact, so filter it out in the next step.4
Keep only real inbound messages
Add a True/False Branch that continues only when the trigger’s
type is message.received.5
Look up the contact in Flowella
The event carries IDs, not the phone number. Add a Send and receive data action with the method
GET, the URL https://api.flowella.io/v1/contacts/{{trigger.data.contactId}} and the same Authorization header as before. The response has the contact’s id, phone, waId, name, email, externalId, isOptedOut, createdAt and updatedAt.To store the message itself, add another request to GET /v1/messages/{{trigger.data.messageId}}. Its body field holds the message text and can be empty.6
Update the person
Add a Create or Update Person action. Choose the identifier your workspace uses for people, and set only the attributes you need, such as the time of the last WhatsApp reply. See Customer.io’s guide to Create or update person.
Security of the inbound webhook
Flowella signs every request with theX-Flowella-Signature header. A Customer.io webhook-triggered automation can’t check that signature, so anyone who has the Customer.io webhook URL can post data to it. Keep the URL private, and don’t use the inbound sync to change consent or billing attributes. See Verify signatures if you route events through your own endpoint first.
Opt-outs
When a contact sendsSTOP or another opt-out keyword, Flowella records the opt-out on that WhatsApp number and blocks every later send to them from it, including sends from Customer.io. See Opt-outs.
To copy opt-outs into Customer.io, don’t rely on the optout.created event: it doesn’t fire for a contact’s first opt-out on a number at the moment. List opt-outs with GET /v1/opt-outs instead, which accepts createdAfter and phone filters.
Troubleshooting
Related
API introduction
Authentication, idempotency, rate limits and errors.
Webhooks reference
Event types, request bodies and signatures.
Template variables
Variables, samples and fallback values.
Bulk send
Send a template to a list from the Flowella app instead.

