Appearance
Proactive follow-ups
A FluidTalk character doesn't only react — it reaches back out. When a lead goes quiet, the character periodically queues a rekindle ("hey stranger…") to re-open the conversation. FluidTalk generates the message and holds it in a pull-queue; your connector pulls the pending follow-ups, sends each one on the platform, and acks it. The API never sends anything itself — how you reach the lead on Instagram is your concern.
This page covers the full loop: pulling pending follow-ups with GET /followups, delivering them, and acknowledging each with POST /followups/{id}/ack.
- Base URL:
https://api-talk.fluidvip.com/api/v1/characters - Authentication:
X-Connector-Token: ftc_live_...(the token is the character — see Concepts)
The follow-up lifecycle
A follow-up is a single queued message bound to one lead — (platform, handle) — that moves through exactly two states:
| State | What it means |
|---|---|
| PENDING | The character has generated a rekindle and it's waiting for you to pull and deliver it. This is what GET /followups returns. |
| DELIVERED | You've sent it on the platform and called ack. This is terminal — a delivered follow-up never re-appears in the queue. |
You don't create follow-ups; the character queues them on its own cadence for leads that have gone dormant (with per-character caps and de-duplication so a lead is never spammed). Your job is the delivery half of the loop: pull → send → ack.
Generating a rekindle is a metered model call charged to the character owner's wallet — see Billing. Pulling and acking follow-ups are free; you are only billed when the character writes the message, not when you fetch it.
Pull pending follow-ups
GET /followups returns this connector's PENDING follow-ups, oldest first, so you can deliver them in the order the character queued them.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
platform | string | Yes | The platform to pull for (e.g. instagram). Required for a character-token caller. |
own_username | string | No | The bot account doing the pull, for multi-account dedup. A follow-up claimed by a specific account is delivered only to that account. |
limit | integer | No | Max rows to return. Default 100, max 500. |
Response shape
The follow-ups live under data.followups. As with every endpoint, the payload is wrapped in the { data, request_id } envelope and the same id is echoed in the X-Request-Id header.
json
{
"data": {
"followups": [
{
"id": "fu_3f2504e0",
"handle": "mark",
"platform": "instagram",
"kind": "rekindle",
"message": "hey stranger, you kinda vanished on me 👀 how've you been?",
"created_at": "2026-06-26T09:12:00Z"
}
]
},
"request_id": "req_8f3c2a10"
}| Field | Type | Description |
|---|---|---|
id | string | The follow-up id. Pass it to ack once delivered. |
handle | string | The lead's @username to send the message to. |
platform | string | The platform the follow-up is for. |
kind | string | The follow-up category (currently rekindle). Treat it as an opaque label for routing/analytics — don't branch behavior on an unknown value. |
message | string | The text to send, exactly as written. Send it verbatim. |
created_at | string | ISO 8601 timestamp of when the follow-up was queued. |
An empty queue returns { "data": { "followups": [] }, "request_id": "req_..." } — there is simply nothing dormant to rekindle right now.
Examples
bash
bash
curl "https://api-talk.fluidvip.com/api/v1/characters/followups?platform=instagram" \
-H "X-Connector-Token: ftc_live_..."Python (pip install fluidtalk)
python
from fluidtalk import FluidTalk
ft = FluidTalk(token="ftc_live_8f3c...")
pending = ft.followups.list(platform="instagram")
for fu in pending.followups:
print(fu.id, fu.handle, fu.message)TypeScript (npm install fluidtalk)
typescript
import { FluidTalk } from "fluidtalk";
const ft = new FluidTalk({ token: "ftc_live_8f3c..." });
const { followups } = await ft.followups.list({ platform: "instagram" });
for (const fu of followups) {
console.log(fu.id, fu.handle, fu.message);
}Errors
| HTTP | error.code | When |
|---|---|---|
| 401 | invalid_token | Missing, invalid, or revoked connector token. |
| 422 | validation_error | platform is missing (required for a character token). |
| 429 | rate_limited | Too many requests — honor Retry-After and back off. |
| 500 | internal_error | Server-side failure; retry with backoff. |
The SDKs raise typed errors — AuthError (401), ValidationError (422), RateLimitError (429), and ApiError for the rest. See Errors.
Acknowledge a delivered follow-up
POST /followups/{id}/ack moves a follow-up from PENDING to DELIVERED. Call it after you've sent the message on the platform, so a row stays in the queue until it has actually gone out.
The underlying path is
/followups/{id}/delivered; the SDKs and these docs call it ack for short.
ack is idempotent: acking the same id twice is safe and returns deduped: true on repeats without changing the delivery time. This makes the loop crash-safe — if your worker dies between sending and acking, a re-run can ack again without harm.
Response shape
json
{
"data": { "ok": true, "deduped": false, "delivered_at": "2026-06-26T09:14:05Z" },
"request_id": "req_b1c4d2e8"
}| Field | Type | Description |
|---|---|---|
ok | boolean | Always true on success. |
deduped | boolean | true if this id was already DELIVERED — the ack was a no-op. |
delivered_at | string | ISO 8601 timestamp of the original delivery. |
Examples
bash
bash
curl -X POST \
https://api-talk.fluidvip.com/api/v1/characters/followups/fu_3f2504e0/ack \
-H "X-Connector-Token: ftc_live_..."Python (pip install fluidtalk)
python
result = ft.followups.ack("fu_3f2504e0")
print(result.delivered_at, result.deduped)TypeScript (npm install fluidtalk)
typescript
const result = await ft.followups.ack("fu_3f2504e0");
console.log(result.delivered_at, result.deduped);Errors
| HTTP | error.code | When |
|---|---|---|
| 401 | invalid_token | Missing, invalid, or revoked connector token. |
| 404 | not_found | Unknown follow-up id, or one that isn't this connector's. Ids never leak across connectors — a foreign id is a 404, never a 403. |
| 429 | rate_limited | Too many requests — honor Retry-After. |
| 500 | internal_error | Server-side failure; retry with backoff. |
The SDKs map these to AuthError (401), NotFoundError (404), RateLimitError (429), and ApiError for the rest. See Errors.
The full loop
Pull, deliver on the platform, then ack — one tight loop you run on a schedule.
Python
python
from fluidtalk import FluidTalk
ft = FluidTalk(token="ftc_live_8f3c...")
pending = ft.followups.list(platform="instagram", limit=100)
for fu in pending.followups:
send_dm_on_instagram(fu.handle, fu.message) # your platform I/O
ft.followups.ack(fu.id) # mark it deliveredTypeScript
typescript
import { FluidTalk } from "fluidtalk";
const ft = new FluidTalk({ token: "ftc_live_8f3c..." });
const { followups } = await ft.followups.list({
platform: "instagram",
limit: 100,
});
for (const fu of followups) {
await sendDmOnInstagram(fu.handle, fu.message); // your platform I/O
await ft.followups.ack(fu.id); // mark it delivered
}Ack after the send succeeds, not before. If a send fails, skip the ack — the follow-up stays PENDING and you'll pull it again next cycle. Because ack is idempotent, retrying the ack after a partial failure is always safe.
Polling cadence
The queue is a pull model — FluidTalk never pushes follow-ups to you, so you decide how often to drain it. A poll every few minutes is plenty for rekindles, which are not time-critical; there's no benefit to polling tighter than the character queues new work. Each GET /followups counts against your per-token request budget, so keep the cadence sensible — see Rate limits. A single pull returns up to limit rows (max 500), so on a busy character, keep pulling until you get a short page rather than polling faster.
Multi-account delivery (own_username)
If the character runs several accounts on one platform, pass own_username so each account only drains its own follow-ups. A follow-up for a lead claimed by one account is delivered only to that account's pulls — another account asking with a different own_username won't see it, so no lead gets messaged twice. Omit own_username and the queue behaves normally (single account). Full rules in Multi-account dedup.
bash
curl "https://api-talk.fluidvip.com/api/v1/characters/followups?platform=instagram&own_username=ava.daily&limit=50" \
-H "X-Connector-Token: ftc_live_..."Patterns
- Drain in a worker. Run the pull → send → ack loop on a timer (every few minutes). Keep paging until a pull returns fewer than
limitrows. - Ack last, idempotently. Send first, ack second. A crash between the two just re-queues the row; re-acking a delivered id is a safe no-op (
deduped: true). - Respect the lead's reply. A rekindle re-opens the conversation — when the lead answers, route their reply straight back through
POST /chat; the character resumes the same session and stays cross-surface aware. - One queue per account. Running multiple accounts? Always pass the matching
own_usernameon the pull so each account delivers only its claimed leads.
Related
- Follow-ups reference — the endpoints' full schema.
- Multi-account dedup — the
own_usernamedelivery filter. - Concepts — sessions, people, and where follow-ups fit.
- Billing — the metered cost of generating rekindles.
- Errors · Rate limits — status codes and request budgets.
- Python SDK · TypeScript SDK —
followups.list/followups.ack.