Update Pages

Add, duplicate, replace, edit, delete or reorder pages of a template without re-sending the whole design

Updated

PATCH
/v1/studio/templates/{templateId}/update-pages
curl -X PATCH "https://api.orshot.com/v1/studio/templates/<TEMPLATE_ID>/update-pages" \
  -H "Authorization: Bearer <ORSHOT_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [],
    "allow_reindex": true,
    "base_updated_at": "<BASE_UPDATED_AT>",
    "embed_user_id": "<EMBED_USER_ID>"
  }'

Change one or more pages of a template in a single request: add a page, duplicate one, replace one, change its settings (name, canvas, background, audio, subtitles, duration), delete it or move it, without re-sending the whole design to Update Template. Only pages whose content changed get a new thumbnail and animated preview; every other page keeps the one that already matches it.

Which update endpoint?#

You want to changeUse
Name, tags, canvas size, or the entire design at onceUpdate Template
Pages: add, duplicate, replace, edit settings, delete, reorderUpdate Pages (this page)
Layers on a page: add, replace, edit (incl. saved animation timing), delete, restackUpdate Layers
Values of existing dynamic layers: text, images, colors, style, positionUpdate Template Modifications

Endpoint#

Endpoint
https://api.orshot.com/v1/studio/templates/:templateId/update-pages

Query Parameters#

ParameterTypeRequiredDefaultDescription
includeThumbnailsBooleanNofalseWhen true, the response waits for the new thumbnails of the changed pages. Otherwise they are generated in the background.
includeVideoThumbnailsBooleanNofalseWhen true and at most 2 changed pages are animated, the response waits for their animated previews. Otherwise they are generated in the background.

Request Examples#

JavaScript
// Append a page (no existing page changes number)
await fetch("https://api.orshot.com/v1/studio/templates/12345/update-pages", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    base_updated_at: "2026-10-02T09:12:44.120Z", // from GET /v1/studio/templates/12345
    operations: [
      {
        op: "add",
        page_data: {
          name: "Pricing",
          canvas: { width: 1080, height: 1080, backgroundColor: "#0F172A" },
          elements: [
            { type: "text", content: "Plans from $9", x: 80, y: 120, width: 920, height: 120, parameterizable: true, parameterId: "price_line" }
          ]
        }
      }
    ]
  }),
});
JavaScript
// Rename page 2 and change its background; canvas merges, so width/height stay
await fetch("https://api.orshot.com/v1/studio/templates/12345/update-pages", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    operations: [
      // By page id (stays valid after pages are reordered). By number: { op: "update", page: 2, set: { ... } }
      { op: "update", page_id: "b2c3d4e5-6f78-9012-bcde-f23456789012", set: { name: "Offer", canvas: { backgroundColor: "#FFFFFF" } } }
    ]
  }),
});
JavaScript
// Copy the cover to the end, then move it to the front.
// Moving changes existing page numbers, so allow_reindex is required.
await fetch("https://api.orshot.com/v1/studio/templates/12345/update-pages", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    allow_reindex: true,
    operations: [
      { op: "duplicate", page: 1 }, // or page_id: "a1b2c3d4-..."
      { op: "move", page: 4, position: 1 } // the copy is page 4 at this point; its page_id is in the response
    ]
  }),
});
JSON
{
  "success": true,
  "changed": true,
  "template": {
    "id": 12345,
    "name": "Spring Sale Carousel",
    "updated_at": "2026-10-02T09:14:03.511Z",
    "pages": [
      { "page": 1, "page_id": "f0e1d2c3-...", "name": "Cover" },
      { "page": 2, "page_id": "a1b2c3d4-...", "name": "Cover" },
      { "page": 3, "page_id": "b2c3d4e5-...", "name": "Offer" },
      { "page": 4, "page_id": "c3d4e5f6-...", "name": "Back" }
    ]
  },
  "results": [
    { "index": 0, "op": "duplicate", "status": "applied", "page_id": "f0e1d2c3-...", "source_page_id": "a1b2c3d4-...", "page": 1 },
    { "index": 1, "op": "move", "status": "applied", "page_id": "f0e1d2c3-...", "page": 1 }
  ],
  "reindexed": [
    { "page_id": "a1b2c3d4-...", "from": 1, "to": 2 },
    { "page_id": "b2c3d4e5-...", "from": 2, "to": 3 },
    { "page_id": "c3d4e5f6-...", "from": 3, "to": 4 }
  ]
}
JSON
{
  "error": "These operations change the numbers of existing pages (page 1 → 2, page 2 → 3, page 3 → 4). Render calls that address pages by number (\"page2@headline\", includePages) would then hit different pages. Send \"allow_reindex\": true to confirm, or add/duplicate pages at the end instead. Nothing was saved.",
  "code": "reindex_not_allowed",
  "details": [
    { "index": null, "code": "reindex_not_allowed", "error": "These operations change the numbers of existing pages (…)" }
  ]
}

Rate Limits#

This endpoint shares the Enterprise limit of 30 requests per minute per API key with the other template endpoints.

URL Parameters#

ParameterTypeRequiredDescription
templateIdIntegerYesThe ID of the template to update

Request Body#

ParameterTypeRequiredDescription
operationsArrayYes1 to 50 page operations, applied in order. See Operations.
allow_reindexBooleanNoRequired (true) when the operations change the number of any existing page: deleting a page that isn't the last, inserting or duplicating somewhere other than the end, or moving.
base_updated_atStringNoThe template's updated_at from your last read. If the template changed since, nothing is written and you get 409. Without it, operations apply to the latest version.
embed_user_idStringNoAssign the template to an embed user, same as Update Template Modifications.

Operations#

Every operation has an op. Operations that act on an existing page name it with page_id (stable, from Get Studio Template) or page (1-based number). A page number means the number at that point in the request, after earlier operations; page_id never changes, so it is the safer target.

Page number or page id#

Anywhere an operation names a page, send either one:

FieldTypeWhat it is
page_idStringThe page's id. It never changes, so it keeps pointing at the same page after pages are added, deleted or reordered. Read it from Get Studio Template or from template.pages in any response of this endpoint.
pageIntegerThe page number, starting at 1. Simple for a one-off edit, but it changes when pages are added, deleted or moved.

If you send both, they must point at the same page, otherwise the request is refused with page_target_mismatch.

opFieldsWhat it does
addpage_data (page object), position (optional page number, default after the last page)Adds a new page. Missing page and layer ids are generated.
duplicatepage_id or page, position (optional, default after the last page)Copies a page with new page and layer ids.
replacepage_id or page, page_dataReplaces the page's whole content. The page keeps its id.
updatepage_id or page, set (object)Changes page settings: name, canvas (merged, so { "backgroundColor": "#000" } keeps the size), audioTracks, subtitle, videoDuration, motion. A null value removes the field. Layers can't be changed here; use Update Layers.
deletepage_id or pageDeletes the page. A template always keeps at least one page.
movepage_id or page, positionMoves the page to that page number.

Page objects use the same shape as pages_data in Create Studio Template. Layers may use the flat x, y, width, height and src fields.

Page numbers and allow_reindex#

Render calls address pages by number: "page2@headline" modification keys, includePages, and the page field of Update Template Modifications. If page 2 becomes page 3, those calls silently hit a different page. So any operation list that changes the number of an existing page is refused unless you send "allow_reindex": true, and the response's reindexed array lists every page that moved. Adding or duplicating at the end, and deleting the last page, never renumber anything.

Response Fields#

FieldTypeDescription
successBooleantrue when the request was valid
changedBooleanfalse when the operations changed nothing (for example setting a name to its current value). Nothing is written and the version does not move.
template.idIntegerTemplate ID
template.nameStringTemplate name
template.updated_atStringThe new version. Send it as base_updated_at on your next write.
template.pagesArrayEvery page after the operations: page (number), page_id, name
template.thumbnailsObjectOnly with ?includeThumbnails=true: the new thumbnails of the changed pages
resultsArrayOne entry per operation: index, op, status (applied or unchanged), page (final number), page_id, and source_page_id for duplicate
reindexedArrayOnly when page numbers changed: { page_id, from, to } per existing page that moved
warningsArrayOnly when present: non-blocking notes, such as unknown motion keys

Errors#

Every error explains what to change. A 400 means nothing was saved: operations are all-or-nothing.

StatuscodeWhen
400operations_required, operations_empty, too_many_operationsoperations is missing, empty, or longer than 50
400unknown_op, invalid_operationAn operation isn't an object with a known op
400page_object_in_pageA page object was sent as page; send it as page_data
400page_target_required, page_not_found, invalid_page, invalid_page_id, page_target_mismatchThe target page is missing, doesn't exist, or page and page_id disagree. The message lists the pages that exist.
400invalid_page_object, duplicate_page_id, page_id_mismatchThe page object is malformed, its id is taken, or it doesn't match the page it replaces
400set_required, field_not_settable, invalid_canvasupdate has no set, tries to change elements, id or generated fields, or has an invalid canvas
400position_required, invalid_positionmove without a valid position
400last_page, too_many_pages, template_too_largeDeleting the only page, going past 200 pages, or leaving a design larger than 10 MB
400no_pagesThe template has no pages yet; send the full design with Update Template first
400reindex_not_allowed, invalid_allow_reindexExisting pages would be renumbered without "allow_reindex": true
400invalid_motionThe operations introduce a structural motion error
403The API key, plan or trial access doesn't include this endpoint
404The template isn't in your workspace
409base_updated_at is stale: fetch the template again and redo the operations on that version
423An approval flow locks the template; see Approvals
Was this page helpful?

Ready to automate?

Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.

Get your API key
  • Image, PDF and video generation via API
  • Visual editor with AI and smart layouts
  • Zapier, Make, MCP and 50+ integrations
  • White-label embed for your own app
  • 100 free credits a month, no credit card required