FluidTalk Characters — Python SDK
Official Python SDK for the FluidTalk Characters API — drive an AI persona across DMs, comments, triggers, and follow-ups from your own bot or connector.
Install
pip install fluidtalk
Quickstart
from fluidtalk import FluidTalk
ft = FluidTalk(token="ftc_live_...") # base_url defaults to production
# A lead DM'd the character — get the reply and send the bubbles yourself.
reply = ft.chat(platform="instagram", handle="mark", message="hey ava")
for bubble in reply.bubbles:
send_dm("mark", bubble.text) # your platform I/O
print("session:", reply.session_id)
Your per-character connector token (ftc_live_…) is sent as X-Connector-Token; get it from the character's platform settings in the dashboard. The token is the character — you name the platform in each call.
API
| Method | Endpoint |
|---|---|
ft.chat(platform, handle, message="", image_url=None, session_id=None, own_username=None) |
POST /chat |
ft.event(platform, handle, external_event_id, event_type="purchase", amount=None, currency=None) |
POST /events |
ft.trigger(platform, handle, event_id, external_event_id, context=None, own_username=None) |
POST /triggers — event_id="outreach" fires the built-in Cold Outreach entry |
ft.followups.list(platform, own_username=None, limit=100) |
GET /followups |
ft.followups.ack(followup_id) |
POST /followups/{id}/ack |
ft.comment(platform, post_ref, caption=None, image_urls=None, author_handle=None) |
POST /comments |
ft.comment_reply(platform, post_ref, replier_handle, reply_text="", parent_comment_ref=None) |
POST /comments/reply |
ft.comment_engage(platform, post_ref, thread, post=None, target_comment_ref=None) |
POST /comments/engage — join a thread between other people |
ft.comment_media(platform, post_ref, data, filename=None, content_type=None) |
POST /comments/media |
ft.inbound_media(platform, data, filename=None, content_type=None) |
POST /inbound-media |
comment_engage is the third comment motion: joining a conversation between other people,
under a post the character may never have touched. You must send thread — we have no rows for
comments we never saw, so it is the character's only context. Omit target_comment_ref and the
character picks the comment worth answering, or abstains. Abstaining is the normal outcome, not
an error — branch on reply, never on the call having succeeded:
res = ft.comment_engage(platform="instagram", post_ref="p1", thread=[
{"comment_ref": "c1", "author_handle": "dan", "text": "silicone will fill a half-inch gap fine"},
{"comment_ref": "c2", "author_handle": "mark", "text": "will it though?", "parent_ref": "c1"},
])
if res.reply: # None on every skip
post_reply(res.target_comment_ref, res.reply) # your platform I/O
else:
print("abstained:", res.reason) # no_target_selected, thread_too_deep, …
comment_media rehosts a post's image so the character can actually see it. We do not fetch
that image — the model provider does — so a public URL is not enough; the host has to serve the
provider's fetcher, and plenty of genuinely public ones do not (Wikimedia renders in a browser and
comes back vision_failed). Bytes in, a URL for comment's image_urls out. Not billed.
up = ft.comment_media(platform="instagram", post_ref="p1", data=raw_bytes, filename="post.jpg")
ft.comment(platform="instagram", post_ref="p1", caption="new deck", image_urls=[up.url])
inbound_media is for a lead-sent photo you only have the bytes of — a Telegram
file_id you downloaded, or an Instagram CDN URL that is signed and expires. You pass
raw bytes, it returns a permanent url to hand to chat as image_url. Already have a
publicly-fetchable URL? Skip it and pass that straight to chat.
up = ft.inbound_media(platform="instagram", data=raw_bytes, filename="photo.jpg")
ft.chat(platform="instagram", handle="mark", message="what do you think? 😏", image_url=up.url)
Responses are returned as attribute-access objects unwrapped from the {data, request_id} envelope (reply.bubbles[0].text, res.decision); a missing field reads as None. Use .to_dict() for the raw dict.
Errors
Every non-2xx response raises a typed exception (all subclass FluidTalkError / ApiError):
from fluidtalk import FluidTalk, PaymentRequiredError, RateLimitError, ApiError
ft = FluidTalk(token="ftc_live_...")
try:
reply = ft.chat(platform="instagram", handle="mark", message="hi")
except PaymentRequiredError:
top_up_wallet() # 402 — the wallet can't cover the turn
except RateLimitError:
backoff_and_retry() # 429
except ApiError as e:
print(e.status, e.code, e.request_id, e.message)
AuthError (401), PaymentRequiredError (402), PermissionError (403), NotFoundError (404), ConflictError (409), ValidationError (422), RateLimitError (429), and ApiError (anything else).
Configuration
FluidTalk(
token="ftc_live_...",
base_url="https://api-talk.fluidvip.com", # green: https://api-green-talk.fluidvip.com
timeout=60.0,
)
Development
pip install -e ".[dev]"
pytest
Release files for fluidtalk 2.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fluidtalk-2.3.0.tar.gz | 13.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fluidtalk-2.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.5 kB
Release files / fluidtalk-2.3.0.tar.gz
| Download URL | fluidtalk-2.3.0.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7a6ef15ce906fa20adbbd58a874e029e2f8e418d08b6cafb42ecc453358f88b3
|
|
BLAKE2b-256 checksum How to use checksums |
fa4a665057a80db379479a6e9f7d61f01aad319b862b457c2d29b09641cc93d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Release files / fluidtalk-2.3.0-py3-none-any.whl
| Download URL | fluidtalk-2.3.0-py3-none-any.whl |
|---|---|
| Size | 11.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
883c48efcbee675df211e4ec3c7220d3b843a1298d5f23df95f5ef7cc40ec46c
|
|
BLAKE2b-256 checksum How to use checksums |
26396c0281c8f35a0ebbebdc807d3dab46cd2d7eed9476446087a5daed94608f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|