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.