A FluidTalk character doesn't only run DMs — it can also speak in public, commenting on a post and then holding a threaded back-and-forth underneath that comment. This guide shows the two comment operations: writing a top-level comment on a post (POST /comments), and replying to the replies that land under it (POST /comments/reply), to the original poster or to other commenters, as many times as the thread keeps going.
As everywhere in the Characters API, you own the platform I/O: the API generates the character's comment text and tracks the relationship; your connector posts it. The API never posts anything itself.
All calls go to the production base URL https://api-talk.fluidvip.com/api/v1/characters and authenticate with your per-character connector token in the X-Connector-Token header (see authentication.md). The token is the character — there is no character_id; you name the platform in each request body, and the same token works for every platform that character runs on.
For the Person model behind cross-surface awareness (the same lead who DMs and comments is one persona), see concepts.md. For the exact field-by-field request/response shapes, see reference/comments.md and reference/comment-reply.md. Every response is wrapped in the standard { "data": …, "request_id": "req_…" } envelope and carries an X-Request-Id header — the shapes below show the inner data.
post a platform post (post_ref + caption/images/author)└── character's comment POST /comments → the text you post under the post ├── reply (the OP) POST /comments/reply → the character replies back └── reply (someone else) POST /comments/reply → the character replies, thread-aware
A comment is a single top-level comment the character writes for a post. You identify the post by post_ref (the platform's unique id or URL) and pass what context you have about it.
A thread is the character's comment plus the replies under it. Each incoming reply is a separate POST /comments/reply call. The character can reply more than once per thread, and to different commenters in the same thread — it stays consistent across the whole thread, and across other threads and DMs via the shared Person.
Generating a comment or a reply is a metered turn — see Billing. If the character owner's wallet can't cover it you get 402before any model call, so you are never charged for a comment you didn't get.
from fluidtalk import FluidTalkft = FluidTalk(token="ftc_live_8f3c...")result = ft.comment( platform="instagram", post_ref="https://instagram.com/p/Cxy12Ab34Cd/", caption="golden hour on the rooftop ✨", image_urls=["https://cdn.example.com/post-1.jpg"], author_handle="mark",)print(result.comment) # post this text on the platformprint(result.recognized_lead) # True if the author is already a known Person
typescript
import { FluidTalk } from "fluidtalk";const ft = new FluidTalk({ token: "ftc_live_8f3c..." });const result = await ft.comment({ platform: "instagram", postRef: "https://instagram.com/p/Cxy12Ab34Cd/", caption: "golden hour on the rooftop ✨", imageUrls: ["https://cdn.example.com/post-1.jpg"], authorHandle: "mark",});console.log(result.comment); // post this text on the platformconsole.log(result.recognizedLead); // true if the author is already a known Person
The SDKs take the post fields flat (caption, image_urls/imageUrls, author_handle/authorHandle) and assemble the nested post object for you. Over raw HTTP, send them nested under post as shown in the curl example.
The response:
json
{ "data": { "ok": true, "comment": "obsessed with this fit 😍 where'd you get the jacket?", "recognized_lead": true, "aware": { "...": "cross-surface awareness summary" }, "thread_id": "cth_7b21…" }, "request_id": "req_2c9f…"}
comment is the text to post. recognized_lead is true when the post's author is a Person the character already knows. aware is a compact summary of what the character knows about them across surfaces (see below). thread_id is present once a thread exists, so you can correlate the replies that follow.
POST /comments/reply generates the character's reply to a reply under its comment. It can fire multiple times in one thread — to the original poster or to other commenters who join in.
Name
Type
Required
Description
platform
string
Yes
The platform the thread is on.
post_ref
string
Yes
The post whose thread this reply belongs to.
replier_handle
string
Yes
The @username of whoever replied.
reply_text
string
No
What they said — the text the character is responding to.
parent_comment_ref
string
No
The platform ref of the comment being replied to, to thread the reply correctly.
post
object
No
Post context (same shape as above), if the thread is new to the character.
result = ft.comment_reply( platform="instagram", post_ref="https://instagram.com/p/Cxy12Ab34Cd/", replier_handle="mark", reply_text="you look unreal here 😍", parent_comment_ref="ig_comment_17985…",)if result.reply is not None: # None when the character chooses to skip print(result.decision, result.reply)
typescript
const result = await ft.commentReply({ platform: "instagram", postRef: "https://instagram.com/p/Cxy12Ab34Cd/", replierHandle: "mark", replyText: "you look unreal here 😍", parentCommentRef: "ig_comment_17985…",});if (result.reply !== null) { // null when the character chooses to skip console.log(result.decision, result.reply);}
The response carries the character's decision and, when it decides to speak, the reply text:
json
{ "data": { "ok": true, "decision": "drive_to_dm", "reply": "aw you're sweet 🙈 come dm me, way easier to chat there", "reason": "warm replier, time to move to DMs", "aware": { "...": "cross-surface awareness summary" } }, "request_id": "req_4f10…"}
The decision tells you what to do:
decision
Meaning
engage
Reply in-thread. reply holds the text to post under the thread.
drive_to_dm
Reply, but nudge the conversation toward DMs. Post reply, then continue in DMs via POST /chat.
skip
The character chose not to reply. reply is null — post nothing.
When reply is null (a skip), do not post anything. The optional reason is a short human-readable note about why the character decided the way it did; aware is the same cross-surface summary returned by POST /comments.
This is the headline of the Characters API: one coherent persona everywhere. A lead is identified by (platform, handle), and the same lead who comments on a post and who slides into the DMs resolves to a single Person. The character remembers across surfaces — DMs ↔ comments — and across multiple posts and threads.
recognized_lead (on POST /comments) is true when the post's author_handle is already a known Person.
aware (on both endpoints) is a compact summary of what the character already knows about that Person — earlier DMs, prior comments, other threads — so a public reply lands consistently with whatever was said in private, and vice-versa.
You don't wire any of this up. As long as you pass the real author_handle / replier_handle, awareness is resolved for you. For the full model, see Core concepts.
When a thread is warming up, POST /comments/reply may return decision: "drive_to_dm". Post the reply text as usual — it gently steers the replier toward the DMs. When that person then messages the character, feed the inbound DM to POST /chat with the same(platform, handle). Because it's the same Person, the character picks the conversation up with full context from the public thread — no handoff state to manage on your side.
Comments are an opt-in surface per character and per platform. If the character you addressed doesn't have comments enabled for that platform, both endpoints return a normal 200 success envelope whose data is a no-op marker rather than a comment:
This is not an error — there is no comment / reply field, so simply post nothing. It lets you call the comment endpoints unconditionally from your connector and let the dashboard decide whether the character speaks publicly on that platform.
Malformed body — e.g. missing post_ref or replier_handle, or invalid JSON.
401
invalid_token
Missing, invalid, or revoked connector token.
402
payment_required
The character owner's wallet can't cover the generation. Returned before any model call — top up in the dashboard and retry.
403
forbidden
The token isn't allowed to act on this platform/surface.
404
not_found
The thread or referenced comment isn't yours, or doesn't exist.
422
validation_error
A field failed validation (e.g. image_urls not a list of strings).
429
rate_limited
Too many requests — honor the Retry-After header.
500
internal_error
Something broke on our side; retry with backoff.
Note that comments not configured is a 200 no-op (above), not a 404. The SDKs raise typed errors — PaymentRequiredError for 402, NotFoundError for 404, ValidationError for 422, and so on. See errors.md for the full error model and rate-limits.md for per-token limits.
Public comments & threaded replies
A FluidTalk character doesn't only run DMs — it can also speak in public, commenting on a post and then holding a threaded back-and-forth underneath that comment. This guide shows the two comment operations: writing a top-level comment on a post (
POST /comments), and replying to the replies that land under it (POST /comments/reply), to the original poster or to other commenters, as many times as the thread keeps going.As everywhere in the Characters API, you own the platform I/O: the API generates the character's comment text and tracks the relationship; your connector posts it. The API never posts anything itself.
All calls go to the production base URL
https://api-talk.fluidvip.com/api/v1/charactersand authenticate with your per-character connector token in theX-Connector-Tokenheader (see authentication.md). The token is the character — there is nocharacter_id; you name the platform in each request body, and the same token works for every platform that character runs on.How a comment thread works
post_ref(the platform's unique id or URL) and pass what context you have about it.POST /comments/replycall. The character can reply more than once per thread, and to different commenters in the same thread — it stays consistent across the whole thread, and across other threads and DMs via the shared Person.Comment on a post
POST /commentsgenerates a top-level comment for a post. Post the returnedcommenttext yourself.platform"instagram"). Selects the character's bound comments workflow.post_refpostThe
postobject:post.captionpost.image_urlspost.author_handle@usernameof whoever published the post — used for cross-surface recognition.The response:
commentis the text to post.recognized_leadistruewhen the post's author is a Person the character already knows.awareis a compact summary of what the character knows about them across surfaces (see below).thread_idis present once a thread exists, so you can correlate the replies that follow.Reply in a thread
POST /comments/replygenerates the character's reply to a reply under its comment. It can fire multiple times in one thread — to the original poster or to other commenters who join in.platformpost_refreplier_handle@usernameof whoever replied.reply_textparent_comment_refpostThe response carries the character's decision and, when it decides to speak, the
replytext:The
decisiontells you what to do:decisionengagereplyholds the text to post under the thread.drive_to_dmreply, then continue in DMs viaPOST /chat.skipreplyisnull— post nothing.When
replyisnull(askip), do not post anything. The optionalreasonis a short human-readable note about why the character decided the way it did;awareis the same cross-surface summary returned byPOST /comments.Cross-surface awareness
This is the headline of the Characters API: one coherent persona everywhere. A lead is identified by
(platform, handle), and the same lead who comments on a post and who slides into the DMs resolves to a single Person. The character remembers across surfaces — DMs ↔ comments — and across multiple posts and threads.recognized_lead(onPOST /comments) istruewhen the post'sauthor_handleis already a known Person.aware(on both endpoints) is a compact summary of what the character already knows about that Person — earlier DMs, prior comments, other threads — so a public reply lands consistently with whatever was said in private, and vice-versa.You don't wire any of this up. As long as you pass the real
author_handle/replier_handle, awareness is resolved for you. For the full model, see Core concepts.Driving a comment into a DM
When a thread is warming up,
POST /comments/replymay returndecision: "drive_to_dm". Post thereplytext as usual — it gently steers the replier toward the DMs. When that person then messages the character, feed the inbound DM toPOST /chatwith the same(platform, handle). Because it's the same Person, the character picks the conversation up with full context from the public thread — no handoff state to manage on your side.When comments aren't configured
Comments are an opt-in surface per character and per platform. If the character you addressed doesn't have comments enabled for that platform, both endpoints return a normal
200success envelope whosedatais a no-op marker rather than a comment:This is not an error — there is no
comment/replyfield, so simply post nothing. It lets you call the comment endpoints unconditionally from your connector and let the dashboard decide whether the character speaks publicly on that platform.Errors you may hit here
error.code400invalid_requestpost_reforreplier_handle, or invalid JSON.401invalid_token402payment_required403forbidden404not_found422validation_errorimage_urlsnot a list of strings).429rate_limitedRetry-Afterheader.500internal_errorNote that comments not configured is a
200no-op (above), not a404. The SDKs raise typed errors —PaymentRequiredErrorfor402,NotFoundErrorfor404,ValidationErrorfor422, and so on. See errors.md for the full error model and rate-limits.md for per-token limits.Related
POST /chat; the other side of adrive_to_dm.402works.