https://app.tuco.ai/api/messages with your Tuco API key, and you get replies back by pointing Tuco’s message webhooks at a webhook-triggered Customer.io journey. Customer.io has no native iMessage channel. Tuco sends from a dedicated line and fires message.sent, message.reply, message.fallback and message.failed, which Customer.io records as events on the person. No Zapier or n8n is needed in between.
Overview, architecture and FAQ: tuco.ai/integrations/customer-io.
Journey 1 — send the message
Create an event-triggered journey on your opt-in event (for exampleform_submitted). Add a Webhook action:
1
Request
Method
POST, URL https://app.tuco.ai/api/messages.2
Headers
Authorization: Bearer tuco_xxxxxxxxxxxxx (API key), Content-Type: application/json, and optionally Idempotency-Key: cio-{{customer.id}}-first so a Customer.io retry never sends twice.3
Body
Paste the JSON below. Customer.io fills in the Liquid from the profile.
recipientEmaillinks replies to the person. Tuco textsrecipientPhonefirst and stores the email on its lead. Every webhook then carrieslead.email, which Journey 2 uses to find the Customer.io profile.contactOwnerEmailassigns the conversation to a rep. Reply notifications go to that person only.- Send window: outside 09:00–20:00 in the contact’s timezone, the request still returns
201withstatus: "scheduled", and Tuco sends when the window opens. - First message = plain text. Links aren’t clickable and attachments are dropped on a first-ever message (limits). Put links and media in a later step.
- Never send an empty greeting. A body like
"Hi , …"or one still containing{{firstName}}is rejected with400 MALFORMED_NAME_TEMPLATE. The{% if %}above prevents it.
"respectGap": true. With skipIfReplied: true, Tuco returns 200 with skippedOnly: true and sends nothing if the contact has already replied. See Send Message for every field.
Journey 2 — get replies back into Customer.io
1
Create a webhook-triggered journey
In Customer.io, create a journey with a Webhook trigger and copy its incoming URL.
2
Add a Create Event action
Identify the person by email =
{{trigger.lead.email}}. Event name (Liquid): tuco_{{ trigger.event | replace: '.', '_' }}.3
Map attributes
text, tuco_message_id = {{trigger.messageId}}, tuco_lead_id = {{trigger.leadId}}, phone = {{trigger.lead.phone}}, occurred_at = {{trigger.timestamp}}.4
Register it in Tuco
Add the URL under Integrations → Webhooks (or Create Webhook) and subscribe to
message.sent, message.reply, message.fallback, message.failed and conversation.handover. Use Send test to confirm delivery.
Payload shapes for each event: Message Webhooks.
Test before you start the journeys
- Trigger the opt-in event for a test profile with your own mobile number.
- Confirm the iMessage arrives, then reply to it.
- Check the profile’s activity log for
tuco_message_reply. - If nothing arrives, open the webhook action’s delivery in Customer.io. The response body names the reason, for example
INVALID_TIMEZONE, a daily cap, orMALFORMED_NAME_TEMPLATE.