
Events
| Event | When it fires |
|---|---|
workbook.production.export | A production export from a workbook. |
workbook.preview.export | A staging/preview export from a workbook. |
Set up an endpoint
Webhook delivery is powered by Svix.- Go to Settings → Webhooks. This opens the Svix portal.
- Click Add endpoint and paste the URL your app listens on.
- Subscribe to
workbook.production.exportand/orworkbook.preview.export. - Save.
Testing without an endpoint
For early testing, the Svix portal offers Svix Play — a temporary URL that catches and displays payloads in a web UI. Useful for proving your export fires before your real endpoint is wired up.Watch deliveries
From the Svix portal you can:- See every webhook delivered to each endpoint.
- Inspect the exact payload sent.
- Replay failed deliveries.
- Grab the signing secret your developers need to verify webhooks.
Verify the signature
Every webhook is signed. Verify the signature before trusting the payload:Retries
If your endpoint returns a non-2xx response, Svix retries automatically on a backoff schedule. Failed deliveries are visible in the Svix portal where you can replay them manually. Make your handler idempotent — retries can deliver the same event twice.Payload format
Workbook exports come in one of two shapes, depending on whether you chose flat or nested mode in the export dialog. Theversion field tells you which one:
1.0— flat2.0— nested
Flat payload (v1.0)
Each sheet is a separate array of row objects. Good when sheets are independent or when you handle relationships in your own system.| Field | Type | Description |
|---|---|---|
version | string | Always "1.0" for flat payloads. |
workbook_id | string | ID of the exported workbook. |
external_id | string | null | The external_id you passed when creating the company. |
batch | object | Batch metadata (see Batching). |
sheets[] | array | Sheet objects. |
sheets[].id | string | Sheet ID. |
sheets[].name | string | Sheet name. |
sheets[].row_count | number | Number of rows in this batch. |
sheets[].start_row_index | number | Starting row index (0-based). |
sheets[].end_row_index | number | Ending row index (inclusive). |
sheets[].data[] | array | Row objects: column keys + errors array + row_index. |
Nested payload (v2.0)
Child sheets are nested under their parents using link rules. Good when sheets have foreign-key relationships and you want them grouped.| Field | Type | Description |
|---|---|---|
version | string | Always "2.0". |
workbook_id | string | ID of the exported workbook. |
company_name | string | Name of the company that owns the workbook. |
external_id | string | null | The external_id you passed at company creation. |
export_mode | string | Always "nested". |
batch | object | Batch metadata. |
root_sheets[] | string[] | Names of the root sheets in the hierarchy. |
data | object | Keyed by root sheet name. Each value is an array of records. |
errors array, a row_index, and a children object keyed by child sheet name. Vern handles circular link rules cleanly — back-edges are treated as references and don’t cause infinite recursion.
Batching
Large exports split across multiple batches to stay within payload size limits. Each webhook delivery is one batch.| Field | Type | Description |
|---|---|---|
batch.id | string | Shared across all batches in the same export. |
batch.index | number | 0-based index of this batch. |
batch.total | number | Total number of batches. |
batch.size | number | Records in this batch. |
batch.timestamp | string | ISO 8601 creation time. |
start_row_index / end_row_index.
Nested (v2.0) batches by root record count — up to 50 roots per batch. A root and all its descendants are always delivered together.
Reassembling batches
- Group by
batch.id. - Order by
batch.index. batch.index === batch.total - 1means it’s the last one.- Flat: concatenate each sheet’s
dataarrays in index order. - Nested: concatenate the
dataarrays for each root sheet name across batches.
Best practices
- Verify the signature on every request.
- Make your handler idempotent — retries can deliver the same event twice.
- Return 200 quickly. Do heavy work asynchronously.
- Store the raw payload until you’ve fully processed it.
- Match the
external_idback to your customer record on receipt.