Two directions, no backend required either way. Ingest URLs bring rows in: any service that can send a webhook can fill a sheet. Webhooks send changes out: we call your endpoint when rows change.
In: ingest URLs
Create one (needs auth), then hand the URL to Stripe, a form, CI, a sensor, anything:
An empty sheet takes its header row from the first event. Arrays of objects append several rows; {"rows": [[...]]} appends raw rows. Every ingest fires the workbook's row.added webhooks, so data can flow straight through.
Out: webhooks
Subscribe an https URL to a workbook and we call it when rows change. Events: row.added, row.changed, row.deleted. Human edits in the grid and agent writes over the API fire the same events. Failed deliveries retry 3 times with backoff. Webhooks require an account: create one free, then use an API key (or a signed-in session).
Every delivery carries X-SheetsFast-Signature: t=<unix>,v1=<hex> where v1 is HMAC-SHA256 of `${t}.${rawBody}` with your signing secret.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret: string, signature: string, rawBody: string): boolean {
const t = signature.match(/t=(\d+)/)?.[1];
const v1 = signature.match(/v1=([a-f0-9]+)/)?.[1];
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5 min tolerance
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
Test and inspect
# send a sample payload (marked "test": true) through the real pipeline
curl -X POST .../workbooks/{id}/webhooks/{whk_id}/test
# recent deliveries with status
curl .../workbooks/{id}/webhooks/{whk_id}/deliveries
Delivery statuses: enqueued, delivered, failed. The workbook page has the same controls under Webhooks.