ForgeAPI keys

Examples

Each runs as is with FORGE_API_KEY set, and runs on your credits.

Ask once and print the answer

curl -s https://api.forge.team/v1/conversations \
  -H "Authorization: Bearer $FORGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Summarise the plot of Hamlet in two sentences","reply":"required"}' \
  | jq -r '.replies[].text'

Stream a reply as it is said

const res = await fetch("https://api.forge.team/v1/conversations", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FORGE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ text: "Write a short poem about steel", stream: true }),
});

const decoder = new TextDecoder();
let buffer = "";
for await (const chunk of res.body!) {
  buffer += decoder.decode(chunk, { stream: true });
  let end;
  while ((end = buffer.indexOf("\n\n")) >= 0) {
    const block = buffer.slice(0, end);
    buffer = buffer.slice(end + 2);
    const event = /^event: (.*)$/m.exec(block)?.[1];
    const data = JSON.parse(/^data: (.*)$/m.exec(block)?.[1] ?? "null");
    if (event === "message") console.log(data.text);
    if (event === "done") console.log(`(${data.replies} replies)`);
  }
}

Start long work, then check on it

import os, time, requests

api = "https://api.forge.team/v1"
auth = {"Authorization": f"Bearer {os.environ['FORGE_API_KEY']}"}

started = requests.post(f"{api}/conversations", headers=auth, json={
    "text": "Research the history of Damascus steel and write a one-page summary",
    "reply": "none",
}).json()

while True:
    status = requests.get(f"{api}/conversations/{started['conversationId']}", headers=auth).json()
    if status["pendingGates"]:
        print("The agent is waiting on you in the app:", status["pendingGates"][0]["prompt"])
        break
    if not status["busy"]:
        print(status["lastAssistantText"])
        break
    time.sleep(10)

Read a whole conversation

const api = "https://api.forge.team/v1";
const headers = { Authorization: `Bearer ${process.env.FORGE_API_KEY}` };
const id = process.argv[2];

const pages = [];
let before: string | null = null;
do {
  const url = `${api}/conversations/${id}/messages?limit=200${before ? `&before=${before}` : ""}`;
  const page = await (await fetch(url, { headers })).json();
  pages.unshift(page.messages);
  before = page.before;
} while (before);

for (const m of pages.flat()) console.log(`${m.role}: ${m.text}`);

Follow conversations live

import WebSocket from "ws";

const ws = new WebSocket("wss://api.forge.team/v1/ws", {
  headers: { Authorization: `Bearer ${process.env.FORGE_API_KEY}` },
});

ws.on("open", () => ws.send(JSON.stringify({ type: "send", id: "1", text: "Plan a three-day trip to Kyoto" })));
ws.on("message", (raw) => {
  const frame = JSON.parse(raw.toString());
  if (frame.type === "ack") ws.send(JSON.stringify({ type: "subscribe", conversationId: frame.conversationId }));
  if (frame.type === "message") console.log(frame.text);
  if (frame.type === "status") ws.close();
});

A typed client

Generate one from the spec at https://api.forge.team/v1/openapi.json:

// npx openapi-typescript https://api.forge.team/v1/openapi.json -o forge.d.ts
// npm i openapi-fetch
import createClient from "openapi-fetch";
import type { paths } from "./forge";

const forge = createClient<paths>({
  baseUrl: "https://api.forge.team/v1",
  headers: { Authorization: `Bearer ${process.env.FORGE_API_KEY}` },
});

const { data } = await forge.GET("/conversations", { params: { query: { limit: 5 } } });
for (const c of data!.conversations) console.log(c.title);