biga

Build an extension

Connect your own service to a biga session and react to new checkpoints.

Run your extension as a script, bot, or service on your own infrastructure. It can read checkpoint events from a biga session you own. Use those events to update a dashboard, send a team notification, or start another job.

Create a credential

Start a broadcast once. From the same directory, run:

biga extensions create "Build monitor"

Copy the feed URL and the token into the extension's server-side secret store. The token appears only at creation. It grants read access to the checkpoint feed for that session. It carries no authority to publish, manage other extensions, or fetch files. The session's existing viewer permissions still apply to its ordinary share link.

Use biga extensions list to see the installed extensions, and run biga extensions revoke <id> to disconnect one. Add --dir <path> to manage a different local broadcast.

Read the feed

Send a GET request to the feed URL using Authorization: Bearer <token>. An owner who creates credentials through the API instead of the CLI can use the endpoints below. Include the server's access key on management requests when required.

MethodSession API pathCredential
POST/api/sessions/{id}/extensions with JSON { "name": "Build monitor" }Session owner token
GET/api/sessions/{id}/extensionsSession owner token
DELETE/api/sessions/{id}/extensions/{extensionId}Session owner token
GET/api/sessions/{id}/extension-eventsExtension token

The feed returns data.events and data.nextAfter in the usual API response envelope. Start with after=-1 to read all retained checkpoints in order. Each request returns events with sequence numbers greater than after. Use limit to choose a page size between 1 and 500. The default is 100.

{
  "ok": true,
  "data": {
    "events": [
      {
        "id": "my-session:0",
        "type": "checkpoint.created",
        "version": 1,
        "sessionId": "my-session",
        "seq": 0,
        "createdAt": "2026-01-01T00:00:00Z",
        "filesChanged": 3,
        "additions": 42,
        "deletions": 0
      }
    ],
    "nextAfter": 0
  }
}

Persist the cursor after processing succeeds. Keep a separate cursor for each installation. A retry can return events you've already seen, so use the event ID as an idempotency key within that installation. A checkpoint can come from an initial snapshot or a later batch of edits. File names, hashes, contents, and diff bodies are excluded from the payload. A marker created with biga checkpoint mark emits a version 1 checkpoint.marked event with the name the owner chose. Its file change counts are zero. Use its session ID and sequence to open /session/{id}?checkpoint={seq} on the viewer origin. Consumers can skip event types they do not handle and still advance the cursor.

Try a consumer

Save this as extension.mjs. Supply BIGA_FEED_URL and BIGA_EXTENSION_TOKEN in the process environment, then run node extension.mjs. Run it periodically with the same working directory to resume from the saved cursor. Use a separate working directory per installation and run one process at a time.

import { readFile, rename, writeFile } from "node:fs/promises";

const { BIGA_FEED_URL, BIGA_EXTENSION_TOKEN } = process.env;
if (!BIGA_FEED_URL || !BIGA_EXTENSION_TOKEN) {
  throw new Error("Set BIGA_FEED_URL and BIGA_EXTENSION_TOKEN");
}

const cursorFile = "biga-extension.cursor";
const cursor = await readFile(cursorFile, "utf8").catch((error) => {
  if (error.code === "ENOENT") return "-1";
  throw error;
});

const url = new URL(BIGA_FEED_URL);
url.searchParams.set("after", cursor.trim());
const response = await fetch(url, {
  headers: { Authorization: `Bearer ${BIGA_EXTENSION_TOKEN}` },
  redirect: "error",
  signal: AbortSignal.timeout(10000),
});
if (!response.ok) throw new Error(`biga feed returned HTTP ${response.status}`);

const { data } = await response.json();
for (const event of data.events) {
  if (event.type === "checkpoint.created" && event.version === 1) {
    console.log(event.id, event.filesChanged);
  }
}
await writeFile(`${cursorFile}.tmp`, String(data.nextAfter));
await rename(`${cursorFile}.tmp`, cursorFile);

In a long-running consumer, fetch the next page immediately while you are catching up. Wait between polls once caught up, honor Retry-After when rate limited, and stop polling when the token is revoked. The feed is available while the session is retained, including when the broadcast is stopped.