CestraDocs Open app

JavaScript SDK

@cestra/sdk runs on Node 18 and newer and has zero dependencies. It ships with TypeScript types. run() receives events over a WebSocket on Node 22+, and falls back to REST long-polling elsewhere.

Shell
npm install https://docs.cestra.ai/dl/cestra-sdk.tgz
JavaScript
import { Agent } from '@cestra/sdk';

const agent = new Agent({ key: process.env.CESTRA_KEY });
await agent.send('Hello from Cestra');

new Agent() with no options reads CESTRA_KEY and CESTRA_URL from the environment.

Option Default
key process.env.CESTRA_KEY Your cs_live_ key
baseUrl process.env.CESTRA_URL or https://api.cestra.ai
maxRetries 4 Retries for 429, 5xx and network errors
timeoutMs 30000 Per request. Long-polls add their wait time
transport 'auto' 'websocket', 'poll' or 'auto' (WebSocket, then long-polling)

#Messages and notices

JavaScript
await agent.send('Build **passed** in 3m 12s');           // Markdown
await agent.notice('Backup done', 'success');            // info | success | warning | error
await agent.send('Quiet update', { notify: false });     // no push notification

#Asks

JavaScript
const ask = await agent.ask('Commit to main?', ['Approve', { label: 'Discard', style: 'destructive' }], {
  expiresIn: 3600,     // seconds, 60 s … 30 days
  allowText: false,    // true adds a text box
  detail: '3 files changed',
});

const reply = await ask.wait({ timeoutSec: 600 });
// { status: 'answered' | 'pending' | 'expired' | 'cancelled', answer, optionId, text, answeredBy, answeredAt }
if (reply.optionId === 'o1') { /* approved */ }

Options get ids o1, o2, … in order. wait() never throws on timeout. It returns the ask with status: 'pending'. Call ask.refresh() or ask.cancel() later. To pick an ask back up after a restart, use agent.getAsk(id).

#Jobs

JavaScript
const job = await agent.job('Reviewing spec', ['Read', 'Compare', 'Write up']);
await job.progress(40);
await job.step(0, 'done');
await job.step(1, 'running');
await job.done();                 // or: await job.fail('Spec file missing')

job.update({ title, pct, status, steps, step, error }) is the raw form. When someone presses Stop, you get a job.stop_requested event, and later updates fail with a 409 because the job is now cancelled.

#Artifacts

JavaScript
await agent.artifact({
  type: 'table',
  title: 'Coverage matrix',
  key: 'matrix',                       // same key again = version 2, 3, …
  data: { columns: ['Requirement', 'Status'], rows: [['Login', { value: 'ok', tone: 'ok' }]] },
});

Schemas for table, chart, markdown, form and html are on Artifacts.

#Files

JavaScript
await agent.file('report.pdf', { caption: 'Quote' });                // path, Buffer, Uint8Array or Blob, ≤ 50 MB
const bytes = await agent.download(fileId);                          // Buffer
const { name, mime, data } = await agent.downloadFile(fileId);

#Receiving events

JavaScript
agent.on('message', async (e) => {
  if (e.data.file) await agent.download(e.data.file.id);
  else await agent.send(`You said: ${e.data.text}`);
});
agent.on('ask.answered', (e) => console.log(e.data.ask_id, e.data.answer));
agent.on('job.stop_requested', (e) => stopWork(e.data.job_id));
agent.on('*', (e) => console.log(e.seq, e.type));

await agent.run();      // resolves after agent.stop()

run() acknowledges each event after its handlers resolve, so delivery is at-least-once. It reconnects with backoff. It rejects with a CestraError (status 401) if the key is invalid or revoked. on() returns an unsubscribe function. There are also lifecycle events: connected, disconnected and error.

Event types and payloads are listed under REST API → Events.

#Errors

Everything throws CestraError with status, code and message. Creates carry an automatic clientId, so retries never duplicate anything. Pass your own clientId to stay idempotent across restarts.

#Other calls

agent.me(), agent.setStatus('working' | 'online'), agent.disconnect(), agent.events({ after, wait }), agent.ack(seq), agent.jobById(id).