
Event Types
| Event Type | Description |
|---|---|
workbook.preview.export | Export sent to preview webhook endpoints |
workbook.production.export | Export sent to production webhook endpoints |
version field in the payload indicates the format:
1.0= Flat payload2.0= Nested payload
Flat vs Nested
Flat (v1.0) sends each sheet as a separate array of row objects. Use this when:- You want a simple, tabular structure
- Sheets are independent or you handle relationships in your own system
- You need backwards compatibility with existing integrations
- Your sheets have foreign key relationships (link rules)
- You want hierarchical data delivered as a single tree structure
- You need child records grouped under their parent records
Flat Payload (v1.0)
Flat Payload Fields
| Field | Type | Description |
|---|---|---|
version | string | Always "1.0" for flat payloads |
workbook_id | string | The ID of the exported workbook |
external_id | string | null | Optional external identifier from the company |
batch | object | Batch metadata (see Batching) |
sheets | array | Array of sheet objects |
sheets[].id | string | Sheet ID |
sheets[].name | string | Sheet name |
sheets[].row_count | number | Number of rows in this batch for this sheet |
sheets[].start_row_index | number | Starting row index (0-based) |
sheets[].end_row_index | number | Ending row index (inclusive) |
sheets[].data | array | Array of row objects with column values, errors, and row_index |
data contains your column names as keys, plus:
errors— an array of validation error strings (empty if the row is valid)row_index— the original row position in the sheet
Nested Payload (v2.0)
Nested Payload Fields
| Field | Type | Description |
|---|---|---|
version | string | Always "2.0" for nested payloads |
workbook_id | string | The ID of the exported workbook |
company_name | string | Name of the company that owns the workbook |
external_id | string | null | Optional external identifier from the company |
export_mode | string | Always "nested" |
batch | object | Batch metadata (see Batching) |
root_sheets | string[] | Names of the root-level sheets in the hierarchy |
data | object | Keyed by root sheet name, each containing an array of nested records |
Nested Record Structure
Each record in the nested payload contains:| Field | Type | Description |
|---|---|---|
[column] | any | Column values from the sheet |
errors | array | Validation errors for this row |
row_index | number | Original row position in the sheet |
children | object | Child records keyed by child sheet name. Empty {} if no children. |
Batching
Large exports are split into multiple batches to stay within payload size limits. Each webhook delivery represents one batch.Batch Object
| Field | Type | Description |
|---|---|---|
id | string | Unique batch ID shared across all batches in the same export (e.g., batch_a1b2c3...) |
index | number | 0-based index of this batch |
total | number | Total number of batches in the export |
size | number | Number of records in this batch |
timestamp | string | ISO 8601 timestamp of when this batch was created |
Batch Size Limits
Flat (v1.0)
Batched by payload size. Each batch targets 256 KB with a soft limit of 512 KB. Large sheets are automatically split across multiple batches using the
start_row_index and end_row_index fields.Nested (v2.0)
Batched by root record count. Each batch contains up to 50 root records to keep parent-child groups atomic — a root record and all its nested children are always delivered together in the same batch.
Reassembling Batched Exports
To reconstruct a complete export from multiple batches:- Use
batch.idto group batches belonging to the same export - Use
batch.indexto order them (0-based) - Check
batch.index === batch.total - 1to confirm you’ve received the final batch - Flat: concatenate each sheet’s
dataarrays, usingstart_row_index/end_row_indexto position rows correctly - Nested: concatenate the
dataarrays for each root sheet name across batches