DEVELOPER DOCS

SDKs

Sample code for common languages plus integration patterns.

Language samples

Direct engine calls. From a web app, prefer POST /api/jobs (it provisions signed URLs automatically).

cURL
curl -X POST https://api.babbage.owlgcorp.com/v1/jobs \
  -H "X-API-Key: $BABBAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
Node.js
const res = await fetch("https://api.babbage.owlgcorp.com/v1/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.BABBAGE_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    jobId: crypto.randomUUID(),
    workspaceId: "<wsId>",
    inputTemplateSignedUrl: "https://…/source.pptx",
    outputSignedUrl: "https://…/result.pptx",
    outputType: "pptx",
    data: { name: "Samsung", products: [{ name: "Basic", price: 29000 }] },
  }),
});
if (!res.ok) throw new Error(`engine ${res.status}`);
const { jobId, status } = await res.json();
Python
import os, uuid, requests

res = requests.post(
    "https://api.babbage.owlgcorp.com/v1/jobs",
    headers={"X-API-Key": os.environ["BABBAGE_API_KEY"]},
    json={
        "jobId": str(uuid.uuid4()),
        "workspaceId": "<wsId>",
        "inputTemplateSignedUrl": "https://…/source.pptx",
        "outputSignedUrl": "https://…/result.pptx",
        "outputType": "pptx",
        "data": {"name": "Samsung", "products": [{"name": "Basic", "price": 29000}]},
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())

inputTemplateSignedUrl and outputSignedUrl are auto-issued by web's POST /api/jobs. External callers should pre-issue Firebase Storage V4 signed URLs.

Polling a job to completion

POST /v1/jobs returns immediately with status: running. Either subscribe to the Firestore job doc or poll the web API every 1–2 seconds.

Node.js
async function waitForJob(workspaceId, jobId, idToken) {
  const deadline = Date.now() + 5 * 60_000; // 5 min
  while (Date.now() < deadline) {
    const r = await fetch(
      `https://babbage.owlgcorp.com/api/jobs/${jobId}?workspaceId=${workspaceId}`,
      { headers: { Authorization: `Bearer ${idToken}` } },
    );
    if (!r.ok) throw new Error(`status ${r.status}`);
    const job = await r.json();
    if (job.status === 'done')  return job;
    if (job.status === 'failed') throw new Error(job.error ?? 'failed');
    await new Promise((res) => setTimeout(res, 1500));
  }
  throw new Error('timeout');
}

For long-running batches, prefer Firestore onSnapshot over polling — it pushes updates the moment they happen.

Retrying on 5xx / 429

5xx and 429 are safe to retry. 4xx (except 429) signal client-side bugs — surface them immediately instead of looping.

Node.js — exponential backoff
async function postJob(body, key, attempts = 4) {
  let delay = 500;
  for (let i = 0; i < attempts; i++) {
    const r = await fetch("https://api.babbage.owlgcorp.com/v1/jobs", {
      method: "POST",
      headers: {
        "X-API-Key": key,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });
    if (r.ok) return r.json();
    // 4xx (except 429) — no retry, surface immediately
    if (r.status >= 400 && r.status < 500 && r.status !== 429) {
      throw new Error(`engine ${r.status}: ${await r.text()}`);
    }
    if (i === attempts - 1) throw new Error(`engine ${r.status} after retries`);
    await new Promise((res) => setTimeout(res, delay + Math.random() * 200));
    delay *= 2;
  }
  throw new Error('unreachable');
}

Use exponential backoff with jitter. Cap total attempts (e.g. 4) so failures don't pile up.

Error handling checklist

Every integration should handle these distinctly:

  • 401 — refresh the ID token or rotate the API key. Don't retry without action.
  • 402 — quota exceeded. Show an upgrade prompt or queue locally until next cycle.
  • 5xx / network errors — retry with backoff, then surface to the user.
  • 200 + later job.status === 'failed' — read job.error and decide whether to retry the bind.

Limits & best practices

  • Signed URLs live for 5 minutes. Adjust ttlMs to extend.
  • Single PPTX is capped at 100 MB. Split larger files.
  • Throttle concurrent jobs to under Cloud Run concurrency (default 20). The engine rejects beyond that — no queueing.
  • Image URLs must be HTTPS and publicly fetchable (or signed).
  • Placeholders can hold non-ASCII text natively. Keys must match JSON exactly.
  • Failed jobs are NOT counted toward your monthly quota.

locale=en