Skip to content

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:

StateWhat it means
PENDINGThe character has generated a rekindle and it's waiting for you to pull and deliver it. This is what GET /followups returns.
DELIVEREDYou'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

NameTypeRequiredDescription
platformstringYesThe platform to pull for (e.g. instagram). Required for a character-token caller.
own_usernamestringNoThe bot account doing the pull, for multi-account dedup. A follow-up claimed by a specific account is delivered only to that account.
limitintegerNoMax 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"
}
FieldTypeDescription
idstringThe follow-up id. Pass it to ack once delivered.
handlestringThe lead's @username to send the message to.
platformstringThe platform the follow-up is for.
kindstringThe follow-up category (currently rekindle). Treat it as an opaque label for routing/analytics — don't branch behavior on an unknown value.
messagestringThe text to send, exactly as written. Send it verbatim.
created_atstringISO 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

HTTPerror.codeWhen
401invalid_tokenMissing, invalid, or revoked connector token.
422validation_errorplatform is missing (required for a character token).
429rate_limitedToo many requests — honor Retry-After and back off.
500internal_errorServer-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"
}
FieldTypeDescription
okbooleanAlways true on success.
dedupedbooleantrue if this id was already DELIVERED — the ack was a no-op.
delivered_atstringISO 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

HTTPerror.codeWhen
401invalid_tokenMissing, invalid, or revoked connector token.
404not_foundUnknown 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.
429rate_limitedToo many requests — honor Retry-After.
500internal_errorServer-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 delivered

TypeScript

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 limit rows.
  • 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_username on the pull so each account delivers only its claimed leads.

FluidTalk Characters API — part of the Fluidvip ecosystem.