Validate a batch
Check a batch's rows before you create it and see each row as the server reads it. Nothing is saved or charged.
POST /api/batches/validateRequires templates:read. Nothing is created, rendered or charged. Use it to check a sheet before Create a batch, or to show a person what will be made.
Request body
The body is Create a batch's without name, collection, run and webhookUrl, plus stream.
| Field | Required | Description |
|---|---|---|
workspace | Yes | Workspace slug or id. |
pages, templates or flow | Exactly one | What the rows would be rendered on. |
rows | Yes | 1 to 1,000 rows, each with modifications and output. |
stream | No | true answers with a stream of events, one per row, instead of one JSON body. Defaults to false. |
curl --request POST "https://app.kalpana.one/api/batches/validate" \
--header "x-api-key: $KALPANA_API_KEY" \
--header "content-type: application/json" \
--data '{
"workspace": "acme",
"pages": ["k7fq2m8a", "p3rtv9we"],
"rows": [
{ "modifications": { "Headline": "Weekend offer", "Photo": "shoe.png" } },
{ "modifications": { "Headlin": "Last chance", "Photo": "missing.png" } }
]
}'Response
A row with problems does not make the call fail: you get 200 and the problems in issues. A body that is not valid in itself (no rows, two targets) is a 400.
{
"valid": false,
"rowCount": 2,
"validRows": 1,
"invalidRows": 1,
"jobCount": 4,
"creditCost": 4,
"issues": [
{ "row": 1, "field": "Headlin", "code": "unknown_layer", "message": "No layer \"Headlin\" in this template. Its layers: Headline, Photo, Badge." },
{ "row": 1, "field": "Photo", "code": "asset_not_found", "message": "No image \"missing.png\" in this workspace." }
],
"rows": [
{
"row": 0,
"pages": [
{ "page": "k7fq2m8a", "modifications": { "Headline": "Weekend offer", "Photo": { "asset": "0b0f5a3e-5b1c-4d65-9a5e-3f7d2f2f6c11", "name": "shoe.png" } } },
{ "page": "p3rtv9we", "modifications": { "Headline": "Weekend offer", "Photo": { "asset": "0b0f5a3e-5b1c-4d65-9a5e-3f7d2f2f6c11", "name": "shoe.png" } } }
]
},
{
"row": 1,
"pages": [
{ "page": "k7fq2m8a", "modifications": {} },
{ "page": "p3rtv9we", "modifications": {} }
]
}
]
}| Field | Description |
|---|---|
valid | true when there are no issues. |
rowCount, validRows, invalidRows | Row counts. |
jobCount | The files the batch would make: rows times pages. |
creditCost | What rendering the whole batch would cost. |
issues | Every problem across all rows: row (from 0), field, code and message. The codes are listed in Modifications. |
rows | Each row as the server read it, per page: the values that page would get, with image names resolved to assets and page keys applied. Use it to confirm a plain key reached the pages you meant. |
warnings | Present when there is something to know but nothing to fix, such as a video longer than the length a render allows. |
Streaming
With "stream": true the answer is a server-sent event stream, so a large sheet can be shown as it is checked. Each event is a message event with a JSON body. First one event per row, in order:
{
"type": "row",
"row": 1,
"valid": false,
"issues": [
{ "row": 1, "field": "Headlin", "code": "unknown_layer", "message": "No layer \"Headlin\" in this template. Its layers: Headline, Photo, Badge." }
],
"pages": [
{ "page": "k7fq2m8a", "modifications": {} },
{ "page": "p3rtv9we", "modifications": {} }
]
}Then one last event with the totals:
{ "type": "done", "total": 2, "valid": 1, "invalid": 1 }done carries warnings too when there are any. A row's pages shows only what the server could read, so the keys it refused are missing from it.
curl --no-buffer --request POST "https://app.kalpana.one/api/batches/validate" \
--header "x-api-key: $KALPANA_API_KEY" \
--header "content-type: application/json" \
--data '{ "workspace": "acme", "pages": ["k7fq2m8a"], "rows": [{ "modifications": { "Headline": "Weekend offer" } }], "stream": true }'An image is named by asset id or file name. To use a picture from a link, add it with Upload from a URL first and send its asset id.
Errors
| Status | Cause |
|---|---|
400 | Not exactly one of pages, templates and flow, no rows or more than 1,000, or an unknown field. |
403 | Your role or key lacks templates:read. |
404 | A page, template or flow is not in this workspace. |
413 | The body is larger than 4 MiB. |
Updated 9 Nov 2026