REST API
The SDKs are thin wrappers over a plain HTTP API. Every method maps to one request, so you can drive Caspian directly from any language, a shell script, or an edge function. This page is the reference for that surface.
Base URL and auth
All endpoints live under a single base URL. Authenticate with your API key as a bearer token on every request.
Base URL: https://api.trycaspianai.com
Header: Authorization: Bearer <api_key>Requests and responses are JSON (Content-Type: application/json on any request with a body). Errors return a non-2xx status with a {"detail": "..."} body. A key is either an anonymous sandbox key (comm_sandbox_...) or a signed-in key (comm_...). See Authentication. The two endpoints that mint keys are the only ones that take no bearer token.
Building with a coding agent? Point it at api.trycaspianai.com/SKILL.md (also served as llms.txt), a machine-readable guide that walks the whole integration end to end. An interactive OpenAPI explorer lives at /docs.
Wherever the SDK is more convenient, the equivalent call is one line:
from caspian_sdk import CommClient
client = CommClient() # reads COMM_API_KEY / COMM_BASE_URL
client.reply(message_id, text="hi") # → POST /v1/messages/{id}/replyimport { CommClient } from "caspian-sdk";
const client = new CommClient(); // reads COMM_API_KEY / COMM_BASE_URL
await client.reply(messageId, "hi"); // → POST /v1/messages/{id}/replyProjects and auth
Mint a key, or run the OAuth 2.0 device flow to sign a user in. The sandbox and device-start endpoints need no bearer token.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/projects/sandbox | Mint an anonymous sandbox project + key (free channels). |
| POST | /v1/auth/device/start | Begin device sign-in; returns a user code and verification link. |
| POST | /v1/auth/device/token | Poll for the signed-in key once the user approves. |
curl -s -X POST https://api.trycaspianai.com/v1/projects/sandbox \
-H 'Content-Type: application/json' -d '{"name":"my-agent"}'
# → {"project_id":"...","api_key":"comm_sandbox_..."}curl -s -X POST https://api.trycaspianai.com/v1/auth/device/start \
-H 'Content-Type: application/json' -d '{"api_key":"comm_sandbox_..."}'
# → {"user_code":"WXYZ-2345","device_code":"...",
# "verification_uri_complete":"https://api.trycaspianai.com/device?code=WXYZ-2345","interval":5}curl -s -X POST https://api.trycaspianai.com/v1/auth/device/token \
-H 'Content-Type: application/json' -d '{"device_code":"..."}'
# pending: {"status":"pending"}
# done: {"status":"approved","api_key":"comm_...","base_url":"https://api.trycaspianai.com"}First sign-in grants $100 in free credit and unlocks the paid channels (X, WhatsApp on Caspian's number, iMessage). Write the returned key to .env; future runs reuse it.
Connections
A connection is one channel bound to your project. Create one by POSTing to /v1/connections/{channel} with channel-specific fields in the body. The channel path segment is email, telegram, discord, slack, whatsapp, phone, imessage, x, instagram, or facebook. OAuth channels return a connection carrying an authorize_url; token channels return one that provisions in the background (poll GET /v1/connections/{id} until status is active).
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/connections/{channel} | Create a connection for a channel. |
| GET | /v1/connections/{id} | Fetch a connection (status, address, error). |
| PATCH | /v1/connections/{id} | Update branding (display_name, icon_url), no re-install. |
| POST | /v1/connections/slack/install | One-click install of the shared Slack app. |
| POST | /v1/connections/discord/install | One-click install of the shared Discord bot. |
| POST | /v1/connections/x/install | One-click connect of an X account ("Sign in with X"). |
| POST | /v1/connections/whatsapp/onboarding-session | Begin Meta WhatsApp Embedded Signup; returns a launcher_url. |
| POST | /v1/connections/{id}/initiate | Cold-start a conversation (needs the initiate capability). |
curl -s -X POST https://api.trycaspianai.com/v1/connections/email \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"display_name":"My Agent"}'
# → {"id":"conn_...","status":"active","address":"...@trycaspianai.com"}curl -s -X POST https://api.trycaspianai.com/v1/connections/telegram \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"bot_token":"7123456789:AAE..."}'curl -s -X POST https://api.trycaspianai.com/v1/connections/slack/install \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"display_name":"Acme Support","icon_url":"https://.../icon.png"}'
# → {"id":"conn_...","status":"pending","authorize_url":"https://slack.com/oauth/..."}curl -s https://api.trycaspianai.com/v1/connections/conn_123 \
-H "Authorization: Bearer $COMM_API_KEY"
curl -s -X PATCH https://api.trycaspianai.com/v1/connections/conn_123 \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"display_name":"Acme Bot"}'The full body each channel accepts is documented per channel. See the Channels overview and the individual channel pages (Slack, Discord, X, WhatsApp, SMS).
Messaging
Reply to an inbound message, show a typing indicator, or work with conversations and their history. Replies route back on the source channel automatically; you only pass the message id.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/messages/{id}/reply | Reply to an inbound message on its channel. |
| POST | /v1/messages/{id}/typing | Show a "thinking…" indicator (Discord/Telegram; no-op elsewhere). |
| GET | /v1/conversations | List conversations; optional ?connection_id= filter. |
| GET | /v1/conversations/{id}/messages | List messages in a conversation. |
| POST | /v1/conversations/{id}/messages | Proactively send into an existing conversation (needs the send capability). |
curl -s -X POST https://api.trycaspianai.com/v1/messages/msg_123/reply \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"text":"Thanks! On it."}'curl -s -X POST https://api.trycaspianai.com/v1/messages/msg_123/typing \
-H "Authorization: Bearer $COMM_API_KEY"curl -s "https://api.trycaspianai.com/v1/conversations?connection_id=conn_123" \
-H "Authorization: Bearer $COMM_API_KEY"
curl -s https://api.trycaspianai.com/v1/conversations/cnv_123/messages \
-H "Authorization: Bearer $COMM_API_KEY"Events
Inbound messages and lifecycle changes are delivered as an ordered event stream. Poll GET /v1/events with an after_seq cursor, or register a webhook to receive events by push. Events are strictly ordered by seq; persist the last seq you handled to resume across restarts.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/events | Read events after a cursor. Params: after_seq, limit, type. |
| PUT | /v1/webhook | Register a push endpoint (url, optional signing secret). |
| GET | /v1/webhook | Read the current webhook config. |
curl -s "https://api.trycaspianai.com/v1/events?after_seq=0&type=message.received" \
-H "Authorization: Bearer $COMM_API_KEY"
# → [{"seq":42,"type":"message.received","data":{"message":{...}}}, ...]curl -s -X PUT https://api.trycaspianai.com/v1/webhook \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"url":"https://your.app/caspian/hook","secret":"whsec_..."}'The SDK's listen() loop is just GET /v1/events polling with backoff and dispatch on top. If you build your own loop, advance the cursor only after you finish handling each event.
Behavior guides
Fetch ready-to-inject, per-channel etiquette to append to your agent's system prompt. These endpoints return text/plain, not JSON.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/behavior-prompt | Combined guide for every channel you've connected. |
| GET | /v1/channels/{channel}/guide | Guide for a single channel (e.g. slack, discord). |
curl -s https://api.trycaspianai.com/v1/behavior-prompt \
-H "Authorization: Bearer $COMM_API_KEY"
curl -s https://api.trycaspianai.com/v1/channels/slack/guide \
-H "Authorization: Bearer $COMM_API_KEY"See Behavior guides for how to wire these into a system prompt.
Channels
List the transports enabled on this gateway right now and their capabilities. A channel may appear more than once: one row per provider (for example WhatsApp via both twilio-whatsapp and meta-whatsapp). Only channels listed here are connectable; others 400 on connect.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/channels | List live channels, their providers, and capabilities. |
curl -s https://api.trycaspianai.com/v1/channels \
-H "Authorization: Bearer $COMM_API_KEY"Domains
Register a subdomain you control to send agent email under your own brand. Registering returns the DNS records to add; poll until the domain is active, then connect email with domain=.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/domains | Register a subdomain; returns DNS records to add. |
| GET | /v1/domains | List registered domains and their status. |
| GET | /v1/domains/{id} | Fetch one domain; poll until status is active. |
curl -s -X POST https://api.trycaspianai.com/v1/domains \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"domain":"agents.acme.com"}'
# → {"id":"dom_...","status":"pending","dns_records":[...]}
curl -s https://api.trycaspianai.com/v1/domains/dom_123 \
-H "Authorization: Bearer $COMM_API_KEY"A zone file for bulk import is served at /v1/domains/{id}/zone-file. See Email for the full custom-domain walkthrough.
Test
Inject a synthetic inbound email into a connection so you can watch your handler run without a mail client.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/test-emails | Deliver a test email to your email connection. |
curl -s -X POST https://api.trycaspianai.com/v1/test-emails \
-H "Authorization: Bearer $COMM_API_KEY" -H 'Content-Type: application/json' \
-d '{"text":"hello, are you alive?"}'Within a few seconds a running listener fires a message.received event and your handler replies. Confirm with GET /v1/events?type=message.sent.
The SDKs cover this whole surface with typed methods in Python and JavaScript. Unless you need raw HTTP, reach for them first; see the SDK reference.