← OrshotCONTENT HISTORY

Update to Orshot

Snapshot Nov 3, 2026 · 00:11 UTC · version 3.4.0

Collection source: downloaded plugin package.

WHAT CHANGED

Payment or plan references changed

Full description — Now says: Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a...

Observed in published text. Live prices and checkout terms have not been verified by this change.

Save Orshot and check future changes in Following.

Up to 3 plugins free · no card required.

All fields and original values.

Full description

Before

Oct 24, 2026 · 12:51 UTC

Generate images, PDFs, and videos from templates with Orshot — via the REST API, SDKs (Node/Python/PHP/Ruby), the remote MCP server, or no-code tools (Zapier, Make, n8n, Airtable), and wire those renders into a recurring automation. Use ...

After

Nov 3, 2026 · 00:11 UTC

Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch, ...

Skill instructions

Before

Oct 24, 2026 · 12:51 UTC

Generate images, PDFs, and videos from templates with Orshot — via the REST API, SDKs (Node/Python/PHP/Ruby), the remote MCP server, or no-code tools (Zapier, Make, n8n, Airtable), and wire those renders into a recurring automation. Use ...

After

Nov 3, 2026 · 00:11 UTC

"Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch,...

Supporting files

Before

Oct 24, 2026 · 12:51 UTC

[]

After

Nov 3, 2026 · 00:11 UTC

[{"relative_path":"references/recurring-automation.md","size_in_bytes":5563}]

Compare saved observations

Download comparison JSON

SKILL.md text changes

− removed · + added

--- before
+++ after
@@ -1,1486 +1,174 @@
 ---
 name: orshot
-description: Generate images, PDFs, and videos from templates with Orshot — via the REST API, SDKs (Node/Python/PHP/Ruby), the remote MCP server, or no-code tools (Zapier, Make, n8n, Airtable), and wire those renders into a recurring automation. Use whenever the user mentions Orshot, connects Orshot to an AI agent or MCP client, wants a render to run unattended on a schedule or trigger, or needs to programmatically produce marketing visuals, OG images, social carousels, certificates/invoices/tickets as PDF, or video from templates — including when migrating from another template-based image-generation API. Always load this skill for Orshot tasks, even simple ones, because it carries gotchas (modifications key by parameterId, multi-page needs a page1@ prefix, long video renders need async render jobs) that otherwise cause failed renders. Not for generating standalone AI images with no template or Orshot involved.
+description: "Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch, async), dynamic image URLs, utility templates (tweet and website screenshots, charts, mockups), posting a render to connected social accounts, wiring it into n8n, Make, Zapier or cron, and the gotchas that break renders (parameterId keys, page1@ prefixes, public image URLs). Use when: \"render my Orshot template\", \"connect Orshot to Claude\", \"call the Orshot API\", \"my modification is ignored\", \"automate this render in n8n\", \"screenshot this tweet\". One job end to end: load its skill (orshot-blog-images, orshot-pdf-generator, orshot-video-generator, orshot-ad-creative, orshot-carousel, orshot-youtube-thumbnail, orshot-social-media-content or orshot-social-media-scheduler). NOT for template design craft (use orshot-design)."
+license: MIT
 metadata:
-  author: Rishi Mohan
-  version: "1.3.0"
+  author: Orshot
+  version: "1.6.3"
 ---
 
-# Orshot – Automated Visual Content Generation
+# Orshot: generate images, PDFs and videos from templates through the API
 
-[Orshot](https://orshot.com) is an automated image, PDF, and video generation platform. Design templates in Orshot Studio (or import from Canva/Figma), then generate renders via REST API, SDKs, or no-code integrations.
+Orshot is an API for automated image, PDF and video generation from templates. A template is designed once (in the Orshot studio, imported, or built by an agent), then rendered with new data on every call. The output can be posted to connected social accounts in the same request.
 
-- **Documentation:** https://orshot.com/docs
-- **API Base URL:** https://api.orshot.com/v1
+This skill is the flow: which tool or endpoint to call, in what order, and what to check. Parameters, allowed values, limits and response fields live in the docs. Every docs page is readable as markdown at `https://orshot.com/docs/<path>.md`; the docs map at the end lists the pages for each job.
 
-## When to Use This Skill
+- MCP server: `https://mcp.orshot.com/mcp`
+- REST base: `https://api.orshot.com/v1`
 
-Use this skill whenever the user mentions **Orshot**, or when the task is:
+Three things hold in every flow below:
 
-- Generating images, PDFs, or videos programmatically from templates
-- Building automated marketing visual pipelines (OG images, ad creatives, thumbnails)
-- Creating dynamic social media content (carousels, posts, stories) and publishing it
-- Generating certificates, invoices, tickets, or reports as PDFs
-- Building image/PDF/video generation into a product
-- Automating visuals with Zapier, Make, n8n, Airtable, or the CLI
-- Connecting Orshot to an AI agent or MCP client (Claude, Cursor, Codex, Windsurf, ChatGPT)
-- Embedding a white-label design editor into an app
-- Migrating from another template-based image/PDF/video generation API
+- No API key over the hosted MCP server. It signs in with OAuth. A key is only for cron jobs, servers and n8n.
+- The credit cost comes first. Say it before a bulk render.
+- Nothing is posted, run or deleted without the user's yes.
 
-**Don't use this skill** for generating standalone AI images with no template or Orshot involved (a one-off "make me an image" request) — that isn't what Orshot does.
+## One job end to end? Load its skill
 
-## Accessing Detailed Documentation
+This skill is Orshot itself: connecting, rendering and the gotchas. A whole job has its own skill with the flow and worked examples:
 
-Any page on orshot.com can be fetched as clean markdown by appending `.md` to the URL or sending `Accept: text/markdown` header. Use this to get detailed, up-to-date information on demand without relying solely on this skill file.
+| The job | Skill |
+| --- | --- |
+| Featured images and OG images for blog posts, in-article images | `orshot-blog-images` |
+| Invoices, certificates, reports and other PDFs from rows of data | `orshot-pdf-generator` |
+| Reels, Shorts, product and listing videos, animated charts and brand films | `orshot-video-generator` |
+| Ads in every Meta, Google and LinkedIn size, A/B variants | `orshot-ad-creative` |
+| TikTok, Instagram and LinkedIn carousels | `orshot-carousel` |
+| YouTube thumbnails | `orshot-youtube-thumbnail` |
+| On-brand social posts and stories | `orshot-social-media-content` |
+| Scheduling, posting and analytics across platforms | `orshot-social-media-scheduler` |
+| Designing or refining a template | `orshot-design` |
 
-**Examples:**
-```
-https://orshot.com/docs/api-reference.md
-https://orshot.com/docs/sdks/node.md
-https://orshot.com/docs/publish/publish-from-api.md
-https://orshot.com/docs/developers/oauth-overview.md
-https://orshot.com/docs/orshot-embed/introduction.md
-```
-
-**Key documentation pages:**
-| Topic | URL |
-|-------|-----|
-| API Reference | `https://orshot.com/docs/api-reference.md` |
-| Node.js SDK | `https://orshot.com/docs/sdks/node.md` |
-| Python SDK | `https://orshot.com/docs/sdks/python.md` |
-| PHP SDK | `https://orshot.com/docs/sdks/php.md` |
-| Ruby SDK | `https://orshot.com/docs/sdks/ruby.md` |
-| Studio Templates | `https://orshot.com/docs/orshot-studio/introduction.md` |
-| Style Parameters | `https://orshot.com/docs/orshot-studio/style-parameters.md` |
-| Setting Parameters | `https://orshot.com/docs/orshot-studio/setting-parameters.md` |
-| Image Generation | `https://orshot.com/docs/image-generation.md` |
-| Video Generation | `https://orshot.com/docs/video-generation.md` |
-| PDF Generation | `https://orshot.com/docs/pdf-generation.md` |
-| Social Publishing | `https://orshot.com/docs/publish/introduction.md` |
-| OAuth / Developer Apps | `https://orshot.com/docs/developers.md` |
-| White-Label Embed | `https://orshot.com/docs/orshot-embed/introduction.md` |
-| Integrations | `https://orshot.com/docs/integrations.md` |
-| Dynamic URLs | `https://orshot.com/docs/integrations/dynamic-urls.md` |
-| Webhooks | `https://orshot.com/docs/integrations/webhooks.md` |
-| Error Reference | `https://orshot.com/docs/error-reference.md` |
-
-When a user asks about a specific topic, fetch the relevant `.md` URL for the latest details.
-
-## Getting Started
-
-### Authentication
-
-All API requests require a Bearer token in the `Authorization` header:
-
-```
-Authorization: Bearer <ORSHOT_API_KEY>
-```
-
-[Get your API key](https://orshot.com/docs/quick-start/get-api-key) from **Workspace Settings → API Keys** in the Orshot dashboard.
+## Connect first
 
-### Remote MCP Server
+The hosted MCP server is the fastest route and the only one that returns inline previews and design checks. Added from an app directory (Claude, ChatGPT), the server is already connected: go straight to the account check below.
 
-Orshot runs a hosted MCP server so agents (Claude, Cursor, Codex, Windsurf, ChatGPT, etc.) can use Orshot as tools — no local install.
 
-- **URL:** `https://mcp.orshot.com/mcp` (transport: `streamable-http`)
-- **Auth:** OAuth 2.0 (the client walks you through it), or send an Orshot API key as a Bearer token
-- **Claude Code:** `claude mcp add --transport http orshot https://mcp.orshot.com/mcp`
-- **Cursor / Windsurf / VS Code / ChatGPT / Codex:** add the URL as a remote/HTTP MCP server
+Confirm with `orshot_get_account`. It returns the plan, credits and which features the plan includes (video, workflows, embed). If it reports an inactive subscription or a used-up limit, that is a billing state, not an auth error. Send the user to https://orshot.com/pricing and stop retrying.
 
-It exposes tools for rendering, studio + library templates, brand assets, workflows, social accounts, and workspace/logs. Full discovery doc: `https://orshot.com/.well-known/mcp.json`. Setup guide: `https://orshot.com/docs/integrations/mcp-server.md`.
-
-### SDKs
-
-#### Node.js
+**Headless fallback: REST with an API key.** Use it only when the agent cannot run MCP (a cron job, a server script, n8n). Read the key from the environment.
 
 ```bash
-npm install orshot
-```
-
-```js
-import { Orshot } from "orshot";
-const orshot = new Orshot("<ORSHOT_API_KEY>");
-
-// Render from template
-const response = await orshot.renderFromTemplate({
-  templateId: "open-graph-image-1",
-  modifications: { title: "Hello World" },
-  responseType: "base64", // "base64" | "url" | "binary"
-  responseFormat: "png", // "png" | "webp" | "jpg" | "pdf"
-});
-
-// Generate signed URL
-const signedUrl = await orshot.generateSignedUrl({
-  templateId: "open-graph-image-1",
-  modifications: { title: "Hello" },
-  expiresAt: 1744276943,
-  renderType: "images",
-  responseFormat: "png",
-});
-```
-
-#### Python
-
-```bash
-pip install orshot
-```
-
-```python
-import orshot
-os = orshot.Orshot('<ORSHOT_API_KEY>')
-
-response = os.render_from_template({
-  'template_id': 'open-graph-image-1',
-  'modifications': {'title': 'Hello World'},
-  'response_type': 'base64',
-  'response_format': 'png'
-})
-```
-
-#### Other SDKs
-
-- **PHP:** `composer require rishimohan/orshot`
-- **Ruby:** `gem install orshot`
-
-## Common Gotchas (read before rendering)
-
-The mistakes that most often make Orshot calls fail or return the wrong output:
-
-1. **Modifications key by `parameterId`, not layer name.** `modifications: { "headline": "..." }` targets the element whose `parameterId` is `headline`. If a value is ignored, the key is wrong — fetch the template's modifications (`GET /v1/studio/templates/:id`) to see the real keys instead of guessing.
-2. **Multi-page templates need a `pageN@` prefix.** Use `"page1@title"`, `"page2@title"`. A bare `"title"` only affects page 1.
-3. **Studio `templateId` is an integer; utility `templateId` is a string.** `/v1/studio/render` takes an integer ID; `/v1/generate/:renderType` takes a string slug like `"website-screenshot"`.
-4. **Video output does NOT require video elements.** `format: "mp4"`/`"webm"`/`"gif"` renders any template — motion can come from video elements, page animations (enter/exit/ambient transitions), page-to-page transitions, or generated narration; a fully static page renders as a still clip of its `videoDuration`. Long video renders should use async render jobs (`response.mode: "async"`) so they don't hit client timeouts.
-5. **Image/video URLs in modifications must be publicly reachable.** The renderer fetches them server-side, so `localhost`, expired signed URLs, or auth-gated URLs won't load.
-6. **Style overrides use dot notation on the parameterId** — `"title.fontSize": "48px"`, not a nested object.
-7. **Response shape differs by page count:** single page → `data` is an object (read `data.content`); multi-page/carousel → `data` is an array of `{ page, content }`. Handle both.
-8. **`base64` and `binary` response types don't combine** — `binary` returns one raw file stream.
-9. **Credits vs AI Credits are separate** — normal renders spend credits; `.prompt` (AI) modifications additionally spend AI Credits.
-10. **On `429`, back off** using the `Retry-After` response header.
-
-When unsure about a template's parameters, always fetch its modifications first.
-
-## Template Architecture
-
-This section describes the complete structure of an Orshot template for MCP tools and AI agents.
-
-### Template Structure
-
-An Orshot template consists of **pages**, each containing a **canvas** and **elements**.
-
-```
-Template
-├── id: number | string
-├── name: string
-├── description: string
-├── width: number
-├── height: number
-├── pages_data: Array
-    └── Page
-        ├── id: string (UUID)
-        ├── name: string
-        ├── canvas: CanvasConfig
-            ├── width: number
-            ├── height: number
-            ├── backgroundColor: string
-            ├── backgroundImage: string
-        ├── elements: Element[]
-        ├── modifications: Modification[] (API parameters)
-            ├── id: string
-            ├── type: string
-            ├── element: Element
-            ├── description: string
-        └── thumbnail_url: string | null
-```
-
-### Canvas Configuration
-
-| Property          | Type   | Default         | Description                               |
-| ----------------- | ------ | --------------- | ----------------------------------------- |
-| `width`           | number | 800             | Canvas width in pixels (max: 5000)        |
-| `height`          | number | 800             | Canvas height in pixels (max: 5000)       |
-| `backgroundColor` | string | "#ffffff"       | Background color (hex, rgba, or gradient) |
-| `backgroundImage` | string | ""              | URL to background image                   |
-| `borderWidth`     | number | 0               | Border width in pixels                    |
-| `borderColor`     | string | "rgba(0,0,0,1)" | Border color                              |
-| `borderStyle`     | string | "solid"         | Border style (solid, dashed, etc)         |
-
-### Canvas Size Presets
-
-| Name                 | Dimensions | Use Case                        |
-| -------------------- | ---------- | ------------------------------- |
-| Square               | 1080×1080  | Instagram posts, general social |
-| Instagram Story      | 1080×1920  | Stories, Reels, TikTok          |
-| Slide/Presentation   | 1920×1080  | Presentations, slides           |
-| YouTube Thumbnail    | 1280×720   | Video thumbnails                |
-| Twitter Post         | 1600×900   | X/Twitter posts                 |
-| Open Graph           | 1200×630   | Link previews, Facebook         |
-| Pinterest Pin        | 1000×1500  | Pinterest                       |
-| A4 Document          | 2480×3508  | Print documents                 |
-| App Store Screenshot | 1290×2796  | iOS app screenshots             |
-
-### Universal Element Properties
-
-All elements share these base properties:
-
-| Property            | Type    | Description                                  |
-| ------------------- | ------- | -------------------------------------------- |
-| `id`                | string  | Unique identifier (UUID)                     |
-| `name`              | string  | Display name in layer list (was `layerName`) |
-| `type`              | string  | "text", "image", "shape", "video"            |
-| `position`          | object  | `{ x: number, y: number }` from top-left     |
-| `dimensions`        | object  | `{ width: number, height: number }`          |
-| `rotation`          | number  | Rotation in degrees (0-360)                  |
-| `zIndex`            | number  | Layer order (higher = on top)                |
-| `aspectRatioLocked` | boolean | Lock aspect ratio during resize              |
-| `isHidden`          | boolean | Hide element from render                     |
-| `skewX`             | number  | Horizontal skew angle                        |
-| `skewY`             | number  | Vertical skew angle                          |
-
-### Text Element
-
-**Content Types:**
-
-- **Plain text**: `"Hello World"` - Standard text string
-- **Multi-line**: Use `\n` for line breaks: `"Line 1\nLine 2"`
-- **Dynamic via API**: Use `.prompt` modifier for AI-generated text
-
-```javascript
-{
-  type: "text",
-  content: string,          // Plain text string
-  layerName: string,        // Display name
-  zIndex: number,
-  rotation: number,
-  position: { x: number, y: number },
-  dimensions: { width: number, height: number },
-  style: {
-    // Typography
-    fontFamily: string,     // e.g., "Inter", "Prata", "SF Pro"
-    fontSize: string,       // e.g., "48px"
-    fontWeight: string | number, // "400", "700", 700
-    fontStyle: string,      // "normal", "italic"
-    lineHeight: number,     // e.g., 1.2
-    letterSpacing: string,  // e.g., "0px", "2px"
-
-    // Appearance
-    fill: string,           // Color or gradient
-    color: string,          // Hex or rgba
-    opacity: number,        // 0-1
-    stroke: string,         // Stroke color
-    strokeWidth: string,    // e.g., "0px"
-
-    // Alignment & Layout
-    textAlign: string,      // "left", "center", "right"
-    verticalAlign: string,  // "flex-start", "center", "flex-end"
-    textTransform: string,  // "none", "uppercase"
-    textDecoration: string, // "none", "underline"
-    textMode: string,       // "overflow", "fit"
-    paddingX: string,
-    paddingY: string,
-
-    // Borders & Backgrounds
-    borderColor: string,
-    borderWidth: string,
-    borderRadius: string,
-    textBackgroundColor: string,
-    textBackgroundRadius: string,
-    textStrokeColor: string,
-    textStrokeWidth: string,
-
-    // Effects
-    minFontSize: string,    // For "fit" mode
-    filter: string,         // e.g., "blur(0px)"
-    mixBlendMode: string,   // "normal", "multiply", etc.
-    boxShadowX: string,
-    boxShadowY: string,
-    boxShadowBlur: string,
-    boxShadowColor: string,
-    dropShadowX: string,
-    dropShadowY: string,
-    dropShadowBlur: string,
-    dropShadowColor: string
-  },
-  // Parameterization
-  parameterizable: boolean,
-  parameterId: string,
-  parameterType: "text"
-}
-```
-
-**Gradient text:**
-
-```javascript
-color: "linear-gradient(90deg, #FF6B6B 0%, #4ECDC4 100%)";
-```
-
-### Image Element
-
-**Content Types:**
-
-- **URL** (recommended): `"https://example.com/image.png"` - Best for dynamic content
-- **Base64**: `"data:image/png;base64,iVBORw0KGgo..."` - For embedded images
-- **Binary**: Raw binary data (API upload only)
-
-```javascript
-{
-  type: "image",
-  content: string,          // URL (preferred), base64, or binary
-  isSvg: boolean,
-  layerName: string,
-  style: {
-    // Sizing & Positioning
-    objectFit: string,      // "contain", "cover", "fill"
-    objectPosition: string, // "center", "top left"
-
-    // Appearance
-    opacity: number,
-    fill: string,           // Background fill
-    stroke: string,         // Border stroke
-
-    // Borders
-    borderRadius: string,   // "0px", "12px", "50%"
-    borderWidth: string,
-    borderColor: string,
-
-    // Effects
-    filter: string,         // "blur(2px)", "grayscale(100%)"
-    mixBlendMode: string,
-    boxShadowX: string,
-    boxShadowY: string,
-    boxShadowBlur: string,
-    boxShadowColor: string,
-    dropShadowX: string,
-    dropShadowY: string,
-    dropShadowBlur: string,
-    dropShadowColor: string,
-
-    svgColor: string        // Recolor monochrome SVGs
-  },
-  parameterType: "imageUrl"
-}
-```
-
-### Shape Element
-
-```javascript
-{
-  type: "shape",
-  shapeType: string,        // "rectangle", "circle", "arrow"
-  layerName: string,
-  style: {
-    // Fill & Stroke
-    fill: string,           // Color or gradient
-    stroke: string,
-    strokeWidth: string,    // e.g. "0px"
-
-    // Dimensions
-    borderRadius: string,   // Rectangle only
-    borderWidth: string,
-    borderColor: string,
-    borderStyle: string,
-
-    // Appearance
-    opacity: number,
-    filter: string,
-    mixBlendMode: string,
-
-    // Shadows
-    boxShadowX: string,
-    boxShadowY: string,
-    boxShadowBlur: string,
-    boxShadowColor: string,
-    dropShadowX: string,
-    dropShadowY: string,
-    dropShadowBlur: string,
-    dropShadowColor: string
-  },
-  parameterType: "fill"
-}
-```
-
-**Gradient fills:**
-
-```javascript
-fill: "linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%)";
-fill: "radial-gradient(circle at center, #FF6B6B 0%, #4ECDC4 100%)";
-```
-
-### Video Element
-
-**Content Types:**
-
-- **URL** (required): `"https://example.com/video.mp4"` - Must be a publicly accessible URL
-- Supported formats: MP4, WebM, MOV
-- For best results, use MP4 with H.264 codec
-
-```javascript
-{
-  type: "video",
-  content: string,          // Video URL (must be publicly accessible)
-  videoOptions: {
-    loop: boolean,
-    muted: boolean,
-    trim_start_time: string,
-    trim_end_time: string,
-    duration: number | null
-  },
-  style: {
-    // Sizing & Positioning
-    objectFit: string,      // "contain", "cover", "fill"
-    objectPosition: string,
-
-    // Appearance
-    opacity: number,
-    filter: string,
-    mixBlendMode: string,
-
-    // Borders & Shadows
-    borderRadius: string,
-    borderWidth: string,
-    borderColor: string,
-    elementBoxShadowX: string,
-    elementBoxShadowY: string,
-    elementBoxShadowBlur: string,
-    elementBoxShadowColor: string
-  },
-  parameterType: "videoUrl"
-}
-```
-
-### Parameterization Best Practices
-
-When creating or updating templates, **always ensure all text, image, and video elements are parameterizable** with unique IDs. This enables dynamic content replacement via the API.
-
-#### Required Setup
-
-Every dynamic element MUST have:
-
-```javascript
-{
-  parameterizable: true,
-  parameterId: "unique_id",  // Unique across template, lowercase with underscores
-  parameterType: "text" | "imageUrl" | "videoUrl"
-}
-```
-
-#### Naming Conventions
-
-| Element Type | parameterId Examples                        | parameterType |
-| ------------ | ------------------------------------------- | ------------- |
-| Text         | `headline`, `subtitle`, `cta_text`, `price` | `"text"`      |
-| Image        | `product_image`, `logo`, `background_image` | `"imageUrl"`  |
-| Video        | `hero_video`, `background_video`            | `"videoUrl"`  |
-
-#### Best Practices
-
-1. **Use descriptive IDs:** `product_title` not `text1`
-2. **Be consistent:** Use snake_case across all templates
-3. **Unique per template:** No duplicate parameterIds on same page
-4. **Group logically:** Related elements share naming prefix (e.g., `card_title`, `card_image`)
-
-#### Validation Checklist
-
-Before finalizing any template update:
-- [ ] All text elements have `parameterizable: true` and unique `parameterId`
-- [ ] All image elements have `parameterizable: true` and unique `parameterId`
-- [ ] All video elements have `parameterizable: true` and unique `parameterId`
-- [ ] No duplicate parameterIds exist on the same page
-- [ ] parameterIds are descriptive and follow snake_case convention
-
-### Design Best Practices
-
-#### Typography Guidelines
-- **Font limit:** Use 2-3 fonts maximum per template
-- **Hierarchy:** Headings should be 1.5-2x larger than body text
-- **Minimum size:** 24px for social media readability
-- **Weights:** Headings 600-900 (bold), Body 400-500 (regular)
-
-**Popular font pairings:**
-- `Prata` + `Inter`
-- `Instrument Serif` + `DM Sans`
-- `Playfair Display` + `Lato`
-- `Montserrat` + `Open Sans`
-
-**Platform-specific fonts:**
-- iOS: `SF Pro Display`, `SF Pro Text`
-- Android: `Google Sans`, `Roboto`
-
-#### Color Guidelines
-**Luxury/Gold palette:**
-- Gold: `#D4AF37`
-- Dark gold: `#B8860B`
-- Light gold: `#F5E7A3`
-
-**iOS system colors:**
-- Blue: `#007AFF`
-- Gray: `#8E8E93`
-- Background: `#F5F5F7`
-
-**Professional dark:**
-- Navy: `#0F172A`
-- Slate: `#1E293B`, `#334155`
-- Muted: `#64748B`, `#94A3B8`
-
-**Best practices:**
-- Ensure 4.5:1 minimum contrast for text readability
-- Use gradients sparingly for premium effects
-- Consistent color palette (3-5 colors max)
-
-#### Layout Guidelines
-- **Edge padding:** 40-60px from canvas edges
-- **Element spacing:** 20-40px between elements
-- **Alignment:** Center for formal, left for modern
-- **Visual flow:** Guide eye with size, color, position
-
-**zIndex ordering:**
-- Background images/colors: 1
-- Overlay shapes: 2-3
-- Text elements: 4-6
-- Interactive elements: 7+
-
-#### Common Operations (Design Automation)
-
-**Add text element:**
-```javascript
-addElement("text", {
-  content: "Hello World",
-  fontFamily: "Inter",
-  fontSize: 48,
-  fontWeight: "700",
-  color: "#FFFFFF",
-  x: 100,
-  y: 100,
-  width: 400,
-  height: 60,
-});
-```
-
-**Add shape backdrop:**
-```javascript
-addElement("rectangle", {
-  fill: "rgba(0,0,0,0.5)",
-  width: 1080,
-  height: 200,
-  x: 0,
-  y: 800,
-  borderRadius: "0px",
-});
+curl -s https://api.orshot.com/v1/me -H "Authorization: Bearer $ORSHOT_API_KEY"
 ```
 
-**Batch update multiple elements:**
-```javascript
-batchUpdate([
-  {
-    elementId: "heading",
-    type: "ORSHOT_UPDATE_ELEMENT",
-    updates: { style: { fontSize: 72 } },
-  },
-  {
-    elementId: "subtitle",
-    type: "ORSHOT_UPDATE_ELEMENT",
-    updates: { style: { color: "#94A3B8" } },
-  },
-  { type: "ORSHOT_UPDATE_CANVAS", updates: { backgroundColor: "#0F172A" } },
-]);
-```
-
-## API Reference
-
-### 1. Render from Studio Template
-
-Generate images/PDFs/videos from templates designed in Orshot Studio.
-
-**POST** `https://api.orshot.com/v1/studio/render`
-
-```js
-await fetch("https://api.orshot.com/v1/studio/render", {
-  method: "POST",
-  headers: {
-    "Content-Type": "application/json",
-    Authorization: "Bearer <ORSHOT_API_KEY>",
-  },
-  body: JSON.stringify({
-    templateId: 123, // Integer - your studio template ID
-    modifications: {
-      title: "Hello World",
-      imageUrl: "https://example.com/photo.jpg",
-      canvasBackgroundColor: "#eff2fa",
-    },
-    response: {
-      type: "base64", // "base64" | "url" | "binary"
-      format: "png", // "png" | "webp" | "jpg" | "pdf" | "mp4" | "webm" | "gif"
-      scale: 1, // 1 = original size, 2 = double
-      includePages: [1, 3], // optional – only for multi-page templates
-      fileName: "my-render", // optional – custom filename (without extension)
-    },
-    pdfOptions: {
-      // optional – only when format is "pdf"
-      margin: "20px",
-      rangeFrom: 1,
-      rangeTo: 2,
-      colorMode: "rgb", // "rgb" or "cmyk"
-      dpi: 300,
-    },
-  }),
-});
-```
-
-**Response (single page):**
-```json
-{
-  "data": {
-    "content": "data:image/png;base64,iVBORw0.....",
-    "format": "png",
-    "type": "base64",
-    "responseTime": 325.22
-  }
-}
-```
-
-**Response (multi-page/carousel):**
-```json
-{
-  "data": [
-    { "page": 1, "content": "https://storage.orshot.com/.../image1.png" },
-    { "page": 2, "content": "https://storage.orshot.com/.../image2.png" }
-  ],
-  "format": "png",
-  "type": "url",
-  "responseTime": 3166.01,
-  "totalPages": 2,
-  "renderedPages": 2
-}
-```
-
-### 2. Render from Utility Template
-
-Generate renders from Orshot's pre-built utility templates (e.g., website screenshots, tweet images).
-
-**POST** `https://api.orshot.com/v1/generate/{renderType}`
-
-- `renderType`: `images` or `pdfs`
-
-```js
-await fetch("https://api.orshot.com/v1/generate/images", {
-  method: "POST",
-  headers: {
-    "Content-Type": "application/json",
-    Authorization: "Bearer <ORSHOT_API_KEY>",
-  },
-  body: JSON.stringify({
-    templateId: "website-screenshot", // String ID for utility templates
-    response: {
-      format: "png",
-      type: "base64",
-    },
-    modifications: {
-      websiteUrl: "https://example.com",
-      fullCapture: false,
-      delay: 500,
-      width: 1200,
-      height: 1000,
-    },
-  }),
-});
-```
-
-### 3. Generate Signed URL
-
-Create publicly accessible render URLs without exposing your API key.
-
-**POST** `https://api.orshot.com/v1/signed-url/create`
-
-```js
-await fetch("https://api.orshot.com/v1/signed-url/create", {
-  method: "POST",
-  headers: {
-    Authorization: "Bearer <ORSHOT_API_KEY>",
-    "Content-Type": "application/json",
-  },
-  body: JSON.stringify({
-    templateId: "website-screenshot",
-    expiresAt: 1744550160505, // UNIX timestamp, or null for no expiry
-    renderType: "images",
-    modifications: {
-      websiteUrl: "https://example.com",
-    },
-  }),
-});
-```
-
-### 4. List Studio Templates
-
-**GET** `https://api.orshot.com/v1/studio/templates/all?page=1&limit=10`
-
-Response includes `data` array of templates and `pagination` object with `page`, `limit`, `total`, `totalPages`.
-
-### 5. Get Studio Template
-
-**GET** `https://api.orshot.com/v1/studio/templates/:templateId`
-
-Returns the template metadata including available `modifications`.
-
-### 6. Delete Studio Template
-
-**DELETE** `https://api.orshot.com/v1/studio/templates/:templateId`
-
-### 7. Duplicate Studio Template
-
-**POST** `https://api.orshot.com/v1/studio/templates/:templateId/duplicate`
-
-### 8. Get Profile & Workspaces
-
-**GET** `https://api.orshot.com/v1/me`
-
-Returns your profile and the workspaces your API key or OAuth token can access, including plan info and credit usage.
-
-### 9. Brand Assets API
-
-Brand assets are grouped by type — **images**, **colors**, **fonts**, **videos**, **audio** — each with its own routes under `/v1/brand-assets/{type}/…`.
-
-#### Images
-- **Get:** `GET https://api.orshot.com/v1/brand-assets/images/get`
-- **Upload:** `POST https://api.orshot.com/v1/brand-assets/images/add` — `multipart/form-data` with a `file` field
-- **Update tags:** `PATCH https://api.orshot.com/v1/brand-assets/images/update/:id`
-- **Delete:** `DELETE https://api.orshot.com/v1/brand-assets/images/delete/:id`
-
-#### Colors
-- **Get:** `GET https://api.orshot.com/v1/brand-assets/colors/get`
-- **Add:** `POST https://api.orshot.com/v1/brand-assets/colors/add` — body `{ type: "hex" | "gradient", value, tags? }`
-- **Update tags:** `PATCH https://api.orshot.com/v1/brand-assets/colors/update/:id`
-- **Delete:** `DELETE https://api.orshot.com/v1/brand-assets/colors/delete/:id`
-
-#### Fonts / Videos / Audio
-Same shape — swap the type segment (`fonts`, `videos`, `audio`):
-- **Get:** `GET /v1/brand-assets/{type}/get`
-- **Upload:** `POST /v1/brand-assets/{type}/add` (`multipart/form-data`, `file` field)
-- **Update tags:** `PATCH /v1/brand-assets/{type}/update/:id`
-- **Delete:** `DELETE /v1/brand-assets/{type}/delete/:id`
-
-#### Search
-- **Search assets:** `GET https://api.orshot.com/v1/brand-assets/search`
-
-Use an uploaded asset in a render by passing its hosted URL as an image/video modification value.
-
-### 10. Enterprise API Endpoints
-
-These endpoints require an Enterprise plan.
-
-#### Create Studio Template
-**POST** `https://api.orshot.com/v1/studio/templates/create`
-
-```js
-{
-  name: "Product Banner",        // required, max 255 chars
-  description: "Banner template", // optional
-  canvas_width: 1200,             // required, 1-5000
-  canvas_height: 628,             // required, 1-5000
-  pages_data: [...]               // optional - array of page objects with elements
-}
-```
-
-#### Bulk Create Studio Templates
-**POST** `https://api.orshot.com/v1/studio/templates/bulk-create`
-Create multiple templates at once via CSV or JSON.
-
-#### Update Template
-**PATCH** `https://api.orshot.com/v1/studio/templates/:templateId`
-Update template name and description.
-
-#### Update Template Modifications
-**PATCH** `https://api.orshot.com/v1/studio/templates/:templateId/update-modifications`
-Update text and image content in template layers.
-
-#### Generate Template Variants
-**POST** `https://api.orshot.com/v1/studio/templates/:templateId/generate-variants`
-Generate multiple size variants of a template using AI.
-
-### 11. Dynamic URLs
-
-Generate images directly from URL parameters:
-
-```
-https://api.orshot.com/v1/studio/dynamic-url/my-image?title=Hello%20World&title.fontSize=48px&title.color=%23ff0000
-```
-URL-encode special characters (e.g., `#` → `%23`).
-
-### 12. Template Folders & Sharing
-
-Organize studio templates into folders and share them:
+Do not go on until one of the two works.
 
-- **List folders:** `GET https://api.orshot.com/v1/studio/folders`
-- **Create folder:** `POST https://api.orshot.com/v1/studio/folders`
-- **Update folder:** `PATCH https://api.orshot.com/v1/studio/folders/:folderId`
-- **Delete folder:** `DELETE https://api.orshot.com/v1/studio/folders/:folderId`
-- **Move template into folder:** `PATCH https://api.orshot.com/v1/studio/templates/:templateId/folder`
-- **Get template sharing:** `GET https://api.orshot.com/v1/studio/templates/:templateId/share`
-- **Update template sharing:** `POST https://api.orshot.com/v1/studio/templates/:templateId/share`
+## Core workflow
 
-### 13. Workflows API
+1. **Connect** (above).
+2. **Find the template.** `orshot_list_studio_templates` (search by name or tags) or `GET /v1/studio/templates/all`. No template yet? Browse `orshot_list_library_templates`, or design one with the `orshot-design` skill.
+3. **Read its keys.** `orshot_get_studio_template_modifications` or `GET /v1/studio/templates/:id`. Never guess a key.
+4. **Render.** `orshot_render_template` or `POST /v1/studio/render`.
+5. **Deliver or post.** Show the output URL. To post it, pass the URL to `orshot_publish_social_post` (over REST, `publish` on the render also works; see Social posting).
+6. **Automate.** Put the call into what the user already runs. Playbook: `references/recurring-automation.md`.
+7. **Verify.** `orshot_list_workspace_logs` or `GET /v1/workspace/logs` shows the render and the `source` that made it. Show the output before calling it done.
 
-Workflows automate multi-step pipelines (trigger → fetch data → render → publish/deliver). Available to first-party clients (like the Orshot MCP server) and Enterprise workspaces.
+## Safety rules
 
-- **List workflows:** `GET https://api.orshot.com/v1/workflows`
-- **Create workflow:** `POST https://api.orshot.com/v1/workflows`
-- **Get workflow:** `GET https://api.orshot.com/v1/workflows/:id`
-- **Update workflow:** `PATCH https://api.orshot.com/v1/workflows/:id`
-- **Delete workflow:** `DELETE https://api.orshot.com/v1/workflows/:id`
-- **Run workflow:** `POST https://api.orshot.com/v1/workflows/:id/run`
-- **List runs:** `GET https://api.orshot.com/v1/workflows/:id/runs`
-- **Get a run:** `GET https://api.orshot.com/v1/workflows/:id/runs/:runId`
-- **Validate a workflow:** `POST https://api.orshot.com/v1/workflows/validate`
-- **List available nodes:** `GET https://api.orshot.com/v1/workflows/nodes`
-- **Get/update sharing:** `GET` / `POST https://api.orshot.com/v1/workflows/:id/share`
+- **Confirm before anything leaves the workspace.** Publishing a post, running a workflow (it spends credits and can deliver or post) and deleting a template, workflow or brand asset need an explicit yes. Say what will go where, and when.
+- **Default social posts to drafts.** "Prepare", "draft" and "let me look first" mean a draft. A draft reaches nobody until it is published.
+- **Show the cost before bulk work.** Renders spend credits and AI modifications spend AI credits ([credits-usage](https://orshot.com/docs/definitions/credits-usage.md)). Say so before a loop of hundreds of renders.
+- **Never paste the API key into chat, files or URLs.** Prefer MCP, which has no key.
+- **Do not delete anything to "clean up" on your own.**
 
-## Render Configuration
+## Gotchas (read before rendering)
 
-### Dynamic Parameters
+1. **Modifications key by `parameterId`, not layer name.** If a value is ignored, the key is wrong. Fetch the keys instead of guessing.
+2. **Multi-page templates need a `pageN@` prefix on every content key** (`"page1@title"`, `"page2@title"`). A bare content key reaches no page; the response carries a warning. A bare dotted style key (`"title.fontSize"`) applies to every page that has that layer. [multi-page-images](https://orshot.com/docs/image-generation/multi-page-images.md)
+3. **Studio template ids are numbers; library template ids are slugs** (`"website-screenshot"`). [template-id](https://orshot.com/docs/definitions/template-id.md)
+4. **MCP and REST shape the render call differently.** Over MCP, output options are flat arguments of `orshot_render_template` (`format`, `scale`, `includePages`, `size`, `extraSizes`, `background`, `videoOptions`). Over REST they sit inside the `response` object. Posting over MCP is its own tool, `orshot_publish_social_post`.
+5. **Long renders must not block.** Over MCP pass `background: true`, then collect with `orshot_get_render_job`. Over REST add `mode: "async"` to `response`, then poll or use a webhook ([async-render-start](https://orshot.com/docs/api-reference/async-render-start.md)). Any template can render as video; the plan must include video.
+6. **Image and video URLs in modifications must be public.** The renderer fetches them server side; localhost, expired signed URLs and login-gated URLs fail.
+7. **The response shape depends on the page count.** Read the Response section of [render-from-studio-template](https://orshot.com/docs/api-reference/render-from-studio-template.md) before writing code that reads it.
+8. **On `429`, back off** for the `Retry-After` seconds.
+9. **Publish by account id, never by platform name.** Two accounts on one platform have different ids. Media rules differ per platform: check [social-posting-guidelines](https://orshot.com/docs/publish/social-posting-guidelines.md) before posting. A PDF render posts only to LinkedIn.
 
-Override template styles, content, and behavior at render time using dot notation.
+## Worked example: one design, several sizes
 
-#### Style Parameters
+Prompt: "Render the launch template as an OG image and an Instagram story, title 'We just shipped'."
 
-Format: `parameterId.property`
-
-```json
-{
-  "modifications": {
-    "title": "Hello World",
-    "title.fontSize": "48px",
-    "title.color": "#ff0000",
-    "title.fontFamily": "Roboto",
-    "title.textAlign": "center",
-    "logo.borderRadius": "50%",
-    "logo.objectFit": "cover"
-  }
-}
-```
-
-**Text properties:** `fontSize`, `fontWeight`, `fontStyle`, `fontFamily`, `lineHeight`, `letterSpacing`, `textAlign`, `verticalAlign`, `textDecoration`, `textTransform`, `color`, `backgroundColor`, `backgroundRadius`, `textStrokeWidth`, `textStrokeColor`, `opacity`, `filter`, `dropShadowX/Y/Blur/Color`
-
-**Image properties:** `objectFit`, `objectPosition`, `borderRadius`, `borderWidth`, `borderColor`, `boxShadowX/Y/Blur/Color`, `opacity`, `filter`
-
-**Shape properties:** `fill`, `stroke`, `strokeWidth`, `borderRadius`, `opacity`
-
-**Position/Size (all elements):** `x`, `y`, `width`, `height`
-
-Property names are **case-insensitive**.
-
-#### Multi-Page Templates
-
-Prefix modifications with page number:
-```json
-{
-  "modifications": {
-    "page1@title": "Page 1 Title",
-    "page2@title": "Page 2 Title",
-    "page1@title.fontSize": "48px"
-  }
-}
-```
-
-#### AI Content Generation (.prompt)
-
-Generate text or images using AI:
-```json
-{
-  "modifications": {
-    "headline.prompt": "Write a catchy headline about coffee",
-    "background.prompt": "A serene mountain landscape at sunset"
-  }
-}
-```
-- `.prompt` on a text element generates copy; on an image element it generates imagery. The underlying AI models are managed by Orshot and may change over time — don't hardcode a specific model. AI modifications consume AI Credits.
-
-#### Interactive Links (.href)
-
-Add clickable links in PDF outputs:
-```json
-{
-  "modifications": {
-    "cta_button.href": "https://example.com/signup",
-    "logo.href": "https://company.com"
-  },
-  "response": { "format": "pdf" }
-}
-```
-
-#### Video Parameters
-
-Control video elements dynamically:
-```json
-{
-  "modifications": {
-    "bgVideo": "https://example.com/video.mp4",
-    "bgVideo.trimStart": 5,
-    "bgVideo.trimEnd": 15,
-    "bgVideo.muted": false,
-    "bgVideo.loop": true
-  },
-  "response": { "format": "mp4" }
-}
-```
-
-### Video Render Example
-
-Render templates with video elements as MP4, WebM, or GIF:
-
-```js
-await fetch("https://api.orshot.com/v1/studio/render", {
-  method: "POST",
-  headers: {
-    "Content-Type": "application/json",
-    Authorization: "Bearer <ORSHOT_API_KEY>",
-  },
-  body: JSON.stringify({
-    templateId: 123,
-    modifications: {
-      videoElement: "https://example.com/custom-video.mp4",
-      "videoElement.trimStart": 0,
-      "videoElement.trimEnd": 10,
-      "videoElement.muted": false,
-      "videoElement.loop": true,
-    },
-    videoOptions: {
-      trimStart: 0,
-      trimEnd: 20,
-      muted: true,
-      loop: true,
-    },
-    response: {
-      type: "url",
-      format: "mp4",
-    },
-  }),
-});
-```
-
-### Render-level `videoOptions` (top level, sibling of `modifications`)
-
-Controls the whole video render, not one element:
-
-```json
-{
-  "templateId": 123,
-  "modifications": { "page1@headline": "Launch day" },
-  "videoOptions": {
-    "duration": 12,
-    "fps": 30,
-    "muted": false,
-    "combinePages": true,
-    "pageTransition": "fade",
-    "pageTransitionDuration": 0.5,
-    "audioSource": "https://example.com/track.mp3",
-    "subtitleSource": "https://example.com/voiceover.mp3",
-    "captionPreset": "karaoke"
-  },
-  "response": { "type": "url", "format": "mp4" }
-}
-```
-
-- `duration`, `fps` (≤30), `quality`, `muted`, `trimStart`+`trimEnd` (must be paired).
-- `combinePages: true` merges a multi-page template into ONE video, with `pageTransition` (42 types — fades, slides, wipes, zooms, blurs and more) and `pageTransitionDuration` (0–2s, default 0.5) between scenes. A string `audioSource` then lays one continuous soundtrack across the combined video — the recipe for multi-scene videos.
-- `audioSource` swaps an existing URL audio track at render time while keeping its saved trim/fades/volume/offset/loop. Generated narration changes through `tts.parameterId` instead (see Audio below).
-- Captions: `subtitleSource` (a media URL to auto-transcribe, or a `.srt`/`.vtt` used as-is), `captionPreset` (`pill` | `minimal` | `karaoke` | `bold` | `outline` | `neon`), plus fine-grained `subtitleMode` (`phrase`|`word-by-word`), `subtitleWordReveal` (`appear`|`highlight`|`align`), `subtitleSplit` (`word`|`letter`), `subtitleMaxWordsPerLine` or `subtitleMaxCharsPerLine`, and `subtitle*` styling keys. Prefer authored `page.subtitle` with source `"auto-audio"` so captions word-sync automatically.
-
-### Async render jobs (long videos — use this from automations)
-
-A synchronous render holds the HTTP connection for the full render time; video routinely outlives client timeouts (Zapier ~30s, many MCP clients 120s). Add `response.mode: "async"`:
-
-```
-POST /v1/studio/render  { ..., "response": { "mode": "async", "format": "mp4" } }
-  → 202 with a job object in ~100ms
-GET  /v1/studio/render-jobs/:id          → poll the job (queued → processing → succeeded/failed/canceled)
-GET  /v1/studio/render-jobs?status=&limit=  → newest-first list
-POST /v1/studio/render-jobs/:id/cancel   → cancel while not terminal
-```
-
-The job's `result` is byte-identical to the sync response. Pass a `webhook_url` to get the settled job POSTed to you instead of polling. Jobs time out at 15 minutes.
-
-### Page animations & ambient motion (video templates)
-
-Elements animate via a page-level `transitions` object on the element — no video file needed:
-
-```json
-{
-  "transitions": {
-    "enter": { "type": "slideInUp", "duration": 0.5, "easing": "ease-out" },
-    "showAt": 0.4,
-    "ambient": { "type": "kenBurns", "customValues": { "zoom": 1.12, "panX": 6 } },
-    "exit": { "type": "fadeOut", "duration": 0.3 },
-    "hideAt": 3.6
-  }
-}
-```
-
-- Three phases: `enter` → `ambient` (plays the element's whole visible life) → `exit`. `showAt`/`hideAt` are siblings of `enter`/`exit`, never nested inside them (the #1 authoring bug).
-- Ambient types: `kenBurns` (runs once, stretched across visibility — give every full-bleed photo one), plus looping `pulse`, `float`, `drift`, `wiggle`, `shake`, `slowRotate`, `breathe`. `shake`/`slowRotate` need `easing: "linear"` or the loop seam shows.
-- All timing is tunable per render via dot-notation modifications: `"photo.showAt": 2.1`, `"photo.enterType": "scaleIn"`, `"photo.ambientType": "kenBurns"`, etc.
-
-### Audio: soundtracks & AI voiceover (video templates)
-
-Each page carries `audioTracks` (max 10, page-level, mixed into every mp4/webm render):
-
-- **Existing audio**: `{ "url": "https://...", "trimStart": 33.2, "trimEnd": 44.1, "fadeOut": 1, "volume": 1, "loop": false, "offset": 0 }` — trim to the song's best part; `videoOptions.audioSource` swaps the file at render keeping these settings.
-- **AI voiceover**: `{ "tts": { "text": "words to speak", "voiceId": "...", "modelId": "flash", "parameterId": "voiceover" } }` instead of `url`. Synthesized when the template is saved (metered voice minutes; identical scripts are cached and free on re-render). Narration length drives the page length, and `page.subtitle` with source `"auto-audio"` gives word-synced captions with no transcription step.
-- **Generated ambient/SFX**: `{ "sfx": { "prompt": "warm modern pulse, instrumental, seamless loop", "seconds": 12 }, "volume": 0.12, "loop": true }`.
-- `tts.parameterId` makes the SPOKEN TEXT a render parameter: `modifications: { "voiceover": "new words" }` (multi-page: `"page1@voiceover"`) re-voices the video per render; `"voiceover.voiceId"` / `"voiceover.modelId"` tune the voice.
-- Budget narration at ~2 words/second plus 1 second of headroom for the page duration.
-- Via MCP, call `orshot_get_audio_authoring_options` first — it returns valid voice ids, models and the workspace's voice allowance in one read-only call.
-
-### Smart Resize (one design → any size)
-
-Render a template at a **different canvas size without redesigning it** — the layout is deterministically re-solved to fit (elements re-anchor, backgrounds stretch, groups move together). Set these on the `response` object:
-
-- **`size`** — *replace* the render size. A preset slug (`"instagram-story"`, `"og-image"`, `"youtube-thumbnail"`, …), a `"WIDTHxHEIGHT"` string (`"1080x1920"`), or use `width` + `height` (10–5000px each).
-- **`extraSizes`** — *add* the same design at extra sizes in one call. An array (`["1080x1920", "1080x1080"]`) or a named object (`{ story: "1080x1920", square: "1080x1080" }`). Each output gains a nested `extraSizes` array of `{ size, width, height, content }`.
+1. `orshot_list_studio_templates` with `search: "launch"`. One match: id `123`.
+2. `orshot_get_studio_template_modifications` with `templateId: 123`. Keys: `title`, `hero_image`.
+3. `orshot_render_template`:
 
 ```json
 {
   "templateId": 123,
-  "modifications": { "title": "Launch Day" },
-  "response": {
-    "type": "url",
-    "format": "png",
-    "size": "1200x630",
-    "extraSizes": ["1080x1920", "1080x1080"]
-  }
-}
-```
-
-Image formats only (`png`/`jpg`/`webp`/`avif`), up to 50 extra outputs per call. Each extra output is billed like a page (1 credit). Saved sizes from the Studio Smart Resize panel reproduce their approved preview exactly.
-
-### Response Types
-
-| Type     | Description                                     |
-| -------- | ----------------------------------------------- |
-| `url`    | Returns a hosted URL to the rendered file       |
-| `base64` | Returns base64-encoded content as a string      |
-| `binary` | Returns binary file content for custom handling |
-
-### Response Formats
-
-| Format | Type  | Notes                                      |
-| ------ | ----- | ------------------------------------------ |
-| `png`  | Image | Best quality, larger size                  |
-| `webp` | Image | Smaller size, good quality                 |
-| `jpg`  | Image | Compressed, no transparency                |
-| `avif` | Image | Smallest size, modern browsers             |
-| `pdf`  | Doc   | Supports multi-page, clickable links, CMYK |
-| `mp4`  | Video | H.264, requires video elements in template |
-| `webm` | Video | VP9, web-optimized                         |
-| `mov`  | Video | QuickTime container                        |
-| `mkv`  | Video | Matroska container                         |
-| `gif`  | Video | Animated, no audio support                 |
-
-### Render Usage (credits)
-
-Usage is measured in **credits**. 1 credit = 1 image, 1 PDF page, or 1 second of video.
-
-| Output               | Cost                     |
-| -------------------- | ------------------------ |
-| Image (PNG/JPG/WebP/AVIF) | 1 credit per image  |
-| PDF                  | 1 credit per page        |
-| Video (MP4/WebM/MOV/MKV/GIF) | 1 credit per second |
-
-Multi-page templates and Smart Resize extra sizes: each output page/size counts as its own credit. AI modifications (`.prompt`) additionally consume AI Credits.
-
-## Common Recipes
-
-End-to-end patterns for the jobs people most often automate with Orshot.
-
-### OG image on every deploy
-Render a studio template to a stable hosted URL and drop it into your `<meta>` tags.
-```js
-const { data } = await fetch("https://api.orshot.com/v1/studio/render", {
-  method: "POST",
-  headers: { "Content-Type": "application/json", Authorization: "Bearer <KEY>" },
-  body: JSON.stringify({
-    templateId: 123,
-    modifications: { title: post.title, author: post.author },
-    response: { type: "url", format: "png", size: "og-image" },
-  }),
-}).then((r) => r.json());
-// <meta property="og:image" content={data.content} />
-```
-
-### Bulk-generate from a CSV (certificates, badges, invoices)
-Loop rows and render one PDF each. Describe image layers with `.alt` for accessible PDFs.
-```js
-for (const row of rows) {
-  await fetch("https://api.orshot.com/v1/studio/render", {
-    method: "POST",
-    headers: { "Content-Type": "application/json", Authorization: "Bearer <KEY>" },
-    body: JSON.stringify({
-      templateId: 456,
-      modifications: { name: row.name, course: row.course, date: row.date },
-      response: { type: "url", format: "pdf" },
-      pdfOptions: { title: `${row.name} — Certificate` },
-    }),
-  });
-}
-```
-
-### Render and auto-post to social in one call
-Add a `publish` object (see Social Publishing) — no second request.
-```js
-body: JSON.stringify({
-  templateId: 123,
-  modifications: { title: "Launch day!" },
-  response: { type: "url", format: "png" },
-  publish: { accounts: [1, 2], content: "We just shipped 🚀" },
-});
-```
-
-### One design, every social size (Smart Resize)
-```js
-response: { type: "url", format: "png", extraSizes: ["1080x1920", "1080x1080", "1200x630"] }
-// each output gains a nested extraSizes[] of { size, width, height, content }
-```
-
-### Automate a recurring pipeline (Workflows)
-Create a workflow (trigger → fetch data → render → publish/deliver) with `POST /v1/workflows`, then trigger it with `POST /v1/workflows/:id/run`. Inspect results via `GET /v1/workflows/:id/runs`. Best driven through the MCP server or the Workflows API on Enterprise/first-party clients.
-
-See **Setting Up Recurring Automation** below for the full playbook, including how
-to wire it into an n8n/Make/Zapier setup the user already runs.
-
-## Setting Up Recurring Automation
-
-A one-off render is a demo. The value shows up when it runs unattended. Pick the
-road that matches where the user ALREADY works, not the one easiest to describe.
-
-### Choose the road first
-
-Check what they already run before recommending anything. `GET /v1/workspace/logs?limit=100`
-returns a `source` on every row, which answers it factually:
-
-| `source` in their logs | They already run | Lead with |
-| --- | --- | --- |
-| `n8n-integration` | n8n | Add a node to the n8n workflow they have |
-| `orshot-make` | Make | Add a module to their scenario |
-| `zapier-integration` | Zapier | Add an action to their Zap |
-| `orshot-pipedream` | Pipedream | Add a step |
-| `orshot-*-sdk`, `api`, `cli` | Their own code | Write the call into their repo |
-| only `playground` / `orshot-mcp-server` | Nothing yet | Offer Orshot Workflows |
-
-Never recommend a second orchestrator to someone who already has one. A user with a
-live n8n setup wants this template added to it, not a new tool to learn.
-
-If you have browser control or repo access, offer to DO the setup rather than
-describe it. Whichever road you take, **run it once and show the resulting image
-URL** before calling it done.
-
-### Road A — Orshot Workflows (no orchestrator yet)
-
-Orshot runs the whole loop: trigger, render, deliver. Over MCP:
-
-1. `orshot_suggest_workflows` with the `templateId` — returns automations that
-   fit this template, with draft-ready steps
-2. `orshot_list_workflow_nodes` — use the EXACT node keys it returns. Invented
-   keys (e.g. `render_studio_template`, `slack_send`) fail as `unknown node`;
-   the real keys are `render`, `slack`, and so on
-3. `orshot_list_connected_integrations` — see what is already connected
-4. `orshot_get_workflow_connection_data` — resolve "my content sheet" into a
-   concrete id, and confirm the name back to the user
-5. `orshot_validate_workflow` — read `errors`, `warnings` AND `gated`. A `gated`
-   entry means the step is not included in the workspace's current plan, not a
-   config problem: the draft still saves, only activation is blocked, so tell
-   the user that instead of editing steps
-6. `orshot_create_workflow` with `status: "draft"`, then share the edit link from
-   the result — it opens pre-configured in their dashboard
-7. `orshot_run_workflow` — prove it works, then they activate
-
-**Scaffolding shapes.** If they have no data source ready, use
-`webhook` → `webhook_body` → `render` → `orshot_url`. It is the only
-trigger→source pair needing no OAuth AND it validates clean with empty configs,
-so it is runnable immediately and they can POST to it from cron, their app, n8n,
-or Make. A bare `schedule` → `render` is NOT runnable — it fails with "no data
-source"; a schedule needs a source step after it.
-
-### Road B — their existing automation platform
-
-Every platform needs the same three things: endpoint, auth header, modification
-keys. Get the exact keys first — never guess them:
-
-```
-orshot_get_studio_template_modifications   (or GET /v1/studio/templates/:id/modifications)
-```
-
-Then the call to drop in:
-
-```http
-POST https://api.orshot.com/v1/studio/render
-Authorization: Bearer <ORSHOT_API_KEY>
-Content-Type: application/json
-
-{
-  "templateId": 1234,
-  "modifications": { "headline": "{{ row.title }}", "hero_image": "{{ row.image_url }}" },
-  "response": { "type": "url", "format": "png" }
+  "modifications": { "title": "We just shipped" },
+  "format": "png",
+  "size": "og-image",
+  "extraSizes": ["instagram-story"]
 }
 ```
 
-The response contains the rendered URL — map it to whatever the next step needs.
-
-- **n8n** — HTTP Request node, POST, header auth, body as above. A dedicated
-  Orshot node also exists in the n8n library.
-- **Make** — HTTP "Make a request" module, or the Orshot app modules.
-- **Zapier** — Webhooks by Zapier → POST, or the Orshot Zapier app.
-- **Pipedream** — HTTP step; Orshot components are published.
-- **Own code / cron** — any HTTP client. Use `response.type: "url"` in production
-  so you are not moving base64 around, and keep the API key in an environment
-  variable, never inline.
-
-Recurring triggers that work well: a new spreadsheet/Airtable/Notion row, a
-schedule, an inbound webhook, or a CMS publish event.
-
-### Verify before you call it done
-
-1. Trigger one real run
-2. `orshot_list_workspace_logs` (or `GET /v1/workspace/logs`) — the new render
-   appears with the `source` of whatever fired it, proving which system made it
-3. Show the user the image URL from that run
-
-If the render 403s, read the body: an expired key, an inactive subscription, and
-a plan limit all return 403 with different messages. A plan block is not an auth
-problem — do not retry it as one.
-
-## Integrations & Helps
-
-### Integrations Service Support
-Orshot connects with:
-- **No-code:** Zapier, Make (Integromat), n8n, Pipedream, Airtable
-- **Storage:** Amazon S3, Cloudflare R2, Google Drive, Dropbox
-- **Notifications:** Slack, Webhooks
-- **Design:** Figma Plugin, Canva Import, Polotno Import
-- **CLI:** `npx orshot-cli` for terminal-based generation
-- **MCP Server:** Use with Claude, Cursor, Windsurf via MCP protocol
-- **Embed:** White-label design editor for your app (React SDK, Vue SDK, iframe)
-
-### Common Error Codes
-
-| Code | Error                          | Fix                                          |
-| ---- | ------------------------------ | -------------------------------------------- |
-| 400  | `templateId missing`           | Add `templateId` to request body             |
-| 400  | `Invalid API Key`              | Generate new key from dashboard              |
-| 403  | `Authorization header missing` | Add `Authorization: Bearer <KEY>` header     |
-| 403  | `Subscription inactive`        | The workspace subscription is not active; check it in the dashboard |
-| 403  | `Template not found`           | Verify template ID belongs to your workspace |
-| 403  | `ai-video-not-on-plan`         | AI video is not included in the workspace's current plan; use stock footage or a Brand Library video instead |
-
-### General Best Practices
-
-1. **Use `url` response type** for production – avoids large base64 payloads
-2. **Use `webp` format** for smaller file sizes with good quality
-3. **Test in Playground** before coding – each template has an interactive playground
-4. **Use style parameters** instead of creating multiple template variants
-5. **Use consistent units** – stick to `px` for sizes
-6. **Multi-page prefix** – always use `page1@paramId` format for carousel templates
-7. **Cache renders** – use `?cache=false` query param to bypass cache when needed
-8. **Handle errors** – check for 403/400 status codes and parse error messages
-
-## Social Publishing
-
-Publish rendered images/videos directly to 13+ social platforms via API. Supports Twitter/X, Instagram, LinkedIn, Pinterest, Facebook, TikTok, YouTube, Threads, Bluesky, Reddit, Telegram, Snapchat, and Google Business.
-
-For detailed setup: fetch `https://orshot.com/docs/publish/introduction.md`
-
-### Connect Accounts
+4. Check: the result lists every output URL and each extra size. A per-size `error` means that one size failed; the others are fine. Show the user every link.
 
-Connect social accounts in **Workspace Settings → Social Accounts** in the Orshot dashboard. Each connected account gets a numeric ID used in API calls.
-
-### Publish from API
-
-Add the `publish` object to any render request:
+The same render over REST:
 
 ```js
 await fetch("https://api.orshot.com/v1/studio/render", {
   method: "POST",
-  headers: {
-    "Content-Type": "application/json",
-    Authorization: "Bearer <ORSHOT_API_KEY>",
-  },
+  headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.ORSHOT_API_KEY}` },
   body: JSON.stringify({
     templateId: 123,
-    modifications: { title: "New Post" },
-    response: { type: "url", format: "png" },
-    publish: {
-      accounts: [1, 3],          // Social account IDs
-      content: "Check this out!", // Caption text (max 5000 chars)
-      isDraft: false,             // true = save as draft
-      schedule: {                 // Optional: schedule for later
-        scheduledFor: "2026-04-25T14:00:00Z"
-      },
-      timezone: "America/New_York",
-      platformOptions: {          // Per-account overrides (keyed by account ID)
-        "1": { firstComment: "https://example.com" },     // LinkedIn
-        "3": { title: "Pin Title", link: "https://..." }   // Pinterest
-      }
-    }
+    modifications: { title: "We just shipped" },
+    response: { type: "url", format: "png", size: "og-image", extraSizes: ["instagram-story"] },
   }),
 });
 ```
 
-**Response includes publish status:**
-```json
-{
-  "data": { "content": "https://storage.orshot.com/..." },
-  "publish": [
-    { "platform": "twitter", "username": "acme", "status": "published", "url": "https://x.com/..." },
-    { "platform": "linkedin", "username": "acme", "status": "scheduled" }
-  ]
-}
-```
-
-**Format compatibility:**
-- PDF cannot be published to any platform
-- Video (mp4/webm/gif) cannot be published to Google Business
-- Image (png/jpg/webp) cannot be published to TikTok or YouTube (video-only)
-
-## Developer Apps & OAuth
-
-Build third-party integrations with Orshot using OAuth 2.0. Register an app, authenticate users, and access their workspaces on their behalf.
-
-For detailed setup: fetch `https://orshot.com/docs/developers/oauth-overview.md`
-
-### Register an App
-
-1. Go to **Workspace Settings → Developer Apps** in the Orshot dashboard
-2. Create a new app with a name, redirect URI, and the scopes it needs
-3. Use the app's `client_id` in the flows below. Orshot OAuth clients are public clients: PKCE (`S256`) is required and no client secret is used.
-
-Discovery: `https://api.orshot.com/.well-known/oauth-authorization-server` (dynamic client registration is available at `registration_endpoint`).
-
-### OAuth 2.0 Flows
-
-**Authorization Code + PKCE** (web and desktop apps):
-```
-GET https://orshot.com/oauth/authorize?
-  response_type=code&
-  client_id=YOUR_CLIENT_ID&
-  redirect_uri=https://yourapp.com/callback&
-  code_challenge=BASE64URL_SHA256_OF_VERIFIER&
-  code_challenge_method=S256&
-  scope=workspace:templates:read workspace:templates:write render:generate
-```
-
-**Device Flow** (CLI/headless):
-```
-POST https://api.orshot.com/v1/oauth/device/code
-  client_id=YOUR_CLIENT_ID&
-  scope=workspace:templates:read render:generate
-```
-
-### Token Exchange
-
-```
-POST https://api.orshot.com/v1/oauth/token
-  grant_type=authorization_code&
-  code=AUTH_CODE&
-  client_id=YOUR_CLIENT_ID&
-  code_verifier=THE_PKCE_VERIFIER&
-  redirect_uri=https://yourapp.com/callback
-```
-
-Refresh with `grant_type=refresh_token` when the `offline_access` scope was granted. Store tokens server-side or in the user's own environment; never place them in prompts, logs, or shared files.
-
-### Available Scopes
-
-| Scope | Description |
-|-------|-------------|
-| `workspace:read` | List and read workspace details |
-| `workspace:templates:read` | List and read templates |
-| `workspace:templates:write` | Update template modifications via API |
-| `render:generate` | Generate images, PDFs, and videos |
-| `mcp:access` | Access via Model Context Protocol |
-| `offline_access` | Long-lived refresh tokens |
-
-For endpoint details: fetch `https://orshot.com/docs/developers/oauth-endpoints.md`
-
-## White-Label Embed (Orshot Embed)
-
-Embed Orshot's template editor into your application as a white-label component. Users can design and customize templates directly in your app.
-
-For detailed setup: fetch `https://orshot.com/docs/orshot-embed/introduction.md`
-
-### Quick Setup (iframe)
-
-```html
-<iframe
-  src="https://orshot.com/embeds/YOUR_EMBED_ID?userId=USER_123"
-  width="100%"
-  height="600"
-  frameborder="0"
-></iframe>
-```
-
-### React SDK
-
-```bash
-npm install @orshot/react
-```
-
-```jsx
-import { OrshotEmbed } from "@orshot/react";
-
-<OrshotEmbed
-  embedId="YOUR_EMBED_ID"
-  userId="user_123"
-  token="JWT_TOKEN"
-  onRender={(data) => console.log("Rendered:", data)}
-  onSave={(data) => console.log("Saved:", data)}
-/>
-```
-
-### Vue SDK
-
-```bash
-npm install @orshot/vue
-```
-
-```vue
-<template>
-  <OrshotEmbed
-    embed-id="YOUR_EMBED_ID"
-    user-id="user_123"
-    @render="onRender"
-    @save="onSave"
-  />
-</template>
-```
+Preset names and size rules: [render-from-studio-template, Smart Resize](https://orshot.com/docs/api-reference/render-from-studio-template.md).
 
-### Key Features
+## Worked example: render and hold as a draft post
+
+Prompt: "Render this week's quote card and put it on LinkedIn as a draft."
+
+1. `orshot_list_social_accounts`. Pick the LinkedIn account id, say `3`. An empty list means no account is connected yet; a person connects one in the dashboard ([connect-account](https://orshot.com/docs/publish/connect-account.md)), the API cannot. A draft or scheduled post can still be saved with `platforms` instead of `accounts`; once an account on that platform is connected, add it with `orshot_update_social_post` and send the post. Publishing now needs an account.
+2. `orshot_render_template` with the modifications. It returns the image URL.
+3. `orshot_publish_social_post` with `mediaUrls: [that URL]`, `accounts: [3]`, `content` and `status: "draft"`. Check the result per account.
+4. After the user approves: `orshot_publish_social_draft`. To change it first: `orshot_update_social_post`.
+
+Over REST, the render `publish` object reads `isDraft` and `schedule.scheduledFor` for timing, not `status` ([publish-from-api](https://orshot.com/docs/publish/publish-from-api.md)). To post a file that already exists, use `orshot_publish_social_post` or `POST /v1/social/publish`. Scheduling, analytics and best time to post: the `orshot-social-media-scheduler` skill.
+
+## Other jobs
+
+- **Utility templates** (tweet and website screenshots, bar and comparison charts, app store screenshots, phone mockups, background removal): `orshot_render_library_template` or `POST /v1/generate/:renderType`.
+- **Many variants of one template** (a list of names, a set of products): `orshot_batch_render_template`, or loop the REST render. A full invoice or certificate run: `orshot-pdf-generator`.
+- **Stable image URLs** for OG tags, emails and embeds: `orshot_create_dynamic_url`. OG images for a whole blog or site: `orshot-blog-images`.
+- **Brand assets**: `orshot_get_brand_kit`, `orshot_import_brand_website`.
+- **Folders and workspaces**: `orshot_list_template_folders`, `orshot_set_active_workspace`.
+- **Recurring jobs** (n8n, Make, Zapier, Pipedream, cron, Orshot Workflows): `references/recurring-automation.md`.
+
+## Docs map
+
+| Job | Docs page |
+| --- | --- |
+| Render a studio template: body, response object, Smart Resize, PDF, video, publish | https://orshot.com/docs/api-reference/render-from-studio-template.md |
+| Render a library (utility) template | https://orshot.com/docs/api-reference/render-from-template.md |
+| What modification values can be | https://orshot.com/docs/definitions/modifications.md |
+| Style overrides with dot keys | https://orshot.com/docs/orshot-studio/style-parameters.md |
+| AI-generated values (`.prompt`) | https://orshot.com/docs/dynamic-parameters/prompt.md |
+| Output formats and response types | https://orshot.com/docs/definitions/response-format.md, https://orshot.com/docs/definitions/response-type.md |
+| Long renders as jobs | https://orshot.com/docs/api-reference/async-render-start.md |
+| Video options, audio, captions | https://orshot.com/docs/video-generation/video-options.md, https://orshot.com/docs/video-generation/adding-subtitles.md |
+| PDF options | https://orshot.com/docs/pdf-generation/pdf-options.md |
+| Plan a design: the formats brands run for the job, palette, type | `orshot_plan_design` (brief, `job`, size), then `orshot_get_design_spec` for the craft, the packs, the film schema, the layout grammar and the format references |
+| Template JSON (pages, elements, styles, animation) | `orshot_get_design_spec` topic `template-json`, https://orshot.com/docs/definitions/template-json.md |
+| Create or update templates over REST (Enterprise) | https://orshot.com/docs/api-reference/enterprise-template-create.md |
+| List and read templates | https://orshot.com/docs/api-reference/studio-templates-list.md, https://orshot.com/docs/api-reference/studio-template-get.md |
+| Dynamic URLs | https://orshot.com/docs/integrations/dynamic-urls.md |
+| Signed render URLs (REST) | https://orshot.com/docs/api-reference/generate-signed-url.md |
+| Social posting | https://orshot.com/docs/publish/publish-from-api.md, https://orshot.com/docs/api-reference/social-publish.md |
+| Social add-on pricing | https://orshot.com/docs/publish/pricing.md |
+| Credits | https://orshot.com/docs/definitions/credits-usage.md |
+| Errors | https://orshot.com/docs/error-reference.md |
+| Workspace logs and `source` values | https://orshot.com/docs/api-reference/workspace-logs.md |
+| Workflows over REST | https://orshot.com/docs/api-reference/workflow-create.md |
+| n8n, Make, Zapier, Pipedream and other integrations | https://orshot.com/docs/integrations/index.md |
+| SDKs (Node, Python, PHP, Ruby) | https://orshot.com/docs/sdks/index.md |
+| Importing templates | https://orshot.com/docs/importing-templates/index.md |
+| White-label editor embed | https://orshot.com/docs/orshot-embed/introduction.md |
+| Third-party apps with OAuth | https://orshot.com/docs/developers/oauth-overview.md |
+| MCP setup and tool list | https://orshot.com/docs/integrations/mcp-server.md |
 
-- **Per-user templates**: Pass `userId` to give each user their own template copies
-- **JWT authentication**: Secure embed access with domain whitelist validation
-- **Webhooks**: Get notified on render completion, template save, etc.
-- **Custom buttons**: Add your own action buttons to the editor toolbar
-- **PostMessage API**: Control the embed programmatically from your app
-
-For React SDK details: fetch `https://orshot.com/docs/orshot-embed/react-sdk.md`
-For Vue SDK details: fetch `https://orshot.com/docs/orshot-embed/vue-sdk.md`
-For webhook setup: fetch `https://orshot.com/docs/orshot-embed/webhooks.md`
-
-## Migrating from Another Template-Render API
-
-The API-call swap is the easy part. The real work is recreating the design, because another platform's template JSON cannot be imported:
-
-1. **Recreate the template in Orshot Studio** (or via `POST /v1/studio/templates/create`). Rebuild the layout, then mark every dynamic layer parameterizable with a clear `parameterId`.
-2. **Map old field names → Orshot `parameterId`s.** Other APIs key by layer name or by fixed keys such as `image_url`; Orshot keys by `parameterId`. Keep a lookup from old names to new ones.
-3. **Swap the API call**: `Authorization: Bearer <ORSHOT_API_KEY>` + `POST /v1/studio/render` with `modifications` as a flat object (page-prefixed keys for multi-page templates).
-4. **Handle sync vs async.** Many render APIs are submit-then-poll; Orshot image and PDF renders return synchronously, so delete the polling/webhook code and read `data.content` from the response. Long video renders use async render jobs (see above).
-5. **Style overrides** that other APIs put in per-layer objects become dot-notation keys here (`"title.fontSize": "48px"`).
-6. **Verify the response shape** — single page is an object, multi-page is an array (Common Gotchas #7) — before cutting traffic over.
-
-Render URLs on Orshot persist; do not add expiry handling unless you delete renders yourself.
-
-### Links
-
-- Documentation: https://orshot.com/docs
-- API Reference: https://orshot.com/docs/api-reference
-- Integrations: https://orshot.com/integrations
-- MCP Server: https://orshot.com/docs/integrations/mcp-server
-- Templates: https://orshot.com/templates
-- Developer Apps: https://orshot.com/docs/developers
-- Social Publishing: https://orshot.com/docs/publish/introduction
-- Orshot Embed: https://orshot.com/docs/orshot-embed/introduction
+The MCP server also serves `orshot://formats` (output formats and size presets).
Full technical diff · 3 changed fields

changed /description

BEFORE
"Generate images, PDFs, and videos from templates with Orshot — via the REST API, SDKs (Node/Python/PHP/Ruby), the remote MCP server, or no-code tools (Zapier, Make, n8n, Airtable), and wire those renders into a recurring automation. Use whenever the user mentions Orshot, connects Orshot to an AI agent or MCP client, wants a render to run unattended on a schedule or trigger, or needs to programmatically produce marketing visuals, OG images, social carousels, certificates/invoices/tickets as PDF, or video from templates — including when migrating from another template-based image-generation API. Always load this skill for Orshot tasks, even simple ones, because it carries gotchas (modifications key by parameterId, multi-page needs a page1@ prefix, long video renders need async render jobs) that otherwise cause failed renders. Not for generating standalone AI images with no template or Orshot involved."
AFTER
"Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch, async), dynamic image URLs, utility templates (tweet and website screenshots, charts, mockups), posting a render to connected social accounts, wiring it into n8n, Make, Zapier or cron, and the gotchas that break renders (parameterId keys, page1@ prefixes, public image URLs). Use when: \"render my Orshot template\", \"connect Orshot to Claude\", \"call the Orshot API\", \"my modification is ignored\", \"automate this render in n8n\", \"screenshot this tweet\". One job end to end: load its skill (orshot-blog-images, orshot-pdf-generator, orshot-video-generator, orshot-ad-creative, orshot-carousel, orshot-youtube-thumbnail, orshot-social-media-content or orshot-social-media-scheduler). NOT for template design craft (use orshot-design)."

changed /included_files

BEFORE
[]
AFTER
[
  {
    "relative_path": "references/recurring-automation.md",
    "size_in_bytes": 5563
  }
]

changed /skill_md_contents

BEFORE
"---\nname: orshot\ndescription: Generate images, PDFs, and videos from templates with Orshot — via the REST API, SDKs (Node/Python/PHP/Ruby), the remote MCP server, or no-code tools (Zapier, Make, n8n, Airtable), and wire those renders into a recurring automation. Use whenever the user mentions Orshot, connects Orshot to an AI agent or MCP client, wants a render to run unattended on a schedule or trigger, or needs to programmatically produce marketing visuals, OG images, social carousels, certificates/invoices/tickets as PDF, or video from templates — including when migrating from another template-based image-generation API. Always load this skill for Orshot tasks, even simple ones, because it carries gotchas (modifications key by parameterId, multi-page needs a page1@ prefix, long video renders need async render jobs) that otherwise cause failed renders. Not for generating standalone AI images with no template or Orshot involved.\nmetadata:\n  author: Rishi Mohan\n  version: \"1.3.0\"\n---\n\n# Orshot – Automated Visual Content Generation\n\n[Orshot](https://orshot.com) is an automated image, PDF, and video generation platform. Design templates in Orshot Studio (or import from Canva/Figma), then generate renders via REST API, SDKs, or no-code integrations.\n\n- **Documentation:** https://orshot.com/docs\n- **API Base URL:** https://api.orshot.com/v1\n\n## When to Use This Skill\n\nUse this skill whenever the user mentions **Orshot**, or when the task is:\n\n- Generating images, PDFs, or videos programmatically from templates\n- Building automated marketing visual pipelines (OG images, ad creatives, thumbnails)\n- Creating dynamic social media content (carousels, posts, stories) and publishing it\n- Generating certificates, invoices, tickets, or reports as PDFs\n- Building image/PDF/video generation into a product\n- Automating visuals with Zapier, Make, n8n, Airtable, or the CLI\n- Connecting Orshot to an AI agent or MCP client (Claude, Cursor, Codex, Windsurf, ChatGPT)\n- Embedding a white-label design editor into an app\n- Migrating from another template-based image/PDF/video generation API\n\n**Don't use this skill** for generating standalone AI images with no template or Orshot involved (a one-off \"make me an image\" request) — that isn't what Orshot does.\n\n## Accessing Detailed Documentation\n\nAny page on orshot.com can be fetched as clean markdown by appending `.md` to the URL or sending `Accept: text/markdown` header. Use this to get detailed, up-to-date information on demand without relying solely on this skill file.\n\n**Examples:**\n```\nhttps://orshot.com/docs/api-reference.md\nhttps://orshot.com/docs/sdks/node.md\nhttps://orshot.com/docs/publish/publish-from-api.md\nhttps://orshot.com/docs/developers/oauth-overview.md\nhttps://orshot.com/docs/orshot-embed/introduction.md\n```\n\n**Key documentation pages:**\n| Topic | URL |\n|-------|-----|\n| API Reference | `https://orshot.com/docs/api-reference.md` |\n| Node.js SDK | `https://orshot.com/docs/sdks/node.md` |\n| Python SDK | `https://orshot.com/docs/sdks/python.md` |\n| PHP SDK | `https://orshot.com/docs/sdks/php.md` |\n| Ruby SDK | `https://orshot.com/docs/sdks/ruby.md` |\n| Studio Templates | `https://orshot.com/docs/orshot-studio/introduction.md` |\n| Style Parameters | `https://orshot.com/docs/orshot-studio/style-parameters.md` |\n| Setting Parameters | `https://orshot.com/docs/orshot-studio/setting-parameters.md` |\n| Image Generation | `https://orshot.com/docs/image-generation.md` |\n| Video Generation | `https://orshot.com/docs/video-generation.md` |\n| PDF Generation | `https://orshot.com/docs/pdf-generation.md` |\n| Social Publishing | `https://orshot.com/docs/publish/introduction.md` |\n| OAuth / Developer Apps | `https://orshot.com/docs/developers.md` |\n| White-Label Embed | `https://orshot.com/docs/orshot-embed/introduction.md` |\n| Integrations | `https://orshot.com/docs/integrations.md` |\n| Dynamic URLs | `https://orshot.com/docs/integrations/dynamic-urls.md` |\n| Webhooks | `https://orshot.com/docs/integrations/webhooks.md` |\n| Error Reference | `https://orshot.com/docs/error-reference.md` |\n\nWhen a user asks about a specific topic, fetch the relevant `.md` URL for the latest details.\n\n## Getting Started\n\n### Authentication\n\nAll API requests require a Bearer token in the `Authorization` header:\n\n```\nAuthorization: Bearer <ORSHOT_API_KEY>\n```\n\n[Get your API key](https://orshot.com/docs/quick-start/get-api-key) from **Workspace Settings → API Keys** in the Orshot dashboard.\n\n### Remote MCP Server\n\nOrshot runs a hosted MCP server so agents (Claude, Cursor, Codex, Windsurf, ChatGPT, etc.) can use Orshot as tools — no local install.\n\n- **URL:** `https://mcp.orshot.com/mcp` (transport: `streamable-http`)\n- **Auth:** OAuth 2.0 (the client walks you through it), or send an Orshot API key as a Bearer token\n- **Claude Code:** `claude mcp add --transport http orshot https://mcp.orshot.com/mcp`\n- **Cursor / Windsurf / VS Code / ChatGPT / Codex:** add the URL as a remote/HTTP MCP server\n\nIt exposes tools for rendering, studio + library templates, brand assets, workflows, social accounts, and workspace/logs. Full discovery doc: `https://orshot.com/.well-known/mcp.json`. Setup guide: `https://orshot.com/docs/integrations/mcp-server.md`.\n\n### SDKs\n\n#### Node.js\n\n```bash\nnpm install orshot\n```\n\n```js\nimport { Orshot } from \"orshot\";\nconst orshot = new Orshot(\"<ORSHOT_API_KEY>\");\n\n// Render from template\nconst response = await orshot.renderFromTemplate({\n  templateId: \"open-graph-image-1\",\n  modifications: { title: \"Hello World\" },\n  responseType: \"base64\", // \"base64\" | \"url\" | \"binary\"\n  responseFormat: \"png\", // \"png\" | \"webp\" | \"jpg\" | \"pdf\"\n});\n\n// Generate signed URL\nconst signedUrl = await orshot.generateSignedUrl({\n  templateId: \"open-graph-image-1\",\n  modifications: { title: \"Hello\" },\n  expiresAt: 1744276943,\n  renderType: \"images\",\n  responseFormat: \"png\",\n});\n```\n\n#### Python\n\n```bash\npip install orshot\n```\n\n```python\nimport orshot\nos = orshot.Orshot('<ORSHOT_API_KEY>')\n\nresponse = os.render_from_template({\n  'template_id': 'open-graph-image-1',\n  'modifications': {'title': 'Hello World'},\n  'response_type': 'base64',\n  'response_format': 'png'\n})\n```\n\n#### Other SDKs\n\n- **PHP:** `composer require rishimohan/orshot`\n- **Ruby:** `gem install orshot`\n\n## Common Gotchas (read before rendering)\n\nThe mistakes that most often make Orshot calls fail or return the wrong output:\n\n1. **Modifications key by `parameterId`, not layer name.** `modifications: { \"headline\": \"...\" }` targets the element whose `parameterId` is `headline`. If a value is ignored, the key is wrong — fetch the template's modifications (`GET /v1/studio/templates/:id`) to see the real keys instead of guessing.\n2. **Multi-page templates need a `pageN@` prefix.** Use `\"page1@title\"`, `\"page2@title\"`. A bare `\"title\"` only affects page 1.\n3. **Studio `templateId` is an integer; utility `templateId` is a string.** `/v1/studio/render` takes an integer ID; `/v1/generate/:renderType` takes a string slug like `\"website-screenshot\"`.\n4. **Video output does NOT require video elements.** `format: \"mp4\"`/`\"webm\"`/`\"gif\"` renders any template — motion can come from video elements, page animations (enter/exit/ambient transitions), page-to-page transitions, or generated narration; a fully static page renders as a still clip of its `videoDuration`. Long video renders should use async render jobs (`response.mode: \"async\"`) so they don't hit client timeouts.\n5. **Image/video URLs in modifications must be publicly reachable.** The renderer fetches them server-side, so `localhost`, expired signed URLs, or auth-gated URLs won't load.\n6. **Style overrides use dot notation on the parameterId** — `\"title.fontSize\": \"48px\"`, not a nested object.\n7. **Response shape differs by page count:** single page → `data` is an object (read `data.content`); multi-page/carousel → `data` is an array of `{ page, content }`. Handle both.\n8. **`base64` and `binary` response types don't combine** — `binary` returns one raw file stream.\n9. **Credits vs AI Credits are separate** — normal renders spend credits; `.prompt` (AI) modifications additionally spend AI Credits.\n10. **On `429`, back off** using the `Retry-After` response header.\n\nWhen unsure about a template's parameters, always fetch its modifications first.\n\n## Template Architecture\n\nThis section describes the complete structure of an Orshot template for MCP tools and AI agents.\n\n### Template Structure\n\nAn Orshot template consists of **pages**, each containing a **canvas** and **elements**.\n\n```\nTemplate\n├── id: number | string\n├── name: string\n├── description: string\n├── width: number\n├── height: number\n├── pages_data: Array\n    └── Page\n        ├── id: string (UUID)\n        ├── name: string\n        ├── canvas: CanvasConfig\n            ├── width: number\n            ├── height: number\n            ├── backgroundColor: string\n            ├── backgroundImage: string\n        ├── elements: Element[]\n        ├── modifications: Modification[] (API parameters)\n            ├── id: string\n            ├── type: string\n            ├── element: Element\n            ├── description: string\n        └── thumbnail_url: string | null\n```\n\n### Canvas Configuration\n\n| Property          | Type   | Default         | Description                               |\n| ----------------- | ------ | --------------- | ----------------------------------------- |\n| `width`           | number | 800             | Canvas width in pixels (max: 5000)        |\n| `height`          | number | 800             | Canvas height in pixels (max: 5000)       |\n| `backgroundColor` | string | \"#ffffff\"       | Background color (hex, rgba, or gradient) |\n| `backgroundImage` | string | \"\"              | URL to background image                   |\n| `borderWidth`     | number | 0               | Border width in pixels                    |\n| `borderColor`     | string | \"rgba(0,0,0,1)\" | Border color                              |\n| `borderStyle`     | string | \"solid\"         | Border style (solid, dashed, etc)         |\n\n### Canvas Size Presets\n\n| Name                 | Dimensions | Use Case                        |\n| -------------------- | ---------- | ------------------------------- |\n| Square               | 1080×1080  | Instagram posts, general social |\n| Instagram Story      | 1080×1920  | Stories, Reels, TikTok          |\n| Slide/Presentation   | 1920×1080  | Presentations, slides           |\n| YouTube Thumbnail    | 1280×720   | Video thumbnails                |\n| Twitter Post         | 1600×900   | X/Twitter posts                 |\n| Open Graph           | 1200×630   | Link previews, Facebook         |\n| Pinterest Pin        | 1000×1500  | Pinterest                       |\n| A4 Document          | 2480×3508  | Print documents                 |\n| App Store Screenshot | 1290×2796  | iOS app screenshots             |\n\n### Universal Element Properties\n\nAll elements share these base properties:\n\n| Property            | Type    | Description                                  |\n| ------------------- | ------- | -------------------------------------------- |\n| `id`                | string  | Unique identifier (UUID)                     |\n| `name`              | string  | Display name in layer list (was `layerName`) |\n| `type`              | string  | \"text\", \"image\", \"shape\", \"video\"            |\n| `position`          | object  | `{ x: number, y: number }` from top-left     |\n| `dimensions`        | object  | `{ width: number, height: number }`          |\n| `rotation`          | number  | Rotation in degrees (0-360)                  |\n| `zIndex`            | number  | Layer order (higher = on top)                |\n| `aspectRatioLocked` | boolean | Lock aspect ratio during resize              |\n| `isHidden`          | boolean | Hide element from render                     |\n| `skewX`             | number  | Horizontal skew angle                        |\n| `skewY`             | number  | Vertical skew angle                          |\n\n### Text Element\n\n**Content Types:**\n\n- **Plain text**: `\"Hello World\"` - Standard text string\n- **Multi-line**: Use `\\n` for line breaks: `\"Line 1\\nLine 2\"`\n- **Dynamic via API**: Use `.prompt` modifier for AI-generated text\n\n```javascript\n{\n  type: \"text\",\n  content: string,          // Plain text string\n  layerName: string,        // Display name\n  zIndex: number,\n  rotation: number,\n  position: { x: number, y: number },\n  dimensions: { width: number, height: number },\n  style: {\n    // Typography\n    fontFamily: string,     // e.g., \"Inter\", \"Prata\", \"SF Pro\"\n    fontSize: string,       // e.g., \"48px\"\n    fontWeight: string | number, // \"400\", \"700\", 700\n    fontStyle: string,      // \"normal\", \"italic\"\n    lineHeight: number,     // e.g., 1.2\n    letterSpacing: string,  // e.g., \"0px\", \"2px\"\n\n    // Appearance\n    fill: string,           // Color or gradient\n    color: string,          // Hex or rgba\n    opacity: number,        // 0-1\n    stroke: string,         // Stroke color\n    strokeWidth: string,    // e.g., \"0px\"\n\n    // Alignment & Layout\n    textAlign: string,      // \"left\", \"center\", \"right\"\n    verticalAlign: string,  // \"flex-start\", \"center\", \"flex-end\"\n    textTransform: string,  // \"none\", \"uppercase\"\n    textDecoration: string, // \"none\", \"underline\"\n    textMode: string,       // \"overflow\", \"fit\"\n    paddingX: string,\n    paddingY: string,\n\n    // Borders & Backgrounds\n    borderColor: string,\n    borderWidth: string,\n    borderRadius: string,\n    textBackgroundColor: string,\n    textBackgroundRadius: string,\n    textStrokeColor: string,\n    textStrokeWidth: string,\n\n    // Effects\n    minFontSize: string,    // For \"fit\" mode\n    filter: string,         // e.g., \"blur(0px)\"\n    mixBlendMode: string,   // \"normal\", \"multiply\", etc.\n    boxShadowX: string,\n    boxShadowY: string,\n    boxShadowBlur: string,\n    boxShadowColor: string,\n    dropShadowX: string,\n    dropShadowY: string,\n    dropShadowBlur: string,\n    dropShadowColor: string\n  },\n  // Parameterization\n  parameterizable: boolean,\n  parameterId: string,\n  parameterType: \"text\"\n}\n```\n\n**Gradient text:**\n\n```javascript\ncolor: \"linear-gradient(90deg, #FF6B6B 0%, #4ECDC4 100%)\";\n```\n\n### Image Element\n\n**Content Types:**\n\n- **URL** (recommended): `\"https://example.com/image.png\"` - Best for dynamic content\n- **Base64**: `\"data:image/png;base64,iVBORw0KGgo...\"` - For embedded images\n- **Binary**: Raw binary data (API upload only)\n\n```javascript\n{\n  type: \"image\",\n  content: string,          // URL (preferred), base64, or binary\n  isSvg: boolean,\n  layerName: string,\n  style: {\n    // Sizing & Positioning\n    objectFit: string,      // \"contain\", \"cover\", \"fill\"\n    objectPosition: string, // \"center\", \"top left\"\n\n    // Appearance\n    opacity: number,\n    fill: string,           // Background fill\n    stroke: string,         // Border stroke\n\n    // Borders\n    borderRadius: string,   // \"0px\", \"12px\", \"50%\"\n    borderWidth: string,\n    borderColor: string,\n\n    // Effects\n    filter: string,         // \"blur(2px)\", \"grayscale(100%)\"\n    mixBlendMode: string,\n    boxShadowX: string,\n    boxShadowY: string,\n    boxShadowBlur: string,\n    boxShadowColor: string,\n    dropShadowX: string,\n    dropShadowY: string,\n    dropShadowBlur: string,\n    dropShadowColor: string,\n\n    svgColor: string        // Recolor monochrome SVGs\n  },\n  parameterType: \"imageUrl\"\n}\n```\n\n### Shape Element\n\n```javascript\n{\n  type: \"shape\",\n  shapeType: string,        // \"rectangle\", \"circle\", \"arrow\"\n  layerName: string,\n  style: {\n    // Fill & Stroke\n    fill: string,           // Color or gradient\n    stroke: string,\n    strokeWidth: string,    // e.g. \"0px\"\n\n    // Dimensions\n    borderRadius: string,   // Rectangle only\n    borderWidth: string,\n    borderColor: string,\n    borderStyle: string,\n\n    // Appearance\n    opacity: number,\n    filter: string,\n    mixBlendMode: string,\n\n    // Shadows\n    boxShadowX: string,\n    boxShadowY: string,\n    boxShadowBlur: string,\n    boxShadowColor: string,\n    dropShadowX: string,\n    dropShadowY: string,\n    dropShadowBlur: string,\n    dropShadowColor: string\n  },\n  parameterType: \"fill\"\n}\n```\n\n**Gradient fills:**\n\n```javascript\nfill: \"linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%)\";\nfill: \"radial-gradient(circle at center, #FF6B6B 0%, #4ECDC4 100%)\";\n```\n\n### Video Element\n\n**Content Types:**\n\n- **URL** (required): `\"https://example.com/video.mp4\"` - Must be a publicly accessible URL\n- Supported formats: MP4, WebM, MOV\n- For best results, use MP4 with H.264 codec\n\n```javascript\n{\n  type: \"video\",\n  content: string,          // Video URL (must be publicly accessible)\n  videoOptions: {\n    loop: boolean,\n    muted: boolean,\n    trim_start_time: string,\n    trim_end_time: string,\n    duration: number | null\n  },\n  style: {\n    // Sizing & Positioning\n    objectFit: string,      // \"contain\", \"cover\", \"fill\"\n    objectPosition: string,\n\n    // Appearance\n    opacity: number,\n    filter: string,\n    mixBlendMode: string,\n\n    // Borders & Shadows\n    borderRadius: string,\n    borderWidth: string,\n    borderColor: string,\n    elementBoxShadowX: string,\n    elementBoxShadowY: string,\n    elementBoxShadowBlur: string,\n    elementBoxShadowColor: string\n  },\n  parameterType: \"videoUrl\"\n}\n```\n\n### Parameterization Best Practices\n\nWhen creating or updating templates, **always ensure all text, image, and video elements are parameterizable** with unique IDs. This enables dynamic content replacement via the API.\n\n#### Required Setup\n\nEvery dynamic element MUST have:\n\n```javascript\n{\n  parameterizable: true,\n  parameterId: \"unique_id\",  // Unique across template, lowercase with underscores\n  parameterType: \"text\" | \"imageUrl\" | \"videoUrl\"\n}\n```\n\n#### Naming Conventions\n\n| Element Type | parameterId Examples                        | parameterType |\n| ------------ | ------------------------------------------- | ------------- |\n| Text         | `headline`, `subtitle`, `cta_text`, `price` | `\"text\"`      |\n| Image        | `product_image`, `logo`, `background_image` | `\"imageUrl\"`  |\n| Video        | `hero_video`, `background_video`            | `\"videoUrl\"`  |\n\n#### Best Practices\n\n1. **Use descriptive IDs:** `product_title` not `text1`\n2. **Be consistent:** Use snake_case across all templates\n3. **Unique per template:** No duplicate parameterIds on same page\n4. **Group logically:** Related elements share naming prefix (e.g., `card_title`, `card_image`)\n\n#### Validation Checklist\n\nBefore finalizing any template update:\n- [ ] All text elements have `parameterizable: true` and unique `parameterId`\n- [ ] All image elements have `parameterizable: true` and unique `parameterId`\n- [ ] All video elements have `parameterizable: true` and unique `parameterId`\n- [ ] No duplicate parameterIds exist on the same page\n- [ ] parameterIds are descriptive and follow snake_case convention\n\n### Design Best Practices\n\n#### Typography Guidelines\n- **Font limit:** Use 2-3 fonts maximum per template\n- **Hierarchy:** Headings should be 1.5-2x larger than body text\n- **Minimum size:** 24px for social media readability\n- **Weights:** Headings 600-900 (bold), Body 400-500 (regular)\n\n**Popular font pairings:**\n- `Prata` + `Inter`\n- `Instrument Serif` + `DM Sans`\n- `Playfair Display` + `Lato`\n- `Montserrat` + `Open Sans`\n\n**Platform-specific fonts:**\n- iOS: `SF Pro Display`, `SF Pro Text`\n- Android: `Google Sans`, `Roboto`\n\n#### Color Guidelines\n**Luxury/Gold palette:**\n- Gold: `#D4AF37`\n- Dark gold: `#B8860B`\n- Light gold: `#F5E7A3`\n\n**iOS system colors:**\n- Blue: `#007AFF`\n- Gray: `#8E8E93`\n- Background: `#F5F5F7`\n\n**Professional dark:**\n- Navy: `#0F172A`\n- Slate: `#1E293B`, `#334155`\n- Muted: `#64748B`, `#94A3B8`\n\n**Best practices:**\n- Ensure 4.5:1 minimum contrast for text readability\n- Use gradients sparingly for premium effects\n- Consistent color palette (3-5 colors max)\n\n#### Layout Guidelines\n- **Edge padding:** 40-60px from canvas edges\n- **Element spacing:** 20-40px between elements\n- **Alignment:** Center for formal, left for modern\n- **Visual flow:** Guide eye with size, color, position\n\n**zIndex ordering:**\n- Background images/colors: 1\n- Overlay shapes: 2-3\n- Text elements: 4-6\n- Interactive elements: 7+\n\n#### Common Operations (Design Automation)\n\n**Add text element:**\n```javascript\naddElement(\"text\", {\n  content: \"Hello World\",\n  fontFamily: \"Inter\",\n  fontSize: 48,\n  fontWeight: \"700\",\n  color: \"#FFFFFF\",\n  x: 100,\n  y: 100,\n  width: 400,\n  height: 60,\n});\n```\n\n**Add shape backdrop:**\n```javascript\naddElement(\"rectangle\", {\n  fill: \"rgba(0,0,0,0.5)\",\n  width: 1080,\n  height: 200,\n  x: 0,\n  y: 800,\n  borderRadius: \"0px\",\n});\n```\n\n**Batch update multiple elements:**\n```javascript\nbatchUpdate([\n  {\n    elementId: \"heading\",\n    type: \"ORSHOT_UPDATE_ELEMENT\",\n    updates: { style: { fontSize: 72 } },\n  },\n  {\n    elementId: \"subtitle\",\n    type: \"ORSHOT_UPDATE_ELEMENT\",\n    updates: { style: { color: \"#94A3B8\" } },\n  },\n  { type: \"ORSHOT_UPDATE_CANVAS\", updates: { backgroundColor: \"#0F172A\" } },\n]);\n```\n\n## API Reference\n\n### 1. Render from Studio Template\n\nGenerate images/PDFs/videos from templates designed in Orshot Studio.\n\n**POST** `https://api.orshot.com/v1/studio/render`\n\n```js\nawait fetch(\"https://api.orshot.com/v1/studio/render\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: \"Bearer <ORSHOT_API_KEY>\",\n  },\n  body: JSON.stringify({\n    templateId: 123, // Integer - your studio template ID\n    modifications: {\n      title: \"Hello World\",\n      imageUrl: \"https://example.com/photo.jpg\",\n      canvasBackgroundColor: \"#eff2fa\",\n    },\n    response: {\n      type: \"base64\", // \"base64\" | \"url\" | \"binary\"\n      format: \"png\", // \"png\" | \"webp\" | \"jpg\" | \"pdf\" | \"mp4\" | \"webm\" | \"gif\"\n      scale: 1, // 1 = original size, 2 = double\n      includePages: [1, 3], // optional – only for multi-page templates\n      fileName: \"my-render\", // optional – custom filename (without extension)\n    },\n    pdfOptions: {\n      // optional – only when format is \"pdf\"\n      margin: \"20px\",\n      rangeFrom: 1,\n      rangeTo: 2,\n      colorMode: \"rgb\", // \"rgb\" or \"cmyk\"\n      dpi: 300,\n    },\n  }),\n});\n```\n\n**Response (single page):**\n```json\n{\n  \"data\": {\n    \"content\": \"data:image/png;base64,iVBORw0.....\",\n    \"format\": \"png\",\n    \"type\": \"base64\",\n    \"responseTime\": 325.22\n  }\n}\n```\n\n**Response (multi-page/carousel):**\n```json\n{\n  \"data\": [\n    { \"page\": 1, \"content\": \"https://storage.orshot.com/.../image1.png\" },\n    { \"page\": 2, \"content\": \"https://storage.orshot.com/.../image2.png\" }\n  ],\n  \"format\": \"png\",\n  \"type\": \"url\",\n  \"responseTime\": 3166.01,\n  \"totalPages\": 2,\n  \"renderedPages\": 2\n}\n```\n\n### 2. Render from Utility Template\n\nGenerate renders from Orshot's pre-built utility templates (e.g., website screenshots, tweet images).\n\n**POST** `https://api.orshot.com/v1/generate/{renderType}`\n\n- `renderType`: `images` or `pdfs`\n\n```js\nawait fetch(\"https://api.orshot.com/v1/generate/images\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: \"Bearer <ORSHOT_API_KEY>\",\n  },\n  body: JSON.stringify({\n    templateId: \"website-screenshot\", // String ID for utility templates\n    response: {\n      format: \"png\",\n      type: \"base64\",\n    },\n    modifications: {\n      websiteUrl: \"https://example.com\",\n      fullCapture: false,\n      delay: 500,\n      width: 1200,\n      height: 1000,\n    },\n  }),\n});\n```\n\n### 3. Generate Signed URL\n\nCreate publicly accessible render URLs without exposing your API key.\n\n**POST** `https://api.orshot.com/v1/signed-url/create`\n\n```js\nawait fetch(\"https://api.orshot.com/v1/signed-url/create\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer <ORSHOT_API_KEY>\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    templateId: \"website-screenshot\",\n    expiresAt: 1744550160505, // UNIX timestamp, or null for no expiry\n    renderType: \"images\",\n    modifications: {\n      websiteUrl: \"https://example.com\",\n    },\n  }),\n});\n```\n\n### 4. List Studio Templates\n\n**GET** `https://api.orshot.com/v1/studio/templates/all?page=1&limit=10`\n\nResponse includes `data` array of templates and `pagination` object with `page`, `limit`, `total`, `totalPages`.\n\n### 5. Get Studio Template\n\n**GET** `https://api.orshot.com/v1/studio/templates/:templateId`\n\nReturns the template metadata including available `modifications`.\n\n### 6. Delete Studio Template\n\n**DELETE** `https://api.orshot.com/v1/studio/templates/:templateId`\n\n### 7. Duplicate Studio Template\n\n**POST** `https://api.orshot.com/v1/studio/templates/:templateId/duplicate`\n\n### 8. Get Profile & Workspaces\n\n**GET** `https://api.orshot.com/v1/me`\n\nReturns your profile and the workspaces your API key or OAuth token can access, including plan info and credit usage.\n\n### 9. Brand Assets API\n\nBrand assets are grouped by type — **images**, **colors**, **fonts**, **videos**, **audio** — each with its own routes under `/v1/brand-assets/{type}/…`.\n\n#### Images\n- **Get:** `GET https://api.orshot.com/v1/brand-assets/images/get`\n- **Upload:** `POST https://api.orshot.com/v1/brand-assets/images/add` — `multipart/form-data` with a `file` field\n- **Update tags:** `PATCH https://api.orshot.com/v1/brand-assets/images/update/:id`\n- **Delete:** `DELETE https://api.orshot.com/v1/brand-assets/images/delete/:id`\n\n#### Colors\n- **Get:** `GET https://api.orshot.com/v1/brand-assets/colors/get`\n- **Add:** `POST https://api.orshot.com/v1/brand-assets/colors/add` — body `{ type: \"hex\" | \"gradient\", value, tags? }`\n- **Update tags:** `PATCH https://api.orshot.com/v1/brand-assets/colors/update/:id`\n- **Delete:** `DELETE https://api.orshot.com/v1/brand-assets/colors/delete/:id`\n\n#### Fonts / Videos / Audio\nSame shape — swap the type segment (`fonts`, `videos`, `audio`):\n- **Get:** `GET /v1/brand-assets/{type}/get`\n- **Upload:** `POST /v1/brand-assets/{type}/add` (`multipart/form-data`, `file` field)\n- **Update tags:** `PATCH /v1/brand-assets/{type}/update/:id`\n- **Delete:** `DELETE /v1/brand-assets/{type}/delete/:id`\n\n#### Search\n- **Search assets:** `GET https://api.orshot.com/v1/brand-assets/search`\n\nUse an uploaded asset in a render by passing its hosted URL as an image/video modification value.\n\n### 10. Enterprise API Endpoints\n\nThese endpoints require an Enterprise plan.\n\n#### Create Studio Template\n**POST** `https://api.orshot.com/v1/studio/templates/create`\n\n```js\n{\n  name: \"Product Banner\",        // required, max 255 chars\n  description: \"Banner template\", // optional\n  canvas_width: 1200,             // required, 1-5000\n  canvas_height: 628,             // required, 1-5000\n  pages_data: [...]               // optional - array of page objects with elements\n}\n```\n\n#### Bulk Create Studio Templates\n**POST** `https://api.orshot.com/v1/studio/templates/bulk-create`\nCreate multiple templates at once via CSV or JSON.\n\n#### Update Template\n**PATCH** `https://api.orshot.com/v1/studio/templates/:templateId`\nUpdate template name and description.\n\n#### Update Template Modifications\n**PATCH** `https://api.orshot.com/v1/studio/templates/:templateId/update-modifications`\nUpdate text and image content in template layers.\n\n#### Generate Template Variants\n**POST** `https://api.orshot.com/v1/studio/templates/:templateId/generate-variants`\nGenerate multiple size variants of a template using AI.\n\n### 11. Dynamic URLs\n\nGenerate images directly from URL parameters:\n\n```\nhttps://api.orshot.com/v1/studio/dynamic-url/my-image?title=Hello%20World&title.fontSize=48px&title.color=%23ff0000\n```\nURL-encode special characters (e.g., `#` → `%23`).\n\n### 12. Template Folders & Sharing\n\nOrganize studio templates into folders and share them:\n\n- **List folders:** `GET https://api.orshot.com/v1/studio/folders`\n- **Create folder:** `POST https://api.orshot.com/v1/studio/folders`\n- **Update folder:** `PATCH https://api.orshot.com/v1/studio/folders/:folderId`\n- **Delete folder:** `DELETE https://api.orshot.com/v1/studio/folders/:folderId`\n- **Move template into folder:** `PATCH https://api.orshot.com/v1/studio/templates/:templateId/folder`\n- **Get template sharing:** `GET https://api.orshot.com/v1/studio/templates/:templateId/share`\n- **Update template sharing:** `POST https://api.orshot.com/v1/studio/templates/:templateId/share`\n\n### 13. Workflows API\n\nWorkflows automate multi-step pipelines (trigger → fetch data → render → publish/deliver). Available to first-party clients (like the Orshot MCP server) and Enterprise workspaces.\n\n- **List workflows:** `GET https://api.orshot.com/v1/workflows`\n- **Create workflow:** `POST https://api.orshot.com/v1/workflows`\n- **Get workflow:** `GET https://api.orshot.com/v1/workflows/:id`\n- **Update workflow:** `PATCH https://api.orshot.com/v1/workflows/:id`\n- **Delete workflow:** `DELETE https://api.orshot.com/v1/workflows/:id`\n- **Run workflow:** `POST https://api.orshot.com/v1/workflows/:id/run`\n- **List runs:** `GET https://api.orshot.com/v1/workflows/:id/runs`\n- **Get a run:** `GET https://api.orshot.com/v1/workflows/:id/runs/:runId`\n- **Validate a workflow:** `POST https://api.orshot.com/v1/workflows/validate`\n- **List available nodes:** `GET https://api.orshot.com/v1/workflows/nodes`\n- **Get/update sharing:** `GET` / `POST https://api.orshot.com/v1/workflows/:id/share`\n\n## Render Configuration\n\n### Dynamic Parameters\n\nOverride template styles, content, and behavior at render time using dot notation.\n\n#### Style Parameters\n\nFormat: `parameterId.property`\n\n```json\n{\n  \"modifications\": {\n    \"title\": \"Hello World\",\n    \"title.fontSize\": \"48px\",\n    \"title.color\": \"#ff0000\",\n    \"title.fontFamily\": \"Roboto\",\n    \"title.textAlign\": \"center\",\n    \"logo.borderRadius\": \"50%\",\n    \"logo.objectFit\": \"cover\"\n  }\n}\n```\n\n**Text properties:** `fontSize`, `fontWeight`, `fontStyle`, `fontFamily`, `lineHeight`, `letterSpacing`, `textAlign`, `verticalAlign`, `textDecoration`, `textTransform`, `color`, `backgroundColor`, `backgroundRadius`, `textStrokeWidth`, `textStrokeColor`, `opacity`, `filter`, `dropShadowX/Y/Blur/Color`\n\n**Image properties:** `objectFit`, `objectPosition`, `borderRadius`, `borderWidth`, `borderColor`, `boxShadowX/Y/Blur/Color`, `opacity`, `filter`\n\n**Shape properties:** `fill`, `stroke`, `strokeWidth`, `borderRadius`, `opacity`\n\n**Position/Size (all elements):** `x`, `y`, `width`, `height`\n\nProperty names are **case-insensitive**.\n\n#### Multi-Page Templates\n\nPrefix modifications with page number:\n```json\n{\n  \"modifications\": {\n    \"page1@title\": \"Page 1 Title\",\n    \"page2@title\": \"Page 2 Title\",\n    \"page1@title.fontSize\": \"48px\"\n  }\n}\n```\n\n#### AI Content Generation (.prompt)\n\nGenerate text or images using AI:\n```json\n{\n  \"modifications\": {\n    \"headline.prompt\": \"Write a catchy headline about coffee\",\n    \"background.prompt\": \"A serene mountain landscape at sunset\"\n  }\n}\n```\n- `.prompt` on a text element generates copy; on an image element it generates imagery. The underlying AI models are managed by Orshot and may change over time — don't hardcode a specific model. AI modifications consume AI Credits.\n\n#### Interactive Links (.href)\n\nAdd clickable links in PDF outputs:\n```json\n{\n  \"modifications\": {\n    \"cta_button.href\": \"https://example.com/signup\",\n    \"logo.href\": \"https://company.com\"\n  },\n  \"response\": { \"format\": \"pdf\" }\n}\n```\n\n#### Video Parameters\n\nControl video elements dynamically:\n```json\n{\n  \"modifications\": {\n    \"bgVideo\": \"https://example.com/video.mp4\",\n    \"bgVideo.trimStart\": 5,\n    \"bgVideo.trimEnd\": 15,\n    \"bgVideo.muted\": false,\n    \"bgVideo.loop\": true\n  },\n  \"response\": { \"format\": \"mp4\" }\n}\n```\n\n### Video Render Example\n\nRender templates with video elements as MP4, WebM, or GIF:\n\n```js\nawait fetch(\"https://api.orshot.com/v1/studio/render\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: \"Bearer <ORSHOT_API_KEY>\",\n  },\n  body: JSON.stringify({\n    templateId: 123,\n    modifications: {\n      videoElement: \"https://example.com/custom-video.mp4\",\n      \"videoElement.trimStart\": 0,\n      \"videoElement.trimEnd\": 10,\n      \"videoElement.muted\": false,\n      \"videoElement.loop\": true,\n    },\n    videoOptions: {\n      trimStart: 0,\n      trimEnd: 20,\n      muted: true,\n      loop: true,\n    },\n    response: {\n      type: \"url\",\n      format: \"mp4\",\n    },\n  }),\n});\n```\n\n### Render-level `videoOptions` (top level, sibling of `modifications`)\n\nControls the whole video render, not one element:\n\n```json\n{\n  \"templateId\": 123,\n  \"modifications\": { \"page1@headline\": \"Launch day\" },\n  \"videoOptions\": {\n    \"duration\": 12,\n    \"fps\": 30,\n    \"muted\": false,\n    \"combinePages\": true,\n    \"pageTransition\": \"fade\",\n    \"pageTransitionDuration\": 0.5,\n    \"audioSource\": \"https://example.com/track.mp3\",\n    \"subtitleSource\": \"https://example.com/voiceover.mp3\",\n    \"captionPreset\": \"karaoke\"\n  },\n  \"response\": { \"type\": \"url\", \"format\": \"mp4\" }\n}\n```\n\n- `duration`, `fps` (≤30), `quality`, `muted`, `trimStart`+`trimEnd` (must be paired).\n- `combinePages: true` merges a multi-page template into ONE video, with `pageTransition` (42 types — fades, slides, wipes, zooms, blurs and more) and `pageTransitionDuration` (0–2s, default 0.5) between scenes. A string `audioSource` then lays one continuous soundtrack across the combined video — the recipe for multi-scene videos.\n- `audioSource` swaps an existing URL audio track at render time while keeping its saved trim/fades/volume/offset/loop. Generated narration changes through `tts.parameterId` instead (see Audio below).\n- Captions: `subtitleSource` (a media URL to auto-transcribe, or a `.srt`/`.vtt` used as-is), `captionPreset` (`pill` | `minimal` | `karaoke` | `bold` | `outline` | `neon`), plus fine-grained `subtitleMode` (`phrase`|`word-by-word`), `subtitleWordReveal` (`appear`|`highlight`|`align`), `subtitleSplit` (`word`|`letter`), `subtitleMaxWordsPerLine` or `subtitleMaxCharsPerLine`, and `subtitle*` styling keys. Prefer authored `page.subtitle` with source `\"auto-audio\"` so captions word-sync automatically.\n\n### Async render jobs (long videos — use this from automations)\n\nA synchronous render holds the HTTP connection for the full render time; video routinely outlives client timeouts (Zapier ~30s, many MCP clients 120s). Add `response.mode: \"async\"`:\n\n```\nPOST /v1/studio/render  { ..., \"response\": { \"mode\": \"async\", \"format\": \"mp4\" } }\n  → 202 with a job object in ~100ms\nGET  /v1/studio/render-jobs/:id          → poll the job (queued → processing → succeeded/failed/canceled)\nGET  /v1/studio/render-jobs?status=&limit=  → newest-first list\nPOST /v1/studio/render-jobs/:id/cancel   → cancel while not terminal\n```\n\nThe job's `result` is byte-identical to the sync response. Pass a `webhook_url` to get the settled job POSTed to you instead of polling. Jobs time out at 15 minutes.\n\n### Page animations & ambient motion (video templates)\n\nElements animate via a page-level `transitions` object on the element — no video file needed:\n\n```json\n{\n  \"transitions\": {\n    \"enter\": { \"type\": \"slideInUp\", \"duration\": 0.5, \"easing\": \"ease-out\" },\n    \"showAt\": 0.4,\n    \"ambient\": { \"type\": \"kenBurns\", \"customValues\": { \"zoom\": 1.12, \"panX\": 6 } },\n    \"exit\": { \"type\": \"fadeOut\", \"duration\": 0.3 },\n    \"hideAt\": 3.6\n  }\n}\n```\n\n- Three phases: `enter` → `ambient` (plays the element's whole visible life) → `exit`. `showAt`/`hideAt` are siblings of `enter`/`exit`, never nested inside them (the #1 authoring bug).\n- Ambient types: `kenBurns` (runs once, stretched across visibility — give every full-bleed photo one), plus looping `pulse`, `float`, `drift`, `wiggle`, `shake`, `slowRotate`, `breathe`. `shake`/`slowRotate` need `easing: \"linear\"` or the loop seam shows.\n- All timing is tunable per render via dot-notation modifications: `\"photo.showAt\": 2.1`, `\"photo.enterType\": \"scaleIn\"`, `\"photo.ambientType\": \"kenBurns\"`, etc.\n\n### Audio: soundtracks & AI voiceover (video templates)\n\nEach page carries `audioTracks` (max 10, page-level, mixed into every mp4/webm render):\n\n- **Existing audio**: `{ \"url\": \"https://...\", \"trimStart\": 33.2, \"trimEnd\": 44.1, \"fadeOut\": 1, \"volume\": 1, \"loop\": false, \"offset\": 0 }` — trim to the song's best part; `videoOptions.audioSource` swaps the file at render keeping these settings.\n- **AI voiceover**: `{ \"tts\": { \"text\": \"words to speak\", \"voiceId\": \"...\", \"modelId\": \"flash\", \"parameterId\": \"voiceover\" } }` instead of `url`. Synthesized when the template is saved (metered voice minutes; identical scripts are cached and free on re-render). Narration length drives the page length, and `page.subtitle` with source `\"auto-audio\"` gives word-synced captions with no transcription step.\n- **Generated ambient/SFX**: `{ \"sfx\": { \"prompt\": \"warm modern pulse, instrumental, seamless loop\", \"seconds\": 12 }, \"volume\": 0.12, \"loop\": true }`.\n- `tts.parameterId` makes the SPOKEN TEXT a render parameter: `modifications: { \"voiceover\": \"new words\" }` (multi-page: `\"page1@voiceover\"`) re-voices the video per render; `\"voiceover.voiceId\"` / `\"voiceover.modelId\"` tune the voice.\n- Budget narration at ~2 words/second plus 1 second of headroom for the page duration.\n- Via MCP, call `orshot_get_audio_authoring_options` first — it returns valid voice ids, models and the workspace's voice allowance in one read-only call.\n\n### Smart Resize (one design → any size)\n\nRender a template at a **different canvas size without redesigning it** — the layout is deterministically re-solved to fit (elements re-anchor, backgrounds stretch, groups move together). Set these on the `response` object:\n\n- **`size`** — *replace* the render size. A preset slug (`\"instagram-story\"`, `\"og-image\"`, `\"youtube-thumbnail\"`, …), a `\"WIDTHxHEIGHT\"` string (`\"1080x1920\"`), or use `width` + `height` (10–5000px each).\n- **`extraSizes`** — *add* the same design at extra sizes in one call. An array (`[\"1080x1920\", \"1080x1080\"]`) or a named object (`{ story: \"1080x1920\", square: \"1080x1080\" }`). Each output gains a nested `extraSizes` array of `{ size, width, height, content }`.\n\n```json\n{\n  \"templateId\": 123,\n  \"modifications\": { \"title\": \"Launch Day\" },\n  \"response\": {\n    \"type\": \"url\",\n    \"format\": \"png\",\n    \"size\": \"1200x630\",\n    \"extraSizes\": [\"1080x1920\", \"1080x1080\"]\n  }\n}\n```\n\nImage formats only (`png`/`jpg`/`webp`/`avif`), up to 50 extra outputs per call. Each extra output is billed like a page (1 credit). Saved sizes from the Studio Smart Resize panel reproduce their approved preview exactly.\n\n### Response Types\n\n| Type     | Description                                     |\n| -------- | ----------------------------------------------- |\n| `url`    | Returns a hosted URL to the rendered file       |\n| `base64` | Returns base64-encoded content as a string      |\n| `binary` | Returns binary file content for custom handling |\n\n### Response Formats\n\n| Format | Type  | Notes                                      |\n| ------ | ----- | ------------------------------------------ |\n| `png`  | Image | Best quality, larger size                  |\n| `webp` | Image | Smaller size, good quality                 |\n| `jpg`  | Image | Compressed, no transparency                |\n| `avif` | Image | Smallest size, modern browsers             |\n| `pdf`  | Doc   | Supports multi-page, clickable links, CMYK |\n| `mp4`  | Video | H.264, requires video elements in template |\n| `webm` | Video | VP9, web-optimized                         |\n| `mov`  | Video | QuickTime container                        |\n| `mkv`  | Video | Matroska container                         |\n| `gif`  | Video | Animated, no audio support                 |\n\n### Render Usage (credits)\n\nUsage is measured in **credits**. 1 credit = 1 image, 1 PDF page, or 1 second of video.\n\n| Output               | Cost                     |\n| -------------------- | ------------------------ |\n| Image (PNG/JPG/WebP/AVIF) | 1 credit per image  |\n| PDF                  | 1 credit per page        |\n| Video (MP4/WebM/MOV/MKV/GIF) | 1 credit per second |\n\nMulti-page templates and Smart Resize extra sizes: each output page/size counts as its own credit. AI modifications (`.prompt`) additionally consume AI Credits.\n\n## Common Recipes\n\nEnd-to-end patterns for the jobs people most often automate with Orshot.\n\n### OG image on every deploy\nRender a studio template to a stable hosted URL and drop it into your `<meta>` tags.\n```js\nconst { data } = await fetch(\"https://api.orshot.com/v1/studio/render\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\", Authorization: \"Bearer <KEY>\" },\n  body: JSON.stringify({\n    templateId: 123,\n    modifications: { title: post.title, author: post.author },\n    response: { type: \"url\", format: \"png\", size: \"og-image\" },\n  }),\n}).then((r) => r.json());\n// <meta property=\"og:image\" content={data.content} />\n```\n\n### Bulk-generate from a CSV (certificates, badges, invoices)\nLoop rows and render one PDF each. Describe image layers with `.alt` for accessible PDFs.\n```js\nfor (const row of rows) {\n  await fetch(\"https://api.orshot.com/v1/studio/render\", {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/json\", Authorization: \"Bearer <KEY>\" },\n    body: JSON.stringify({\n      templateId: 456,\n      modifications: { name: row.name, course: row.course, date: row.date },\n      response: { type: \"url\", format: \"pdf\" },\n      pdfOptions: { title: `${row.name} — Certificate` },\n    }),\n  });\n}\n```\n\n### Render and auto-post to social in one call\nAdd a `publish` object (see Social Publishing) — no second request.\n```js\nbody: JSON.stringify({\n  templateId: 123,\n  modifications: { title: \"Launch day!\" },\n  response: { type: \"url\", format: \"png\" },\n  publish: { accounts: [1, 2], content: \"We just shipped 🚀\" },\n});\n```\n\n### One design, every social size (Smart Resize)\n```js\nresponse: { type: \"url\", format: \"png\", extraSizes: [\"1080x1920\", \"1080x1080\", \"1200x630\"] }\n// each output gains a nested extraSizes[] of { size, width, height, content }\n```\n\n### Automate a recurring pipeline (Workflows)\nCreate a workflow (trigger → fetch data → render → publish/deliver) with `POST /v1/workflows`, then trigger it with `POST /v1/workflows/:id/run`. Inspect results via `GET /v1/workflows/:id/runs`. Best driven through the MCP server or the Workflows API on Enterprise/first-party clients.\n\nSee **Setting Up Recurring Automation** below for the full playbook, including how\nto wire it into an n8n/Make/Zapier setup the user already runs.\n\n## Setting Up Recurring Automation\n\nA one-off render is a demo. The value shows up when it runs unattended. Pick the\nroad that matches where the user ALREADY works, not the one easiest to describe.\n\n### Choose the road first\n\nCheck what they already run before recommending anything. `GET /v1/workspace/logs?limit=100`\nreturns a `source` on every row, which answers it factually:\n\n| `source` in their logs | They already run | Lead with |\n| --- | --- | --- |\n| `n8n-integration` | n8n | Add a node to the n8n workflow they have |\n| `orshot-make` | Make | Add a module to their scenario |\n| `zapier-integration` | Zapier | Add an action to their Zap |\n| `orshot-pipedream` | Pipedream | Add a step |\n| `orshot-*-sdk`, `api`, `cli` | Their own code | Write the call into their repo |\n| only `playground` / `orshot-mcp-server` | Nothing yet | Offer Orshot Workflows |\n\nNever recommend a second orchestrator to someone who already has one. A user with a\nlive n8n setup wants this template added to it, not a new tool to learn.\n\nIf you have browser control or repo access, offer to DO the setup rather than\ndescribe it. Whichever road you take, **run it once and show the resulting image\nURL** before calling it done.\n\n### Road A — Orshot Workflows (no orchestrator yet)\n\nOrshot runs the whole loop: trigger, render, deliver. Over MCP:\n\n1. `orshot_suggest_workflows` with the `templateId` — returns automations that\n   fit this template, with draft-ready steps\n2. `orshot_list_workflow_nodes` — use the EXACT node keys it returns. Invented\n   keys (e.g. `render_studio_template`, `slack_send`) fail as `unknown node`;\n   the real keys are `render`, `slack`, and so on\n3. `orshot_list_connected_integrations` — see what is already connected\n4. `orshot_get_workflow_connection_data` — resolve \"my content sheet\" into a\n   concrete id, and confirm the name back to the user\n5. `orshot_validate_workflow` — read `errors`, `warnings` AND `gated`. A `gated`\n   entry means the step is not included in the workspace's current plan, not a\n   config problem: the draft still saves, only activation is blocked, so tell\n   the user that instead of editing steps\n6. `orshot_create_workflow` with `status: \"draft\"`, then share the edit link from\n   the result — it opens pre-configured in their dashboard\n7. `orshot_run_workflow` — prove it works, then they activate\n\n**Scaffolding shapes.** If they have no data source ready, use\n`webhook` → `webhook_body` → `render` → `orshot_url`. It is the only\ntrigger→source pair needing no OAuth AND it validates clean with empty configs,\nso it is runnable immediately and they can POST to it from cron, their app, n8n,\nor Make. A bare `schedule` → `render` is NOT runnable — it fails with \"no data\nsource\"; a schedule needs a source step after it.\n\n### Road B — their existing automation platform\n\nEvery platform needs the same three things: endpoint, auth header, modification\nkeys. Get the exact keys first — never guess them:\n\n```\norshot_get_studio_template_modifications   (or GET /v1/studio/templates/:id/modifications)\n```\n\nThen the call to drop in:\n\n```http\nPOST https://api.orshot.com/v1/studio/render\nAuthorization: Bearer <ORSHOT_API_KEY>\nContent-Type: application/json\n\n{\n  \"templateId\": 1234,\n  \"modifications\": { \"headline\": \"{{ row.title }}\", \"hero_image\": \"{{ row.image_url }}\" },\n  \"response\": { \"type\": \"url\", \"format\": \"png\" }\n}\n```\n\nThe response contains the rendered URL — map it to whatever the next step needs.\n\n- **n8n** — HTTP Request node, POST, header auth, body as above. A dedicated\n  Orshot node also exists in the n8n library.\n- **Make** — HTTP \"Make a request\" module, or the Orshot app modules.\n- **Zapier** — Webhooks by Zapier → POST, or the Orshot Zapier app.\n- **Pipedream** — HTTP step; Orshot components are published.\n- **Own code / cron** — any HTTP client. Use `response.type: \"url\"` in production\n  so you are not moving base64 around, and keep the API key in an environment\n  variable, never inline.\n\nRecurring triggers that work well: a new spreadsheet/Airtable/Notion row, a\nschedule, an inbound webhook, or a CMS publish event.\n\n### Verify before you call it done\n\n1. Trigger one real run\n2. `orshot_list_workspace_logs` (or `GET /v1/workspace/logs`) — the new render\n   appears with the `source` of whatever fired it, proving which system made it\n3. Show the user the image URL from that run\n\nIf the render 403s, read the body: an expired key, an inactive subscription, and\na plan limit all return 403 with different messages. A plan block is not an auth\nproblem — do not retry it as one.\n\n## Integrations & Helps\n\n### Integrations Service Support\nOrshot connects with:\n- **No-code:** Zapier, Make (Integromat), n8n, Pipedream, Airtable\n- **Storage:** Amazon S3, Cloudflare R2, Google Drive, Dropbox\n- **Notifications:** Slack, Webhooks\n- **Design:** Figma Plugin, Canva Import, Polotno Import\n- **CLI:** `npx orshot-cli` for terminal-based generation\n- **MCP Server:** Use with Claude, Cursor, Windsurf via MCP protocol\n- **Embed:** White-label design editor for your app (React SDK, Vue SDK, iframe)\n\n### Common Error Codes\n\n| Code | Error                          | Fix                                          |\n| ---- | ------------------------------ | -------------------------------------------- |\n| 400  | `templateId missing`           | Add `templateId` to request body             |\n| 400  | `Invalid API Key`              | Generate new key from dashboard              |\n| 403  | `Authorization header missing` | Add `Authorization: Bearer <KEY>` header     |\n| 403  | `Subscription inactive`        | The workspace subscription is not active; check it in the dashboard |\n| 403  | `Template not found`           | Verify template ID belongs to your workspace |\n| 403  | `ai-video-not-on-plan`         | AI video is not included in the workspace's current plan; use stock footage or a Brand Library video instead |\n\n### General Best Practices\n\n1. **Use `url` response type** for production – avoids large base64 payloads\n2. **Use `webp` format** for smaller file sizes with good quality\n3. **Test in Playground** before coding – each template has an interactive playground\n4. **Use style parameters** instead of creating multiple template variants\n5. **Use consistent units** – stick to `px` for sizes\n6. **Multi-page prefix** – always use `page1@paramId` format for carousel templates\n7. **Cache renders** – use `?cache=false` query param to bypass cache when needed\n8. **Handle errors** – check for 403/400 status codes and parse error messages\n\n## Social Publishing\n\nPublish rendered images/videos directly to 13+ social platforms via API. Supports Twitter/X, Instagram, LinkedIn, Pinterest, Facebook, TikTok, YouTube, Threads, Bluesky, Reddit, Telegram, Snapchat, and Google Business.\n\nFor detailed setup: fetch `https://orshot.com/docs/publish/introduction.md`\n\n### Connect Accounts\n\nConnect social accounts in **Workspace Settings → Social Accounts** in the Orshot dashboard. Each connected account gets a numeric ID used in API calls.\n\n### Publish from API\n\nAdd the `publish` object to any render request:\n\n```js\nawait fetch(\"https://api.orshot.com/v1/studio/render\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: \"Bearer <ORSHOT_API_KEY>\",\n  },\n  body: JSON.stringify({\n    templateId: 123,\n    modifications: { title: \"New Post\" },\n    response: { type: \"url\", format: \"png\" },\n    publish: {\n      accounts: [1, 3],          // Social account IDs\n      content: \"Check this out!\", // Caption text (max 5000 chars)\n      isDraft: false,             // true = save as draft\n      schedule: {                 // Optional: schedule for later\n        scheduledFor: \"2026-04-25T14:00:00Z\"\n      },\n      timezone: \"America/New_York\",\n      platformOptions: {          // Per-account overrides (keyed by account ID)\n        \"1\": { firstComment: \"https://example.com\" },     // LinkedIn\n        \"3\": { title: \"Pin Title\", link: \"https://...\" }   // Pinterest\n      }\n    }\n  }),\n});\n```\n\n**Response includes publish status:**\n```json\n{\n  \"data\": { \"content\": \"https://storage.orshot.com/...\" },\n  \"publish\": [\n    { \"platform\": \"twitter\", \"username\": \"acme\", \"status\": \"published\", \"url\": \"https://x.com/...\" },\n    { \"platform\": \"linkedin\", \"username\": \"acme\", \"status\": \"scheduled\" }\n  ]\n}\n```\n\n**Format compatibility:**\n- PDF cannot be published to any platform\n- Video (mp4/webm/gif) cannot be published to Google Business\n- Image (png/jpg/webp) cannot be published to TikTok or YouTube (video-only)\n\n## Developer Apps & OAuth\n\nBuild third-party integrations with Orshot using OAuth 2.0. Register an app, authenticate users, and access their workspaces on their behalf.\n\nFor detailed setup: fetch `https://orshot.com/docs/developers/oauth-overview.md`\n\n### Register an App\n\n1. Go to **Workspace Settings → Developer Apps** in the Orshot dashboard\n2. Create a new app with a name, redirect URI, and the scopes it needs\n3. Use the app's `client_id` in the flows below. Orshot OAuth clients are public clients: PKCE (`S256`) is required and no client secret is used.\n\nDiscovery: `https://api.orshot.com/.well-known/oauth-authorization-server` (dynamic client registration is available at `registration_endpoint`).\n\n### OAuth 2.0 Flows\n\n**Authorization Code + PKCE** (web and desktop apps):\n```\nGET https://orshot.com/oauth/authorize?\n  response_type=code&\n  client_id=YOUR_CLIENT_ID&\n  redirect_uri=https://yourapp.com/callback&\n  code_challenge=BASE64URL_SHA256_OF_VERIFIER&\n  code_challenge_method=S256&\n  scope=workspace:templates:read workspace:templates:write render:generate\n```\n\n**Device Flow** (CLI/headless):\n```\nPOST https://api.orshot.com/v1/oauth/device/code\n  client_id=YOUR_CLIENT_ID&\n  scope=workspace:templates:read render:generate\n```\n\n### Token Exchange\n\n```\nPOST https://api.orshot.com/v1/oauth/token\n  grant_type=authorization_code&\n  code=AUTH_CODE&\n  client_id=YOUR_CLIENT_ID&\n  code_verifier=THE_PKCE_VERIFIER&\n  redirect_uri=https://yourapp.com/callback\n```\n\nRefresh with `grant_type=refresh_token` when the `offline_access` scope was granted. Store tokens server-side or in the user's own environment; never place them in prompts, logs, or shared files.\n\n### Available Scopes\n\n| Scope | Description |\n|-------|-------------|\n| `workspace:read` | List and read workspace details |\n| `workspace:templates:read` | List and read templates |\n| `workspace:templates:write` | Update template modifications via API |\n| `render:generate` | Generate images, PDFs, and videos |\n| `mcp:access` | Access via Model Context Protocol |\n| `offline_access` | Long-lived refresh tokens |\n\nFor endpoint details: fetch `https://orshot.com/docs/developers/oauth-endpoints.md`\n\n## White-Label Embed (Orshot Embed)\n\nEmbed Orshot's template editor into your application as a white-label component. Users can design and customize templates directly in your app.\n\nFor detailed setup: fetch `https://orshot.com/docs/orshot-embed/introduction.md`\n\n### Quick Setup (iframe)\n\n```html\n<iframe\n  src=\"https://orshot.com/embeds/YOUR_EMBED_ID?userId=USER_123\"\n  width=\"100%\"\n  height=\"600\"\n  frameborder=\"0\"\n></iframe>\n```\n\n### React SDK\n\n```bash\nnpm install @orshot/react\n```\n\n```jsx\nimport { OrshotEmbed } from \"@orshot/react\";\n\n<OrshotEmbed\n  embedId=\"YOUR_EMBED_ID\"\n  userId=\"user_123\"\n  token=\"JWT_TOKEN\"\n  onRender={(data) => console.log(\"Rendered:\", data)}\n  onSave={(data) => console.log(\"Saved:\", data)}\n/>\n```\n\n### Vue SDK\n\n```bash\nnpm install @orshot/vue\n```\n\n```vue\n<template>\n  <OrshotEmbed\n    embed-id=\"YOUR_EMBED_ID\"\n    user-id=\"user_123\"\n    @render=\"onRender\"\n    @save=\"onSave\"\n  />\n</template>\n```\n\n### Key Features\n\n- **Per-user templates**: Pass `userId` to give each user their own template copies\n- **JWT authentication**: Secure embed access with domain whitelist validation\n- **Webhooks**: Get notified on render completion, template save, etc.\n- **Custom buttons**: Add your own action buttons to the editor toolbar\n- **PostMessage API**: Control the embed programmatically from your app\n\nFor React SDK details: fetch `https://orshot.com/docs/orshot-embed/react-sdk.md`\nFor Vue SDK details: fetch `https://orshot.com/docs/orshot-embed/vue-sdk.md`\nFor webhook setup: fetch `https://orshot.com/docs/orshot-embed/webhooks.md`\n\n## Migrating from Another Template-Render API\n\nThe API-call swap is the easy part. The real work is recreating the design, because another platform's template JSON cannot be imported:\n\n1. **Recreate the template in Orshot Studio** (or via `POST /v1/studio/templates/create`). Rebuild the layout, then mark every dynamic layer parameterizable with a clear `parameterId`.\n2. **Map old field names → Orshot `parameterId`s.** Other APIs key by layer name or by fixed keys such as `image_url`; Orshot keys by `parameterId`. Keep a lookup from old names to new ones.\n3. **Swap the API call**: `Authorization: Bearer <ORSHOT_API_KEY>` + `POST /v1/studio/render` with `modifications` as a flat object (page-prefixed keys for multi-page templates).\n4. **Handle sync vs async.** Many render APIs are submit-then-poll; Orshot image and PDF renders return synchronously, so delete the polling/webhook code and read `data.content` from the response. Long video renders use async render jobs (see above).\n5. **Style overrides** that other APIs put in per-layer objects become dot-notation keys here (`\"title.fontSize\": \"48px\"`).\n6. **Verify the response shape** — single page is an object, multi-page is an array (Common Gotchas #7) — before cutting traffic over.\n\nRender URLs on Orshot persist; do not add expiry handling unless you delete renders yourself.\n\n### Links\n\n- Documentation: https://orshot.com/docs\n- API Reference: https://orshot.com/docs/api-reference\n- Integrations: https://orshot.com/integrations\n- MCP Server: https://orshot.com/docs/integrations/mcp-server\n- Templates: https://orshot.com/templates\n- Developer Apps: https://orshot.com/docs/developers\n- Social Publishing: https://orshot.com/docs/publish/introduction\n- Orshot Embed: https://orshot.com/docs/orshot-embed/introduction\n"
AFTER
"---\nname: orshot\ndescription: \"Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch, async), dynamic image URLs, utility templates (tweet and website screenshots, charts, mockups), posting a render to connected social accounts, wiring it into n8n, Make, Zapier or cron, and the gotchas that break renders (parameterId keys, page1@ prefixes, public image URLs). Use when: \\\"render my Orshot template\\\", \\\"connect Orshot to Claude\\\", \\\"call the Orshot API\\\", \\\"my modification is ignored\\\", \\\"automate this render in n8n\\\", \\\"screenshot this tweet\\\". One job end to end: load its skill (orshot-blog-images, orshot-pdf-generator, orshot-video-generator, orshot-ad-creative, orshot-carousel, orshot-youtube-thumbnail, orshot-social-media-content or orshot-social-media-scheduler). NOT for template design craft (use orshot-design).\"\nlicense: MIT\nmetadata:\n  author: Orshot\n  version: \"1.6.3\"\n---\n\n# Orshot: generate images, PDFs and videos from templates through the API\n\nOrshot is an API for automated image, PDF and video generation from templates. A template is designed once (in the Orshot studio, imported, or built by an agent), then rendered with new data on every call. The output can be posted to connected social accounts in the same request.\n\nThis skill is the flow: which tool or endpoint to call, in what order, and what to check. Parameters, allowed values, limits and response fields live in the docs. Every docs page is readable as markdown at `https://orshot.com/docs/<path>.md`; the docs map at the end lists the pages for each job.\n\n- MCP server: `https://mcp.orshot.com/mcp`\n- REST base: `https://api.orshot.com/v1`\n\nThree things hold in every flow below:\n\n- No API key over the hosted MCP server. It signs in with OAuth. A key is only for cron jobs, servers and n8n.\n- The credit cost comes first. Say it before a bulk render.\n- Nothing is posted, run or deleted without the user's yes.\n\n## One job end to end? Load its skill\n\nThis skill is Orshot itself: connecting, rendering and the gotchas. A whole job has its own skill with the flow and worked examples:\n\n| The job | Skill |\n| --- | --- |\n| Featured images and OG images for blog posts, in-article images | `orshot-blog-images` |\n| Invoices, certificates, reports and other PDFs from rows of data | `orshot-pdf-generator` |\n| Reels, Shorts, product and listing videos, animated charts and brand films | `orshot-video-generator` |\n| Ads in every Meta, Google and LinkedIn size, A/B variants | `orshot-ad-creative` |\n| TikTok, Instagram and LinkedIn carousels | `orshot-carousel` |\n| YouTube thumbnails | `orshot-youtube-thumbnail` |\n| On-brand social posts and stories | `orshot-social-media-content` |\n| Scheduling, posting and analytics across platforms | `orshot-social-media-scheduler` |\n| Designing or refining a template | `orshot-design` |\n\n## Connect first\n\nThe hosted MCP server is the fastest route and the only one that returns inline previews and design checks. Added from an app directory (Claude, ChatGPT), the server is already connected: go straight to the account check below.\n\n\nConfirm with `orshot_get_account`. It returns the plan, credits and which features the plan includes (video, workflows, embed). If it reports an inactive subscription or a used-up limit, that is a billing state, not an auth error. Send the user to https://orshot.com/pricing and stop retrying.\n\n**Headless fallback: REST with an API key.** Use it only when the agent cannot run MCP (a cron job, a server script, n8n). Read the key from the environment.\n\n```bash\ncurl -s https://api.orshot.com/v1/me -H \"Authorization: Bearer $ORSHOT_API_KEY\"\n```\n\nDo not go on until one of the two works.\n\n## Core workflow\n\n1. **Connect** (above).\n2. **Find the template.** `orshot_list_studio_templates` (search by name or tags) or `GET /v1/studio/templates/all`. No template yet? Browse `orshot_list_library_templates`, or design one with the `orshot-design` skill.\n3. **Read its keys.** `orshot_get_studio_template_modifications` or `GET /v1/studio/templates/:id`. Never guess a key.\n4. **Render.** `orshot_render_template` or `POST /v1/studio/render`.\n5. **Deliver or post.** Show the output URL. To post it, pass the URL to `orshot_publish_social_post` (over REST, `publish` on the render also works; see Social posting).\n6. **Automate.** Put the call into what the user already runs. Playbook: `references/recurring-automation.md`.\n7. **Verify.** `orshot_list_workspace_logs` or `GET /v1/workspace/logs` shows the render and the `source` that made it. Show the output before calling it done.\n\n## Safety rules\n\n- **Confirm before anything leaves the workspace.** Publishing a post, running a workflow (it spends credits and can deliver or post) and deleting a template, workflow or brand asset need an explicit yes. Say what will go where, and when.\n- **Default social posts to drafts.** \"Prepare\", \"draft\" and \"let me look first\" mean a draft. A draft reaches nobody until it is published.\n- **Show the cost before bulk work.** Renders spend credits and AI modifications spend AI credits ([credits-usage](https://orshot.com/docs/definitions/credits-usage.md)). Say so before a loop of hundreds of renders.\n- **Never paste the API key into chat, files or URLs.** Prefer MCP, which has no key.\n- **Do not delete anything to \"clean up\" on your own.**\n\n## Gotchas (read before rendering)\n\n1. **Modifications key by `parameterId`, not layer name.** If a value is ignored, the key is wrong. Fetch the keys instead of guessing.\n2. **Multi-page templates need a `pageN@` prefix on every content key** (`\"page1@title\"`, `\"page2@title\"`). A bare content key reaches no page; the response carries a warning. A bare dotted style key (`\"title.fontSize\"`) applies to every page that has that layer. [multi-page-images](https://orshot.com/docs/image-generation/multi-page-images.md)\n3. **Studio template ids are numbers; library template ids are slugs** (`\"website-screenshot\"`). [template-id](https://orshot.com/docs/definitions/template-id.md)\n4. **MCP and REST shape the render call differently.** Over MCP, output options are flat arguments of `orshot_render_template` (`format`, `scale`, `includePages`, `size`, `extraSizes`, `background`, `videoOptions`). Over REST they sit inside the `response` object. Posting over MCP is its own tool, `orshot_publish_social_post`.\n5. **Long renders must not block.** Over MCP pass `background: true`, then collect with `orshot_get_render_job`. Over REST add `mode: \"async\"` to `response`, then poll or use a webhook ([async-render-start](https://orshot.com/docs/api-reference/async-render-start.md)). Any template can render as video; the plan must include video.\n6. **Image and video URLs in modifications must be public.** The renderer fetches them server side; localhost, expired signed URLs and login-gated URLs fail.\n7. **The response shape depends on the page count.** Read the Response section of [render-from-studio-template](https://orshot.com/docs/api-reference/render-from-studio-template.md) before writing code that reads it.\n8. **On `429`, back off** for the `Retry-After` seconds.\n9. **Publish by account id, never by platform name.** Two accounts on one platform have different ids. Media rules differ per platform: check [social-posting-guidelines](https://orshot.com/docs/publish/social-posting-guidelines.md) before posting. A PDF render posts only to LinkedIn.\n\n## Worked example: one design, several sizes\n\nPrompt: \"Render the launch template as an OG image and an Instagram story, title 'We just shipped'.\"\n\n1. `orshot_list_studio_templates` with `search: \"launch\"`. One match: id `123`.\n2. `orshot_get_studio_template_modifications` with `templateId: 123`. Keys: `title`, `hero_image`.\n3. `orshot_render_template`:\n\n```json\n{\n  \"templateId\": 123,\n  \"modifications\": { \"title\": \"We just shipped\" },\n  \"format\": \"png\",\n  \"size\": \"og-image\",\n  \"extraSizes\": [\"instagram-story\"]\n}\n```\n\n4. Check: the result lists every output URL and each extra size. A per-size `error` means that one size failed; the others are fine. Show the user every link.\n\nThe same render over REST:\n\n```js\nawait fetch(\"https://api.orshot.com/v1/studio/render\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${process.env.ORSHOT_API_KEY}` },\n  body: JSON.stringify({\n    templateId: 123,\n    modifications: { title: \"We just shipped\" },\n    response: { type: \"url\", format: \"png\", size: \"og-image\", extraSizes: [\"instagram-story\"] },\n  }),\n});\n```\n\nPreset names and size rules: [render-from-studio-template, Smart Resize](https://orshot.com/docs/api-reference/render-from-studio-template.md).\n\n## Worked example: render and hold as a draft post\n\nPrompt: \"Render this week's quote card and put it on LinkedIn as a draft.\"\n\n1. `orshot_list_social_accounts`. Pick the LinkedIn account id, say `3`. An empty list means no account is connected yet; a person connects one in the dashboard ([connect-account](https://orshot.com/docs/publish/connect-account.md)), the API cannot. A draft or scheduled post can still be saved with `platforms` instead of `accounts`; once an account on that platform is connected, add it with `orshot_update_social_post` and send the post. Publishing now needs an account.\n2. `orshot_render_template` with the modifications. It returns the image URL.\n3. `orshot_publish_social_post` with `mediaUrls: [that URL]`, `accounts: [3]`, `content` and `status: \"draft\"`. Check the result per account.\n4. After the user approves: `orshot_publish_social_draft`. To change it first: `orshot_update_social_post`.\n\nOver REST, the render `publish` object reads `isDraft` and `schedule.scheduledFor` for timing, not `status` ([publish-from-api](https://orshot.com/docs/publish/publish-from-api.md)). To post a file that already exists, use `orshot_publish_social_post` or `POST /v1/social/publish`. Scheduling, analytics and best time to post: the `orshot-social-media-scheduler` skill.\n\n## Other jobs\n\n- **Utility templates** (tweet and website screenshots, bar and comparison charts, app store screenshots, phone mockups, background removal): `orshot_render_library_template` or `POST /v1/generate/:renderType`.\n- **Many variants of one template** (a list of names, a set of products): `orshot_batch_render_template`, or loop the REST render. A full invoice or certificate run: `orshot-pdf-generator`.\n- **Stable image URLs** for OG tags, emails and embeds: `orshot_create_dynamic_url`. OG images for a whole blog or site: `orshot-blog-images`.\n- **Brand assets**: `orshot_get_brand_kit`, `orshot_import_brand_website`.\n- **Folders and workspaces**: `orshot_list_template_folders`, `orshot_set_active_workspace`.\n- **Recurring jobs** (n8n, Make, Zapier, Pipedream, cron, Orshot Workflows): `references/recurring-automation.md`.\n\n## Docs map\n\n| Job | Docs page |\n| --- | --- |\n| Render a studio template: body, response object, Smart Resize, PDF, video, publish | https://orshot.com/docs/api-reference/render-from-studio-template.md |\n| Render a library (utility) template | https://orshot.com/docs/api-reference/render-from-template.md |\n| What modification values can be | https://orshot.com/docs/definitions/modifications.md |\n| Style overrides with dot keys | https://orshot.com/docs/orshot-studio/style-parameters.md |\n| AI-generated values (`.prompt`) | https://orshot.com/docs/dynamic-parameters/prompt.md |\n| Output formats and response types | https://orshot.com/docs/definitions/response-format.md, https://orshot.com/docs/definitions/response-type.md |\n| Long renders as jobs | https://orshot.com/docs/api-reference/async-render-start.md |\n| Video options, audio, captions | https://orshot.com/docs/video-generation/video-options.md, https://orshot.com/docs/video-generation/adding-subtitles.md |\n| PDF options | https://orshot.com/docs/pdf-generation/pdf-options.md |\n| Plan a design: the formats brands run for the job, palette, type | `orshot_plan_design` (brief, `job`, size), then `orshot_get_design_spec` for the craft, the packs, the film schema, the layout grammar and the format references |\n| Template JSON (pages, elements, styles, animation) | `orshot_get_design_spec` topic `template-json`, https://orshot.com/docs/definitions/template-json.md |\n| Create or update templates over REST (Enterprise) | https://orshot.com/docs/api-reference/enterprise-template-create.md |\n| List and read templates | https://orshot.com/docs/api-reference/studio-templates-list.md, https://orshot.com/docs/api-reference/studio-template-get.md |\n| Dynamic URLs | https://orshot.com/docs/integrations/dynamic-urls.md |\n| Signed render URLs (REST) | https://orshot.com/docs/api-reference/generate-signed-url.md |\n| Social posting | https://orshot.com/docs/publish/publish-from-api.md, https://orshot.com/docs/api-reference/social-publish.md |\n| Social add-on pricing | https://orshot.com/docs/publish/pricing.md |\n| Credits | https://orshot.com/docs/definitions/credits-usage.md |\n| Errors | https://orshot.com/docs/error-reference.md |\n| Workspace logs and `source` values | https://orshot.com/docs/api-reference/workspace-logs.md |\n| Workflows over REST | https://orshot.com/docs/api-reference/workflow-create.md |\n| n8n, Make, Zapier, Pipedream and other integrations | https://orshot.com/docs/integrations/index.md |\n| SDKs (Node, Python, PHP, Ruby) | https://orshot.com/docs/sdks/index.md |\n| Importing templates | https://orshot.com/docs/importing-templates/index.md |\n| White-label editor embed | https://orshot.com/docs/orshot-embed/introduction.md |\n| Third-party apps with OAuth | https://orshot.com/docs/developers/oauth-overview.md |\n| MCP setup and tool list | https://orshot.com/docs/integrations/mcp-server.md |\n\nThe MCP server also serves `orshot://formats` (output formats and size presets).\n"
Full snapshot data
{
  "description": "Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch, async), dynamic image URLs, utility templates (tweet and website screenshots, charts, mockups), posting a render to connected social accounts, wiring it into n8n, Make, Zapier or cron, and the gotchas that break renders (parameterId keys, page1@ prefixes, public image URLs). Use when: \"render my Orshot template\", \"connect Orshot to Claude\", \"call the Orshot API\", \"my modification is ignored\", \"automate this render in n8n\", \"screenshot this tweet\". One job end to end: load its skill (orshot-blog-images, orshot-pdf-generator, orshot-video-generator, orshot-ad-creative, orshot-carousel, orshot-youtube-thumbnail, orshot-social-media-content or orshot-social-media-scheduler). NOT for template design craft (use orshot-design).",
  "included_files": [
    {
      "relative_path": "references/recurring-automation.md",
      "size_in_bytes": 5563
    }
  ],
  "name": "orshot",
  "skill_md_contents": "---\nname: orshot\ndescription: \"Image, PDF and video generation from Orshot templates, through the hosted MCP server (OAuth, no API key) or the REST API. Covers connecting, finding a template and its parameter keys, render options (formats, sizes, Smart Resize, batch, async), dynamic image URLs, utility templates (tweet and website screenshots, charts, mockups), posting a render to connected social accounts, wiring it into n8n, Make, Zapier or cron, and the gotchas that break renders (parameterId keys, page1@ prefixes, public image URLs). Use when: \\\"render my Orshot template\\\", \\\"connect Orshot to Claude\\\", \\\"call the Orshot API\\\", \\\"my modification is ignored\\\", \\\"automate this render in n8n\\\", \\\"screenshot this tweet\\\". One job end to end: load its skill (orshot-blog-images, orshot-pdf-generator, orshot-video-generator, orshot-ad-creative, orshot-carousel, orshot-youtube-thumbnail, orshot-social-media-content or orshot-social-media-scheduler). NOT for template design craft (use orshot-design).\"\nlicense: MIT\nmetadata:\n  author: Orshot\n  version: \"1.6.3\"\n---\n\n# Orshot: generate images, PDFs and videos from templates through the API\n\nOrshot is an API for automated image, PDF and video generation from templates. A template is designed once (in the Orshot studio, imported, or built by an agent), then rendered with new data on every call. The output can be posted to connected social accounts in the same request.\n\nThis skill is the flow: which tool or endpoint to call, in what order, and what to check. Parameters, allowed values, limits and response fields live in the docs. Every docs page is readable as markdown at `https://orshot.com/docs/<path>.md`; the docs map at the end lists the pages for each job.\n\n- MCP server: `https://mcp.orshot.com/mcp`\n- REST base: `https://api.orshot.com/v1`\n\nThree things hold in every flow below:\n\n- No API key over the hosted MCP server. It signs in with OAuth. A key is only for cron jobs, servers and n8n.\n- The credit cost comes first. Say it before a bulk render.\n- Nothing is posted, run or deleted without the user's yes.\n\n## One job end to end? Load its skill\n\nThis skill is Orshot itself: connecting, rendering and the gotchas. A whole job has its own skill with the flow and worked examples:\n\n| The job | Skill |\n| --- | --- |\n| Featured images and OG images for blog posts, in-article images | `orshot-blog-images` |\n| Invoices, certificates, reports and other PDFs from rows of data | `orshot-pdf-generator` |\n| Reels, Shorts, product and listing videos, animated charts and brand films | `orshot-video-generator` |\n| Ads in every Meta, Google and LinkedIn size, A/B variants | `orshot-ad-creative` |\n| TikTok, Instagram and LinkedIn carousels | `orshot-carousel` |\n| YouTube thumbnails | `orshot-youtube-thumbnail` |\n| On-brand social posts and stories | `orshot-social-media-content` |\n| Scheduling, posting and analytics across platforms | `orshot-social-media-scheduler` |\n| Designing or refining a template | `orshot-design` |\n\n## Connect first\n\nThe hosted MCP server is the fastest route and the only one that returns inline previews and design checks. Added from an app directory (Claude, ChatGPT), the server is already connected: go straight to the account check below.\n\n\nConfirm with `orshot_get_account`. It returns the plan, credits and which features the plan includes (video, workflows, embed). If it reports an inactive subscription or a used-up limit, that is a billing state, not an auth error. Send the user to https://orshot.com/pricing and stop retrying.\n\n**Headless fallback: REST with an API key.** Use it only when the agent cannot run MCP (a cron job, a server script, n8n). Read the key from the environment.\n\n```bash\ncurl -s https://api.orshot.com/v1/me -H \"Authorization: Bearer $ORSHOT_API_KEY\"\n```\n\nDo not go on until one of the two works.\n\n## Core workflow\n\n1. **Connect** (above).\n2. **Find the template.** `orshot_list_studio_templates` (search by name or tags) or `GET /v1/studio/templates/all`. No template yet? Browse `orshot_list_library_templates`, or design one with the `orshot-design` skill.\n3. **Read its keys.** `orshot_get_studio_template_modifications` or `GET /v1/studio/templates/:id`. Never guess a key.\n4. **Render.** `orshot_render_template` or `POST /v1/studio/render`.\n5. **Deliver or post.** Show the output URL. To post it, pass the URL to `orshot_publish_social_post` (over REST, `publish` on the render also works; see Social posting).\n6. **Automate.** Put the call into what the user already runs. Playbook: `references/recurring-automation.md`.\n7. **Verify.** `orshot_list_workspace_logs` or `GET /v1/workspace/logs` shows the render and the `source` that made it. Show the output before calling it done.\n\n## Safety rules\n\n- **Confirm before anything leaves the workspace.** Publishing a post, running a workflow (it spends credits and can deliver or post) and deleting a template, workflow or brand asset need an explicit yes. Say what will go where, and when.\n- **Default social posts to drafts.** \"Prepare\", \"draft\" and \"let me look first\" mean a draft. A draft reaches nobody until it is published.\n- **Show the cost before bulk work.** Renders spend credits and AI modifications spend AI credits ([credits-usage](https://orshot.com/docs/definitions/credits-usage.md)). Say so before a loop of hundreds of renders.\n- **Never paste the API key into chat, files or URLs.** Prefer MCP, which has no key.\n- **Do not delete anything to \"clean up\" on your own.**\n\n## Gotchas (read before rendering)\n\n1. **Modifications key by `parameterId`, not layer name.** If a value is ignored, the key is wrong. Fetch the keys instead of guessing.\n2. **Multi-page templates need a `pageN@` prefix on every content key** (`\"page1@title\"`, `\"page2@title\"`). A bare content key reaches no page; the response carries a warning. A bare dotted style key (`\"title.fontSize\"`) applies to every page that has that layer. [multi-page-images](https://orshot.com/docs/image-generation/multi-page-images.md)\n3. **Studio template ids are numbers; library template ids are slugs** (`\"website-screenshot\"`). [template-id](https://orshot.com/docs/definitions/template-id.md)\n4. **MCP and REST shape the render call differently.** Over MCP, output options are flat arguments of `orshot_render_template` (`format`, `scale`, `includePages`, `size`, `extraSizes`, `background`, `videoOptions`). Over REST they sit inside the `response` object. Posting over MCP is its own tool, `orshot_publish_social_post`.\n5. **Long renders must not block.** Over MCP pass `background: true`, then collect with `orshot_get_render_job`. Over REST add `mode: \"async\"` to `response`, then poll or use a webhook ([async-render-start](https://orshot.com/docs/api-reference/async-render-start.md)). Any template can render as video; the plan must include video.\n6. **Image and video URLs in modifications must be public.** The renderer fetches them server side; localhost, expired signed URLs and login-gated URLs fail.\n7. **The response shape depends on the page count.** Read the Response section of [render-from-studio-template](https://orshot.com/docs/api-reference/render-from-studio-template.md) before writing code that reads it.\n8. **On `429`, back off** for the `Retry-After` seconds.\n9. **Publish by account id, never by platform name.** Two accounts on one platform have different ids. Media rules differ per platform: check [social-posting-guidelines](https://orshot.com/docs/publish/social-posting-guidelines.md) before posting. A PDF render posts only to LinkedIn.\n\n## Worked example: one design, several sizes\n\nPrompt: \"Render the launch template as an OG image and an Instagram story, title 'We just shipped'.\"\n\n1. `orshot_list_studio_templates` with `search: \"launch\"`. One match: id `123`.\n2. `orshot_get_studio_template_modifications` with `templateId: 123`. Keys: `title`, `hero_image`.\n3. `orshot_render_template`:\n\n```json\n{\n  \"templateId\": 123,\n  \"modifications\": { \"title\": \"We just shipped\" },\n  \"format\": \"png\",\n  \"size\": \"og-image\",\n  \"extraSizes\": [\"instagram-story\"]\n}\n```\n\n4. Check: the result lists every output URL and each extra size. A per-size `error` means that one size failed; the others are fine. Show the user every link.\n\nThe same render over REST:\n\n```js\nawait fetch(\"https://api.orshot.com/v1/studio/render\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${process.env.ORSHOT_API_KEY}` },\n  body: JSON.stringify({\n    templateId: 123,\n    modifications: { title: \"We just shipped\" },\n    response: { type: \"url\", format: \"png\", size: \"og-image\", extraSizes: [\"instagram-story\"] },\n  }),\n});\n```\n\nPreset names and size rules: [render-from-studio-template, Smart Resize](https://orshot.com/docs/api-reference/render-from-studio-template.md).\n\n## Worked example: render and hold as a draft post\n\nPrompt: \"Render this week's quote card and put it on LinkedIn as a draft.\"\n\n1. `orshot_list_social_accounts`. Pick the LinkedIn account id, say `3`. An empty list means no account is connected yet; a person connects one in the dashboard ([connect-account](https://orshot.com/docs/publish/connect-account.md)), the API cannot. A draft or scheduled post can still be saved with `platforms` instead of `accounts`; once an account on that platform is connected, add it with `orshot_update_social_post` and send the post. Publishing now needs an account.\n2. `orshot_render_template` with the modifications. It returns the image URL.\n3. `orshot_publish_social_post` with `mediaUrls: [that URL]`, `accounts: [3]`, `content` and `status: \"draft\"`. Check the result per account.\n4. After the user approves: `orshot_publish_social_draft`. To change it first: `orshot_update_social_post`.\n\nOver REST, the render `publish` object reads `isDraft` and `schedule.scheduledFor` for timing, not `status` ([publish-from-api](https://orshot.com/docs/publish/publish-from-api.md)). To post a file that already exists, use `orshot_publish_social_post` or `POST /v1/social/publish`. Scheduling, analytics and best time to post: the `orshot-social-media-scheduler` skill.\n\n## Other jobs\n\n- **Utility templates** (tweet and website screenshots, bar and comparison charts, app store screenshots, phone mockups, background removal): `orshot_render_library_template` or `POST /v1/generate/:renderType`.\n- **Many variants of one template** (a list of names, a set of products): `orshot_batch_render_template`, or loop the REST render. A full invoice or certificate run: `orshot-pdf-generator`.\n- **Stable image URLs** for OG tags, emails and embeds: `orshot_create_dynamic_url`. OG images for a whole blog or site: `orshot-blog-images`.\n- **Brand assets**: `orshot_get_brand_kit`, `orshot_import_brand_website`.\n- **Folders and workspaces**: `orshot_list_template_folders`, `orshot_set_active_workspace`.\n- **Recurring jobs** (n8n, Make, Zapier, Pipedream, cron, Orshot Workflows): `references/recurring-automation.md`.\n\n## Docs map\n\n| Job | Docs page |\n| --- | --- |\n| Render a studio template: body, response object, Smart Resize, PDF, video, publish | https://orshot.com/docs/api-reference/render-from-studio-template.md |\n| Render a library (utility) template | https://orshot.com/docs/api-reference/render-from-template.md |\n| What modification values can be | https://orshot.com/docs/definitions/modifications.md |\n| Style overrides with dot keys | https://orshot.com/docs/orshot-studio/style-parameters.md |\n| AI-generated values (`.prompt`) | https://orshot.com/docs/dynamic-parameters/prompt.md |\n| Output formats and response types | https://orshot.com/docs/definitions/response-format.md, https://orshot.com/docs/definitions/response-type.md |\n| Long renders as jobs | https://orshot.com/docs/api-reference/async-render-start.md |\n| Video options, audio, captions | https://orshot.com/docs/video-generation/video-options.md, https://orshot.com/docs/video-generation/adding-subtitles.md |\n| PDF options | https://orshot.com/docs/pdf-generation/pdf-options.md |\n| Plan a design: the formats brands run for the job, palette, type | `orshot_plan_design` (brief, `job`, size), then `orshot_get_design_spec` for the craft, the packs, the film schema, the layout grammar and the format references |\n| Template JSON (pages, elements, styles, animation) | `orshot_get_design_spec` topic `template-json`, https://orshot.com/docs/definitions/template-json.md |\n| Create or update templates over REST (Enterprise) | https://orshot.com/docs/api-reference/enterprise-template-create.md |\n| List and read templates | https://orshot.com/docs/api-reference/studio-templates-list.md, https://orshot.com/docs/api-reference/studio-template-get.md |\n| Dynamic URLs | https://orshot.com/docs/integrations/dynamic-urls.md |\n| Signed render URLs (REST) | https://orshot.com/docs/api-reference/generate-signed-url.md |\n| Social posting | https://orshot.com/docs/publish/publish-from-api.md, https://orshot.com/docs/api-reference/social-publish.md |\n| Social add-on pricing | https://orshot.com/docs/publish/pricing.md |\n| Credits | https://orshot.com/docs/definitions/credits-usage.md |\n| Errors | https://orshot.com/docs/error-reference.md |\n| Workspace logs and `source` values | https://orshot.com/docs/api-reference/workspace-logs.md |\n| Workflows over REST | https://orshot.com/docs/api-reference/workflow-create.md |\n| n8n, Make, Zapier, Pipedream and other integrations | https://orshot.com/docs/integrations/index.md |\n| SDKs (Node, Python, PHP, Ruby) | https://orshot.com/docs/sdks/index.md |\n| Importing templates | https://orshot.com/docs/importing-templates/index.md |\n| White-label editor embed | https://orshot.com/docs/orshot-embed/introduction.md |\n| Third-party apps with OAuth | https://orshot.com/docs/developers/oauth-overview.md |\n| MCP setup and tool list | https://orshot.com/docs/integrations/mcp-server.md |\n\nThe MCP server also serves `orshot://formats` (output formats and size presets).\n"
}

SHA-256 of public snapshot: 1a729c72a304fba35121fdc2cbe754d8b0fe2cfb00ef0dab94ec829482bc3dbb