Skip to content

Security & responsible use

This page covers how to keep your character safe to run in production: protecting the connector token that is your character, the strict isolation that keeps a token reachable only by its own character, and the line between what FluidTalk does (generate the character's text) and what you do (talk to the platform). It also documents the things you must handle yourself, honestly, so you can design around them.

If you only read one thing: your connector token is the character — anyone holding it can drive Ava, so treat it like a password and never ship it client-side — and FluidTalk only generates text; it never posts anything. How you read and send on Instagram, X, Fanvue, or anywhere else is your concern, and so is staying within each platform's rules.


Protect your connector token

A connector token (ftc_live_...) authenticates as exactly one character. There is no account-level key and no character_id field — the token is the identity. Whoever holds it can send DMs as your character, fire triggers, generate comments, and pull follow-ups. Guard it accordingly.

You create and rotate tokens in the dashboard (https://talk.fluidvip.com → the character's platform settings), never through the API. FluidTalk stores only a hash of the token; the plaintext is shown once at creation and cannot be recovered.

The token is shown exactly once. Copy it the moment it is created. If you lose it — or suspect it leaked — revoke it in the dashboard and issue a new one. The old token stops working immediately and returns 401 invalid_token.

Where the token may and may not live:

Where the token livesAllowed?Why
Server-side environment variable / secret managerYesRead it at runtime; never bake it into an image or commit it.
Backend-to-backend calls to the APIYesThe token only ever travels from your server to api-talk.fluidvip.com.
Browser, mobile app, or any client-side codeNoClient code is inspectable — a shipped token is a leaked token.
Committed to source controlNoHistory is forever; rotate at once if this happens.
A URL, query string, or log lineNoSend it only in the X-Connector-Token header (see authentication.md).

Rotating a token

Rotation is a dashboard operation: revoke the current token and issue a replacement, then update the secret your connector reads at runtime. Because each token is scoped to a single character, rotating one never affects your other characters. A revoked or mistyped token is rejected at request time with the standard error envelope:

json
{
  "error": {
    "code": "invalid_token",
    "message": "Invalid or revoked connector token.",
    "type": "client_error",
    "request_id": "req_8f3c2a7b"
  }
}

A 401 means re-check the credential — the token is missing, mistyped, or revoked. The SDKs raise a typed AuthError. See errors.md for the full mapping.


Tenant isolation

Your token is scoped to a single character. Every endpoint filters every lookup by that character, so a token can only ever address its own sessions, leads, follow-ups, and comment threads.

Foreign ids return 404, not 403. If you reference a session_id, a follow-up id, or any other resource that belongs to another character — or that simply does not exist — FluidTalk returns 404 not_found, identically. This is deliberate: a 403 would confirm that the id exists somewhere, leaking information across character boundaries. By collapsing "not yours" and "doesn't exist" into the same 404, the API never reveals whether another character's resource exists.

Practical consequences for your integration:

  • Treat 404 as "this id is not addressable by my token" — it does not distinguish "ended", "never existed", or "belongs to another character". Do not branch on those cases; you cannot observe them.
  • You never need to (and cannot) name a character id. Your token already determines the character; you only ever pass platform in the body to select the bound workflow.
  • A lead is addressed by (platform, handle) — never by a global id — so one token can never reach into another character's people.
bash
# Acking a follow-up id that isn't this character's -> 404, never 403.
curl -s -o /dev/null -w "%{http_code}\n" \
  -X POST \
  -H "X-Connector-Token: ftc_live_..." \
  "https://api-talk.fluidvip.com/api/v1/characters/followups/fu_00000000/ack"
# 404

FluidTalk generates text — you post it

FluidTalk returns words. /chat returns bubbles, /comments returns a comment, /comments/reply returns a reply, /followups returns queued messages. The API never connects to a platform and never posts anything itself. Your connector owns all platform I/O: reading inbound DMs and events, and sending the bubbles, comments, and replies the API hands back. How you connect to Instagram, X, Reddit, Fanvue, TikTok, or any platform is your concern.

Because the platform side is entirely yours, you are responsible for platform compliance — each platform's terms of service, automation and rate policies, content rules, and any disclosure obligations. FluidTalk has no visibility into what you post or where, and no control over it. Generate responsibly and send within each platform's limits.

Where the boundary sits

ConcernFluidTalkYou (the connector)
Generating the character's text (bubbles, comments, replies, openers)
Tracking the relationship — one cross-surface-aware Person
Metering model spend and charging the wallet
Reading inbound DMs, events, and comments from the platform
Sending bubbles / posting comments and replies
Platform terms-of-service & automation-policy compliance
At-most-once delivery of each inbound DM

Keep PII out of URLs

Send everything in the request body and the token in the header — never in the path or query string. The one query-string endpoint, GET /followups, takes only platform, own_username, and limit; do not append identifiers or secrets to it.

When you pass an image_url, a post's image_urls, or a post_ref, prefer platform-native references and avoid embedding unnecessary personal data in those values. Identify leads by their handle (@mark), not by legal names or email addresses. URLs end up in access logs and edge caches that you do not control; bodies and headers do not.


Known limitations

We document these honestly so you can design around them. Both are current behavior, not promises.

Inbound DMs are not deduplicated for you

/events and /triggers are idempotent on external_event_id — a re-delivered webhook applies exactly once and returns deduped: true on repeats. /chat is not. An inbound DM has no message id, so a re-sent inbound creates a new turn (and meters a new charge).

At-most-once delivery of inbound DMs is your responsibility. De-duplicate inbound messages before you call /chat, or pass a stable session_id to keep a retried inbound in the same conversation rather than spawning a fresh turn. See concepts.md for sessions and idempotency.

Connector echo (raw) is scrubbed best-effort

/events and /triggers accept an optional raw object so you can echo the platform payload back for your own records. FluidTalk strips obvious tokens from it, but treat that as best-effort, not a guarantee. Do not put credentials, access tokens, or sensitive PII in raw; send only the minimum you need for correlation.


Checklist for integrators

  • Keep the connector token server-side only; never ship it in client code, a URL, or source control. Rotate it in the dashboard if it leaks.
  • Treat any 404 as "not addressable by my token" — never infer existence across characters.
  • Remember FluidTalk only generates text. You post it, and you own platform-ToS and automation-policy compliance.
  • De-duplicate inbound DMs before calling /chat (or pass a stable session_id); /chat is not idempotent.
  • Keep identifiers and secrets out of URLs and raw; send data in the body, the token in the header.
  • Handle 402 payment_required by topping up the wallet — see billing.md.

FluidTalk Characters API — part of the Fluidvip ecosystem.