One identity for your AI agent across every channel — email, Slack, Discord, WhatsApp, GitHub, SMS, X, Telegram, iMessage — behind a single on_message handler.
Project description
caspian-sdk
Give your AI agent one identity that reaches any human, on whatever app they already use — email, Slack, Discord, WhatsApp, SMS, X, Telegram, iMessage — all behind a single on_message handler.
You write the handler once. Caspian handles the provider quirks, threading, delivery, and dedup for every channel.
pip install caspian-sdk
Quickstart
from caspian_sdk import CommClient
client = CommClient(api_key="YOUR_KEY")
# Connect any channel — email needs nothing; others take a token or one-click OAuth.
inbox = client.connect_email()
print("Agent address:", inbox["address"])
@client.on_message
def handle(message):
# The same handler answers every channel you connect.
message.reply(f"Thanks! You said: {message.text}")
client.listen() # one loop, every channel
api_key and base_url fall back to CASPIAN_API_KEY / CASPIAN_BASE_URL from the
environment or a local .env, so CommClient() with no arguments works too. The legacy
COMM_API_KEY / COMM_BASE_URL names are still honoured as a fallback.
Channels
| Connect | |
|---|---|
connect_email() — default domain or your own |
|
| Slack | install_slack() (one-click) or connect_slack(...) |
| Discord | install_discord() (one-click) or connect_discord(...) |
| GitHub issues / PRs | install_github() or connect_github(...) |
| X / Twitter | install_x() (one-click) or connect_x(...) |
connect_whatsapp(...) (Caspian hosted) |
|
| SMS / phone | connect_phone(...) — own GSM modem, or Caspian hosted |
| Telegram | connect_telegram(bot_token=...) |
| iMessage | connect_imessage() (Caspian hosted) |
Make your agent platform-aware
Each channel behaves differently (Slack threads, WhatsApp's 24-hour window, SMS length, iMessage has no markdown). Pull per-channel etiquette for the channels you've connected and drop it into your agent's system prompt:
guide = client.behavior_prompt()
system_prompt += "\n\n" + guide
# or one channel: client.channel_guide("slack")
Use it, tweak it, or ignore it and write your own.
Rich messages
Send one provider-neutral blocks payload and each channel gets its best
rendering — Slack, Discord and Telegram render natively, email gets rich HTML,
and text-only channels degrade to clean text automatically.
from caspian_sdk import blocks as b
message.reply(blocks=[
b.card(
title="Order #1024 shipped",
subtitle="Arriving Thursday",
buttons=[
{"label": "Track", "url": "https://example.com/track/1024"},
{"label": "Get help", "value": "help:1024"}, # callback
],
),
])
Block types: heading, text, divider, image, fields, list, buttons,
card. A button with a url is a link; a button with a value is a callback.
How it works
- One handler, every channel. Adding a channel is another
connect_*()call — never new handler code. message.reply()answers in the right thread on the right channel automatically.message.typing()shows a "typing…" indicator while your agent thinks (where the platform supports it).client.listen()is resilient — a handler error or a dropped poll won't stop the loop; onlyCtrl+C(KeyboardInterrupt) stops it. Passack=to auto-send an instant acknowledgement the moment a message arrives, before your handler runs — handy on channels with no typing indicator (X, SMS, email):
client.listen(ack="On it — one moment…")
Errors
Non-2xx responses raise a CommError with status_code and detail. Two paid-channel
cases raise typed subclasses that carry structured fields, so you can react in code:
from caspian_sdk import AccountRequiredError, CommError, InsufficientCreditError
try:
message.reply("On it!")
except AccountRequiredError as err:
# 401 — a paid channel needs a one-time developer sign-in first.
err.login() # runs the device sign-in; or read err.login_options
except InsufficientCreditError as err:
# 402 (out of credit) or 429 (spend cap reached).
print(f"Balance: {err.balance_cents}¢")
err.top_up(2000) # mint a Stripe checkout link to refill; or read err.payment_options
except CommError as err:
# Anything else — e.g. a 422 validation error.
print(f"{err.status_code}: {err.detail}")
AccountRequiredError(HTTP 401) —reason,message,login_options;.login()runs the sign-in.InsufficientCreditError(HTTP 402 / 429) —reason,balance_cents,payment_options;.top_up()mints a refill link.CommError— base class for every other non-2xx response.
Overlapping messages
listen() uses a separate queue for each conversation, so a slow reply in one
conversation does not block everyone else. The default is queue:
client.listen(concurrency="queue")
Choose a different policy when the handler does not need every message:
| Policy | Behavior | Use when |
|---|---|---|
queue |
Run every message in order for that conversation | The agent must handle every message |
debounce |
Wait for a pause, then run only the latest message | Several quick messages should become one turn |
drop |
Ignore new messages while that conversation is busy | Skipping interruptions is acceptable |
parallel |
Run every message immediately | Handlers are independent; replies may finish out of order |
Set the debounce window in milliseconds:
client.listen(concurrency="debounce", debounce_ms=500)
The queues live in the client process. Multiple agent processes need their own shared coordination layer.
Docs
Point your coding agent at the setup guide and it does the whole integration for you. Full docs and your API key: trycaspianai.com.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file caspian_sdk-0.5.0.tar.gz.
File metadata
- Download URL: caspian_sdk-0.5.0.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6526dad1055ef59d39d4077c3f8a6d9d725453dccfabe28a8ed25baf5e33eae5
|
|
| MD5 |
a5294b4e59bef58bb2cea8989a010290
|
|
| BLAKE2b-256 |
140a0217805673f8ec591aba712d360453702d84a4f29336a150ad837e088841
|
File details
Details for the file caspian_sdk-0.5.0-py3-none-any.whl.
File metadata
- Download URL: caspian_sdk-0.5.0-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
533c4739dd88d96553201e318956ccf8ca14c43495cb5e63b54cbb4681f065a1
|
|
| MD5 |
3c207023f3562363a9ac5d47a8f7f3f3
|
|
| BLAKE2b-256 |
798049d7b97b42b2b0222e8385417faa170ebc427dd364fd91bc3f6d99506e58
|