Create Studio Template

Create a single studio template programmatically via API

Updated

POST
/v1/studio/templates/create
curl -X POST "https://api.orshot.com/v1/studio/templates/create" \
  -H "Authorization: Bearer <ORSHOT_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "<NAME>",
    "canvas_width": 1,
    "canvas_height": 1,
    "description": "<DESCRIPTION>"
  }'

Create a new studio template in your workspace programmatically. This endpoint allows you to define the template structure, dimensions, and optionally include pre-built pages with elements.

For detailed information about the template data structure, see Anatomy of a Template.

Endpoint#

Endpoint
https://api.orshot.com/v1/studio/templates/create

Query Parameters#

ParameterTypeRequiredDefaultDescription
includeThumbnailsBooleanNofalseWhen true, the endpoint renders a thumbnail for every page and waits for all uploads before responding. When false or absent, thumbnails are generated asynchronously after the response is sent.
includeVideoThumbnailsBooleanNofalseWhen true and at most 2 pages are animated, the response waits for their animated previews and returns them in template.pages_meta[].video_thumbnail_url. Otherwise they are generated in the background.

Request#

JavaScript
// Create a blank template with specified dimensions
await fetch("https://api.orshot.com/v1/studio/templates/create", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    name: "Product Banner",
    description: "E-commerce product showcase banner",
    canvas_width: 1200,
    canvas_height: 628,
    embed_user_id: "your-external-user-id" // Optional
  }),
});
JavaScript
// Create a template with pre-built page structure
await fetch("https://api.orshot.com/v1/studio/templates/create", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    name: "Social Media Post",
    description: "Instagram post template",
    canvas_width: 1080,
    canvas_height: 1080,
    pages_data: [
      {
        id: "page-1",
        name: "Page 1",
        canvas: {
          width: 1080,
          height: 1080,
          backgroundColor: "#ffffff"
        },
        elements: [
          {
            id: "headline-element",
            name: "Headline",
            type: "text",
            position: { x: 100, y: 200 },
            dimensions: { width: 880, height: 100 },
            content: "Your Headline Here",
            parameterId: "headline",
            parameterizable: true
          },
          {
            id: "image-element",
            name: "Product Image",
            type: "image",
            position: { x: 100, y: 350 },
            dimensions: { width: 880, height: 500 },
            content: "https://example.com/placeholder.png",
            parameterId: "product_image",
            parameterizable: true
          }
        ],
        modifications: []
      }
    ]
  }),
});
JSON
{
  "success": true,
  "template": {
    "id": 12345,
    "name": "Social Media Post",
    "canvas_width": 1080,
    "canvas_height": 1080,
    "tags": [],
    "pages_count": 1,
    "modifications": [
      {
        "key": "headline",
        "id": "headline",
        "type": "text",
        "description": "Headline — Text content",
        "helpText": "Headline — Text content",
        "help_text": "Headline — Text content",
        "element_name": "Headline",
        "example": "Your Headline Here",
        "page_number": 1,
        "page_id": "page-1"
      },
      {
        "key": "product_image",
        "id": "product_image",
        "type": "imageUrl",
        "description": "Product Image — Image URL",
        "helpText": "Product Image — Image URL",
        "help_text": "Product Image — Image URL",
        "element_name": "Product Image",
        "example": "https://example.com/placeholder.png",
        "page_number": 1,
        "page_id": "page-1"
      }
    ],
    "created_at": "2026-01-29T10:30:00.000Z"
  }
}
JSON
{
  "success": true,
  "template": {
    "id": 12345,
    "name": "Social Media Post",
    "canvas_width": 1080,
    "canvas_height": 1080,
    "tags": [],
    "pages_count": 1,
    "modifications": [
      {
        "key": "headline",
        "id": "headline",
        "type": "text",
        "description": "Headline — Text content",
        "helpText": "Headline — Text content",
        "help_text": "Headline — Text content",
        "element_name": "Headline",
        "example": "Your Headline Here",
        "page_number": 1,
        "page_id": "page-1"
      }
    ],
    "created_at": "2026-01-29T10:30:00.000Z",
    "thumbnails": {
      "success": true,
      "pages": [
        {
          "page": 1,
          "page_id": "page-1",
          "thumbnail_url": "https://storage.orshot.com/cloud/w-50/renders/thumbnails/abc123.png",
          "error": null
        }
      ]
    }
  }
}

Rate Limits#

LimitValue
Requests per minute30

The limit is 30 requests per minute per API key, shared by every /v1/studio template endpoint, including template reads. Renders are not counted.

Request Body Parameters#

ParameterTypeRequiredDescription
nameStringYesName of the template (max 255 characters)
descriptionStringNoDescription of the template
canvas_widthIntegerYesWidth of the canvas in pixels (1-5000)
canvas_heightIntegerYesHeight of the canvas in pixels (1-5000)
pages_dataArrayNoArray of page objects with elements. If not provided, a blank page is created
filmObjectNoMakes the template a film: the pages play as one video, joined by each page's transitionIn. { "v": 1, "background"?, "output"?: { "profile": "film", "fps"? }, "tracks"? }. Validated with the pages; problems fail with 400
tagsArrayNoArray of tag strings to categorize the template (e.g. ["social", "marketing"])
thumbnail_urlStringNoIgnored on create. Orshot renders a thumbnail for each page from your design
embed_user_idStringNoAssign the template to an embed user. Accepts your external user ID or an Orshot internal ID (prefixed with eui_). Needs an embed with Per-User Data turned on and a plan that includes per-user data. A new external ID creates the embed user and counts toward your plan's embed user limit. External IDs use letters, numbers, _, -, @ and ., up to 256 characters

Pages Data Structure#

Each page in the pages_data array should follow this structure:

FieldTypeDescription
idStringOptional. The page's id, unique in the template, used by links and page operations. Generated when omitted; send it back unchanged when you update the template. A page id used twice gets a new one, with a warning in the response
nameStringName of the page (e.g., "Page 1")
canvasObjectCanvas settings { width, height, backgroundColor }
elementsArrayArray of text, image, shape, video, waveform, and other elements
motionObjectOptional page-level motion: look (grain, vignette), camera moves and animated groups
transitionInObjectOptional, films only: how this page starts from the previous one, e.g. { "type": "whip", "duration": 0.4 } or { "type": "grow", "from": "<layer id on the previous page>" }. See Film
modificationsArrayGenerated by Orshot from the elements. You don't need to send it
thumbnail_urlStringGenerated by Orshot. You don't need to send it
audioTracksArrayExisting audio, generated narration, and ambient tracks
subtitleObjectBurned-in subtitle configuration
videoDurationNumberSaved video duration for this page in seconds

Elements may carry transitions (animations) and motion (cinematic behaviors) for video output. Motion params are validated on write: structural problems fail with a 400 listing each issue; unknown or misspelled params are accepted and reported in the response warnings array with a did-you-mean suggestion.

Use audioTracks[].tts for generated narration and audioTracks[].sfx for ambient audio. Add tts.parameterId when the spoken script should be dynamic. See Template Anatomy for the complete page, audio, subtitle, and waveform schemas.

Response Fields#

FieldTypeDescription
successBooleanWhether the operation was successful
template.idIntegerUnique identifier for the created template
template.nameStringName of the template
template.canvas_widthIntegerCanvas width in pixels
template.canvas_heightIntegerCanvas height in pixels
template.tagsArrayTags assigned to the template
template.pages_countIntegerNumber of pages in the template
template.modificationsArrayList of parameterizable modifications extracted from elements
warningsArrayNon-blocking authoring warnings (e.g. an unknown motion param with a suggested fix). Present only when there is something to fix
template.created_atStringISO timestamp of creation
template.thumbnailsObjectOnly present when ?includeThumbnails=true. See Thumbnails Object Structure below
template.pages_metaArrayOnly present when ?includeVideoThumbnails=true rendered the animated previews in the request. Each page's preview is in video_thumbnail_url

Modification Object Structure#

Each modification in template.modifications contains:

FieldTypeDescription
keyStringUnique key for the modification. For multi-page templates this is prefixed, e.g. page1@headline
idStringSame as key
typeStringType of modification (text, voiceText, imageUrl, videoUrl, backgroundColor, fill, color, stroke)
descriptionStringHuman-readable description, e.g. "Headline — Text content" or "Image URL" when no element name is set
help_textStringSame as description
helpTextStringDeprecated copy of help_text
element_nameStringLayer name of the element in the editor (only present when the element has a name)
exampleStringDefault/example value for the field
page_numberInteger1-based page number this modification belongs to
page_idStringStable id of the page this modification belongs to — use this to reliably map modifications to pages after reorder

Error Responses#

Status CodeErrorDescription
400Validation failedInvalid request body parameters, including a film block or page transitionIn that fails film validation (details names each problem by path)
400Invalid inputtags is not an array of strings, or embed_user_id, stock media or generated narration could not be resolved. The error names the problem
401UnauthorizedThe OAuth token is invalid or expired. Refresh it
403Access ForbiddenInvalid or missing API key, your plan does not include this endpoint, or a payment is overdue (see code). Per-User Data errors for embed_user_id also return 403
429Rate limit exceededToo many requests (max 30/min)
500Internal server errorServer-side error

Thumbnails Object Structure#

When you pass ?includeThumbnails=true, the endpoint renders a PNG thumbnail for each page using the same scale convention as the Studio editor (capped at 1200×1200, never upscaled). The returned template.thumbnails object has this shape:

JSON
{
  "success": true,
  "pages": [
    { "page": 1, "page_id": "...", "thumbnail_url": "https://...", "error": null },
    { "page": 2, "page_id": "...", "thumbnail_url": "https://...", "error": null }
  ]
}
FieldTypeDescription
successBooleantrue if at least one page rendered successfully
pages[].pageInteger1-based page number
pages[].page_idStringStable id of the page
pages[].thumbnail_urlString|nullPublic URL of the uploaded thumbnail PNG. null if this specific page failed
pages[].errorString|nullError message if this page failed. null on success

Asynchronous thumbnails#

When ?includeThumbnails=true is not set (the default), the endpoint responds immediately and generates thumbnails in the background. They appear on each page's thumbnail_url in Get Studio Template shortly after creation, usually within a few seconds per page.

Pass ?includeThumbnails=true only when your workflow needs the thumbnail URLs in the same response (e.g., you're about to render or display the template immediately). Sync mode adds ~1–2 seconds per page to the response time.

Notes#

  • If pages_data is not provided, a blank page with the specified dimensions is automatically created
  • Elements with parameterizable: true and a parameterId will be automatically extracted as modifications
  • The modifications array in the response shows which dynamic parameters are available for rendering
  • When embed_user_id is provided, the template is assigned to that embed user. Your own external user ID is resolved to an internal Orshot ID using your workspace's embed. If that user does not exist yet, it is created, which needs Per-User Data turned on for the embed and counts toward your plan's embed user limit. You can also pass an Orshot internal ID directly (format: eui_ followed by 16 alphanumeric characters). An internal ID must already exist.
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 40+ integrations
  • White-label embed for your own app
  • 100 free credits a month, no credit card required