Skip to main content

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, max_chars=None) POST /comments
ft.comment_reply(platform, post_ref, replier_handle, reply_text="", parent_comment_ref=None, max_chars=None) POST /comments/reply
ft.comment_engage(platform, post_ref, thread, post=None, target_comment_ref=None, max_chars=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",
    timeout=60.0,
)

Development

pip install -e ".[dev]"
pytest

Metadata

Release files for fluidtalk 2.8.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fluidtalk 2.8.1
File Size Uploaded Provenance
fluidtalk-2.8.1.tar.gz 16.4 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for fluidtalk 2.8.1
File Interpreter ABI Platform
fluidtalk-2.8.1-py3-none-any.whl Python 3 none any Details

Total release size: 29.3 kB

Release files / fluidtalk-2.8.1.tar.gz

Download URL fluidtalk-2.8.1.tar.gz
Size 16.4 kB
Tags Source
SHA-256 checksum
How to use checksums
c5b9ff19f28341f6cb829428f085ff61e83d017d3613dd8aafeb8d1134001744
BLAKE2b-256 checksum
How to use checksums
83e81b461ea6bb8c06c2fb6d0cab14f39dc52d6c70fad802cdabee0b59600b50
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.8.1-py3-none-any.whl

Download URL fluidtalk-2.8.1-py3-none-any.whl
Size 12.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1fbeb584d137fa08239532a4f5306e207b4d5e9d7a3f73e988ebc741e5c50fe2
BLAKE2b-256 checksum
How to use checksums
44efcf369adf277b4de2a4329ab02ab645bd78d91ca62a6d2a1091317f11d427
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Security for fluidtalk 2.8.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page