Kalpana Docs
Resources

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/validate

Requires 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.

FieldRequiredDescription
workspaceYesWorkspace slug or id.
pages, templates or flowExactly oneWhat the rows would be rendered on.
rowsYes1 to 1,000 rows, each with modifications and output.
streamNotrue 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": {} }
      ]
    }
  ]
}
FieldDescription
validtrue when there are no issues.
rowCount, validRows, invalidRowsRow counts.
jobCountThe files the batch would make: rows times pages.
creditCostWhat rendering the whole batch would cost.
issuesEvery problem across all rows: row (from 0), field, code and message. The codes are listed in Modifications.
rowsEach 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.
warningsPresent 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

StatusCause
400Not exactly one of pages, templates and flow, no rows or more than 1,000, or an unknown field.
403Your role or key lacks templates:read.
404A page, template or flow is not in this workspace.
413The body is larger than 4 MiB.
Was this page helpful?

Updated 9 Nov 2026

On this page