Skip to content
Docs/SDKs

Agents & API

SDKs

Official clients for TypeScript/JavaScript and Python. Both have zero dependencies, cover every API operation, and include helpers for heartbeats, deploys and webhook verification.

Install

npm install @upbutler/sdk
# or: bun add @upbutler/sdk · pnpm add @upbutler/sdk

@upbutler/sdk runs on Node 18+, Bun, Deno, Cloudflare Workers and browsers. upbutler for Python needs 3.9+ and uses only the standard library. Create an API key under Settings → API keys, or let an agent create a workspace with agent.bootstrap (see Quickstart for agents).

Quickstart

import { UpButler } from "@upbutler/sdk";

const ub = new UpButler({ apiKey: process.env.UPBUTLER_API_KEY });

// Monitor a URL and publish it on a status page in one call
const { monitor, statusPageUrl } = await ub.services.add({
  name: "Search API",
  url: "https://api.example.com/health",
  statusPage: "acme",
});

// Operation `a.b.c` is the typed method ub.a.b.c(input)
const { data: monitors } = await ub.monitors.list();
await ub.incidents.create({ title: "Elevated errors", message: "Investigating 5xx on search", impact: "minor" });

// Public operations need no API key
const status = await new UpButler().public.status({ slug: "acme" });

Every operation in the REST API reference is a method: the operation id a.b.c becomes client.a.b.c(input), for example monitors.create, pages.push or public.subscriber.get. In TypeScript every input is typed. The types are generated from the same schemas the API validates against. Results are the JSON documented for each endpoint.

Client options

new UpButler({
  apiKey: "ub_live_...",            // optional for public operations
  baseUrl: "https://upbutler.com",  // default
  fetch,                            // custom fetch for tests or proxies
  maxRetries: 2,                    // 429 / 5xx / network errors, idempotent requests only
  timeoutMs: 30_000,
  autoIdempotency: false,           // true: random Idempotency-Key on every POST
});

// Per call
await ub.monitors.create(input, { idempotencyKey: "deploy-42-api", signal });

Requests are retried with exponential backoff (honoring Retry-After) on 429, 5xx and network errors, but only when repeating them is safe: GET, PUT, DELETE, and POSTs that carry an Idempotency-Key. The server replays the first response for a repeated key for 24 hours.

Low-level calls

await ub.call("monitors.get", { id: "mon_..." });           // by operation id
await ub.request("GET", "/monitors/:id", { id: "mon_..." });  // method + path under /api/v1

Path parameters are taken from the input. For GET and DELETE the remaining fields become the query string, otherwise a JSON body. OPERATIONS exports the full operation table (id, method, path, auth, scope, summary).

Errors

import { UpButlerError } from "@upbutler/sdk";

try {
  await ub.monitors.get({ id: "mon_missing" });
} catch (err) {
  if (err instanceof UpButlerError) {
    console.log(err.status, err.code, err.message, err.hint, err.details);
    // 404 "not_found" "Monitor not found" ...
  }
}

Failed calls throw UpButlerError (Python: UpButlerError exception) with the API's code, message, hint and details, plus the HTTP status. Network failures use code: "network_error" or "timeout" with status 0. See Errors & rate limits.

Heartbeats

import { heartbeat, withHeartbeat } from "@upbutler/sdk";

const HB = process.env.UPBUTLER_HB_URL!; // https://upbutler.com/hb/hb_Xf3kq9LmR2vT8wYzN4bC1dE7

await heartbeat(HB, { message: "backup 42GB ok", durationMs: 48210 });
await heartbeat(HB, { status: "down", message: "pg_dump exited 1" });

// Success with the duration, or failure with the error message (rethrown)
await withHeartbeat(HB, () => runBackup());

Heartbeat helpers need no API key, because the token is the secret. They accept the full ping URL (including the /fail and /api/v1/heartbeat/… forms) or the bare hb_… token. withHeartbeat reports the job's duration so it shows up in charts. If the ping itself fails, your job's result is unaffected. See Heartbeats.

Deploys

await ub.markDeploy({
  service: "api",
  version: "v1.42.0",
  environment: "production",
  commit: process.env.GITHUB_SHA,
});

Deploy markers appear on monitor timelines and give AI incident analysis the "what changed" context. markDeploy wraps deploys.create.

Verify webhooks

import { verifyWebhook } from "@upbutler/sdk";

export default {
  async fetch(req: Request) {
    const body = await req.text(); // the raw body, exactly as received
    const ok = await verifyWebhook(process.env.UPBUTLER_WEBHOOK_SECRET!, req.headers, body);
    if (!ok) return new Response("invalid signature", { status: 401 });
    const event = JSON.parse(body);
    // ...
    return new Response("ok");
  },
};

UpButler signs webhooks with Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). verifyWebhook(secret, headers, body, toleranceSec = 300) accepts a Headers object or a plain header record. In JavaScript it is async and uses WebCrypto, so the same code runs on Node, Bun, Deno and edge runtimes.

Other languages

The OpenAPI spec works with any OpenAPI generator. The CLI covers shell scripts and CI (see CLI), and agents can connect to the MCP server directly.