Examples

Common recipes for the Chesly API. Replace csk_... with your real token and pick the language you prefer.

List your rooms

curl

curl https://api.chesly.app/api/rooms \
  -H "Authorization: Bearer csk_..."

JavaScript (Node 18+)

const res = await fetch('https://api.chesly.app/api/rooms', {
  headers: { Authorization: `Bearer ${process.env.CHESLY_TOKEN}` },
});
const { rooms } = await res.json();
console.log(rooms.map((r) => r.name));

Python

import os
import httpx

token = os.environ["CHESLY_TOKEN"]
res = httpx.get(
    "https://api.chesly.app/api/rooms",
    headers={"Authorization": f"Bearer {token}"},
)
res.raise_for_status()
print([r["name"] for r in res.json()["rooms"]])

Read and search locally

Personal tokens return readable text, senderId, and direction. No decryption key or extra headers are required. Follow nextCursor until null, including after an empty page.

const headers = { Authorization: `Bearer ${process.env.CHESLY_TOKEN}` };
const roomId = '!conversation:matrix.chesly.app'; // from GET /api/rooms
const query = new URLSearchParams({ roomId, limit: '100' });
let cursor = null;
do {
  if (cursor) query.set('cursor', cursor);
  const res = await fetch(`https://api.chesly.app/api/messages?${query}`, { headers });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  const page = await res.json();
  // Search locally; retain IDs for deduplication and later human approval.
  for (const m of page.messages) {
    if (m.text.toLowerCase().includes('meeting')) {
      console.log(m.roomId, m.senderId, m.direction, m.text);
    }
  }
  cursor = page.nextCursor;
} while (cursor !== null);

Send after human approval

Use the returned Matrix roomId, URL-encoded in the path. Show the platform, conversation ID, recipient IDs, and exact text before approval. Names are labels, not verified identities. Groups have no single recipient.

import { createInterface } from 'node:readline/promises';
import { randomUUID } from 'node:crypto';

const roomId = '!conversation:matrix.chesly.app'; // selected from GET /api/rooms
const content = 'The exact text to approve';
const headers = { Authorization: `Bearer ${process.env.CHESLY_TOKEN}` };
const url = `https://api.chesly.app/api/rooms/${encodeURIComponent(roomId)}`;
const lookup = await fetch(url, { headers });
if (!lookup.ok) throw new Error(`HTTP ${lookup.status}`);
const { room } = await lookup.json();
if (!room.isGroup && !room.recipientId) {
  throw new Error('Recipient identity unavailable. Verify this conversation in Chesly before sending.');
}
const approved = Object.freeze({ roomId, content, txnId: randomUUID() });
console.log(JSON.stringify({
  platform: room.platform, name: room.name, roomId,
  recipientId: room.recipientId, memberIds: room.memberIds,
  content: approved.content,
}, null, 2));
const prompt = createInterface({ input: process.stdin, output: process.stdout });
const answer = await prompt.question('Send this exact message to this conversation? Type SEND: ');
prompt.close();
if (answer !== 'SEND') process.exit(0);

// Persist approved before sending. A retry must reuse its content and txnId.
const res = await fetch(`${url}/messages`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({ content: approved.content, txnId: approved.txnId }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
console.log(await res.json()); // 202 = accepted, not proof of recipient delivery

Use one new txnId per approved message. Retry that same message with the same ID. A successful response means accepted for sending; delivery to the remote network happens asynchronously.

Handling errors

The Chesly API returns standard HTTP status codes. Common cases worth handling:

  • 401 Unauthorized: the token is missing, malformed, or has been revoked.
  • 403 Forbidden with error: "subscription_expired": your Chesly Plus subscription lapsed. Resubscribe to reactivate all of your tokens.
  • 429 Too Many Requests: back off and retry, honoring Retry-After.
  • 5xx: transient. Retry with exponential backoff and jitter.

Want more?

The complete list of endpoints lives in the API reference.