Automations
Send a series of timed messages to contacts who opted in, with automatic exits when they reply or unsubscribe, on WhatsApp, Instagram, Facebook and more.
A sequence is a drip campaign over DMs: a list of steps, each with a delay and a message, sent to every contact you enroll. Typical uses are onboarding after a sign-up, a follow-up series after a lead form, or a reminder before an appointment. Contacts leave automatically when they reply or unsubscribe, so a person who starts talking to you stops getting scripted messages.
Sequences run on one account: WhatsApp, Instagram, Facebook, X, Bluesky or Slack. Telegram is coming soon.
#Create a sequence
curl -X POST https://api.pozy.ai/v1/sequences \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "6650c1e2a3b4c5d6e7f80911",
"accountId": "6650c1e2a3b4c5d6e7f80a44",
"platform": "whatsapp",
"name": "Trial onboarding",
"steps": [
{ "order": 1, "delayMinutes": 0, "template": { "name": "trial_welcome", "language": "en", "variableMapping": { "1": { "field": "name" } } } },
{ "order": 2, "delayMinutes": 1440, "template": { "name": "trial_day_two_tips", "language": "en" } },
{ "order": 3, "delayMinutes": 7200, "template": { "name": "trial_ending", "language": "en" } }
],
"exitOnReply": true,
"exitOnUnsubscribe": true
}'const res = await fetch("https://api.pozy.ai/v1/sequences", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
profileId: "6650c1e2a3b4c5d6e7f80911",
accountId: "6650c1e2a3b4c5d6e7f80a44",
platform: "whatsapp",
name: "Trial onboarding",
steps: [
{ order: 1, delayMinutes: 0, template: { name: "trial_welcome", language: "en", variableMapping: { "1": { field: "name" } } } },
{ order: 2, delayMinutes: 1440, template: { name: "trial_day_two_tips", language: "en" } },
{ order: 3, delayMinutes: 7200, template: { name: "trial_ending", language: "en" } },
],
exitOnReply: true,
exitOnUnsubscribe: true,
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
const res2 = await fetch(`https://api.pozy.ai/v1/sequences/${data.sequence.id}/activate`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
if (!res2.ok) throw new Error((await res2.json()).error);import os
import requests
H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
seq = requests.post(
"https://api.pozy.ai/v1/sequences",
headers=H,
json={
"profileId": "6650c1e2a3b4c5d6e7f80911",
"accountId": "6650c1e2a3b4c5d6e7f80a44",
"platform": "whatsapp",
"name": "Trial onboarding",
"steps": [
{"order": 1, "delayMinutes": 0, "template": {"name": "trial_welcome", "language": "en"}},
{"order": 2, "delayMinutes": 1440, "template": {"name": "trial_day_two_tips", "language": "en"}},
],
},
timeout=30,
).json()["sequence"]
requests.post(f"https://api.pozy.ai/v1/sequences/{seq['id']}/activate", headers=H, timeout=30)A sequence starts as a draft. Activate it to start sending; it needs at least one step.
#Steps
| Field | Notes |
|---|---|
order |
Position of the step, starting at 1 |
delayMinutes |
How long to wait before sending this step. 0 on the first step sends it as soon as the contact is enrolled |
message |
{ text } for a free-form message |
template |
{ name, language, variableMapping } for an approved WhatsApp template |
Check nextStepAt on each enrollment to see exactly when the next step goes out.
#Pick message or template per platform
- WhatsApp: a step that may go out more than 24 hours after the contact last wrote must be an approved template. In practice, use templates for every step.
variableMappingmaps template placeholders toname,phone,email,companyor a fixedcustomvalue. See Templates. - Instagram and Facebook: automated messages can only be sent within 24 hours of the contact's last message, so a long sequence stops reaching people who went quiet. Keep sequences short there, or use them right after a conversation starts.
- Telegram, X, Bluesky, Slack:
message.textsteps.
#Enroll contacts
curl -X POST https://api.pozy.ai/v1/sequences/6650c1e2a3b4c5d6e7f80f77/enroll \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "contactIds": ["6650c1e2a3b4c5d6e7f80c44", "6650c1e2a3b4c5d6e7f80c45"] }'const res = await fetch("https://api.pozy.ai/v1/sequences/6650c1e2a3b4c5d6e7f80f77/enroll", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ contactIds: ["6650c1e2a3b4c5d6e7f80c44", "6650c1e2a3b4c5d6e7f80c45"] }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.enrolled, data.failed, data.results);resp = requests.post(
"https://api.pozy.ai/v1/sequences/6650c1e2a3b4c5d6e7f80f77/enroll",
headers=H,
json={"contactIds": ["6650c1e2a3b4c5d6e7f80c44", "6650c1e2a3b4c5d6e7f80c45"]},
timeout=30,
)
print(resp.json())Pozy picks each contact's channel on the sequence's account; pass channelIds to choose it yourself. Contacts already enrolled are skipped. The response lists a result per contact, so check failed and the results array.
Other ways to enroll:
- A workflow's
enroll_sequencenode enrolls the contact in the middle of a conversation, for example after they share an email. - Your backend can enroll contacts when something happens in your product, such as a trial starting.
Only enroll people who agreed to hear from you. On WhatsApp and SMS, that consent is a platform rule, not just good practice.
#How contacts leave
exitReason |
Why |
|---|---|
completed |
The last step was sent |
replied |
The contact replied, and exitOnReply is on (the default) |
unsubscribed |
The contact opted out, and exitOnUnsubscribe is on (the default) |
manual |
You removed them with DELETE /v1/sequences/{id}/enroll/{contactId} |
failed |
A step kept failing to send |
sequence.enrolled and sequence.exited webhooks report each move, with exitReason on exits.
#Monitor and change
GET /v1/sequences/{id}returns the steps and totals (totalEnrolled,totalCompleted,totalExited).GET /v1/sequences/{id}/enrollmentslists contacts withstatus(active,completed,exited,paused),currentStepIndex,stepsSent,lastStepSentAtandnextStepAt.POST /v1/sequences/{id}/pausestops all sends until you activate it again. Enrolled contacts keep their place.PATCH /v1/sequences/{id}changes the name, exits or steps. The step list is replaced as a whole and can only change while the sequence isdraftorpaused.DELETE /v1/sequences/{id}deletes the sequence and stops every active enrollment.
Messages a sequence sends arrive as message.sent with sentVia: "sequence" and the sequenceId, and count toward your outbound messages.
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/sequences |
List sequences |
POST /v1/sequences |
Create sequence |
GET /v1/sequences/{sequenceId} |
Get sequence with steps |
PATCH /v1/sequences/{sequenceId} |
Update sequence |
DELETE /v1/sequences/{sequenceId} |
Delete sequence |
POST /v1/sequences/{sequenceId}/activate |
Activate sequence |
POST /v1/sequences/{sequenceId}/pause |
Pause sequence |
POST /v1/sequences/{sequenceId}/enroll |
Enroll contacts in a sequence |
DELETE /v1/sequences/{sequenceId}/enroll/{contactId} |
Unenroll contact |
GET /v1/sequences/{sequenceId}/enrollments |
List enrollments for a sequence |
#Webhook events
| Event | When it fires |
|---|---|
sequence.enrolled |
Sequence enrolled. |
sequence.exited |
Sequence exited. |