Webbu Docs

TypeScript / JavaScript

Call the Webbu API from TypeScript or JavaScript with fetch.

Webbu doesn't publish an npm SDK. The API is plain JSON over HTTPS, so fetch (built into Node.js 18+ and browsers) is all you need. To generate a typed client, point a generator such as openapi-typescript at webbu.dev/api/openapi.json.

A small client

const WEBBU_API = 'https://webbu.dev/api';

class WebbuError extends Error {
  constructor(public status: number, public body: { error?: string; message?: string; code?: string }) {
    super(body.message ?? body.error ?? `HTTP ${status}`);
  }
}

async function webbu<T>(path: string, init: RequestInit = {}): Promise<T> {
  const res = await fetch(`${WEBBU_API}${path}`, {
    ...init,
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.WEBBU_API_KEY!,
      ...init.headers,
    },
  });
  if (res.status === 204) return undefined as T;
  const body = await res.json().catch(() => ({}));
  if (!res.ok) throw new WebbuError(res.status, body);
  return body as T;
}

Projects and buffers

// Create a project (admin or owner)
await webbu('/v1/admin/projects', {
  method: 'POST',
  body: JSON.stringify({ projectId: 'p_shop', name: 'Shop' }),
});

// Create a buffer
await webbu('/v1/admin/buffers', {
  method: 'POST',
  body: JSON.stringify({
    bufferId: 'b_orders',
    projectId: 'p_shop',
    name: 'Order events',
    groupKey: { mode: 'jsonpath', path: '$.customerId' },
    batch: { maxItems: 100, maxBytes: 262144, idleTimeoutMs: 5000, maxWaitFromFirstMs: 30000 },
    delivery: { url: 'https://your-app.example.com/webhooks/batch' },
  }),
});

// List buffers of a project
const buffers = await webbu<Array<{ bufferId: string; name: string }>>(
  '/v1/admin/buffers?projectId=p_shop',
);

Ingest

Ingest doesn't need the API key. Pass a stable message ID to make retries safe:

const res = await fetch(`${WEBBU_API}/v1/ingest/p_shop/b_orders`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-Idempotency-Key': 'evt_123' },
  body: JSON.stringify({ event: 'order.created', customerId: 'cus_001' }),
});
// 202 → { status: 'accepted', messageId: 'evt_123', itemId: '…' }

Receiving batches

import express from 'express';

interface WebbuBatch<T = unknown> {
  webbu: { customerId: string; projectId: string; bufferId: string; groupKey: string; groupHash: string; segment: number };
  window: { firstItemAt: number; lastActivityAt: number; itemsCount: number };
  items: Array<{ messageId: string; receivedAt: number; payload: T }>;
}

const app = express();
app.use(express.json({ limit: '10mb' }));

app.post('/webhooks/batch', async (req, res) => {
  const batch = req.body as WebbuBatch;
  res.sendStatus(200); // acknowledge within 10 seconds

  for (const item of batch.items) {
    // deduplicate by item.messageId, then process item.payload
  }
});

Acknowledging before processing means a crash after the response loses the batch. If that matters, process (or persist) first and answer within 10 seconds.

Analytics and DLQ

const summary = await webbu<{ today: { ingestCount: number; deliveryCount: number } }>('/v1/analytics/summary');
const failed = await webbu<Array<{ dlqId: string; reason: string; lastError?: string }>>('/v1/dlq?bufferId=b_orders');

Errors

try {
  await webbu('/v1/admin/projects', { method: 'POST', body: JSON.stringify({ name: 'No ID' }) });
} catch (err) {
  if (err instanceof WebbuError) {
    console.error(err.status, err.body.error); // 400 "projectId and name are required"
  }
}

See Error Codes for every status and message.

On this page