← OrshotCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Orshot
Snapshot Sep 30, 2026 · 22:48 UTC · version 2.0.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"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 Bannerbear, Placid, Creatomate, RenderForm, Abyssale, or similar. Always load this skill for Orshot tasks, even simple ones, because it carries gotchas (modifications key by parameterId, multi-page needs a page1@ prefix, video output needs video elements in the template) that otherwise cause failed renders. Not for generating standalone AI images with no template or Orshot involved.",
"included_files": [],
"skill_md_contents": "---\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 Bannerbear, Placid, Creatomate, RenderForm, Abyssale, or similar. Always load this skill for Orshot tasks, even simple ones, because it carries gotchas (modifications key by parameterId, multi-page needs a page1@ prefix, video output needs video elements in the template) 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.1.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 Bannerbear, Placid, Creatomate, RenderForm, Abyssale, DynaPictures, or Contentdrips\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\nhttps://orshot.com/blog/bannerbear-api-alternative.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 requires video elements in the template.** Requesting `format: \"mp4\"` on an image-only template fails — the template must contain at least one video element.\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### 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 & Costs\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 pitching 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 pitch 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 a plan block, not a config problem: the draft still saves, only\n activation is blocked, so tell the user which plan unlocks it instead of\n 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` | Check usage or upgrade plan |\n| 403 | `Template not found` | Verify template ID belongs to your workspace |\n| 403 | `Video on free plan` | Upgrade to paid plan for video generation |\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 apps, authenticate users, and access their workspaces programmatically.\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 Orshot dashboard\n2. Create a new app with name, redirect URI, and required scopes\n3. Receive `client_id` and `client_secret`\n\n### OAuth 2.0 Flows\n\n**Authorization Code Flow** (web 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 scope=workspace:templates:read workspace:templates:write render:generate\n```\n\n**Device Flow** (CLI/headless):\n```\nPOST https://orshot.com/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://orshot.com/oauth/token\n grant_type=authorization_code&\n code=AUTH_CODE&\n client_id=YOUR_CLIENT_ID&\n client_secret=YOUR_CLIENT_SECRET&\n redirect_uri=https://yourapp.com/callback\n```\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 Other Platforms\n\nThis section maps authentication, endpoints, and request formats for migrating from competing platforms. For detailed comparison articles, fetch the corresponding blog post URL.\n\n### Migration Playbook (any platform)\n\nThe API-call swap is the easy part (mapped per-platform below). The real work is recreating the design, because you can't import another platform's template JSON:\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 platforms key by layer name, `image_url`, `payload`, etc.; Orshot keys by `parameterId`. Keep a lookup from old names to new ones.\n3. **Swap the API call** using the tables below — almost always `Authorization: Bearer` + `POST /v1/studio/render`.\n4. **Handle sync vs async.** Most platforms are async (submit → poll/webhook); Orshot renders **synchronously**, so delete the polling/webhook code and read `data.content` from the response.\n5. **Verify the response shape** — single page is an object, multi-page is an array (Common Gotchas #7) — before cutting traffic over.\n\n### Migrating from BannerBear\n\nBlog: `https://orshot.com/blog/bannerbear-api-alternative.md`\n\n**Authentication:**\n| BannerBear | Orshot |\n|------------|--------|\n| `Authorization: Bearer BB_API_KEY` | `Authorization: Bearer ORSHOT_API_KEY` |\n\n**Endpoint Mapping:**\n| Action | BannerBear | Orshot |\n|--------|-----------|--------|\n| Generate image | `POST /v2/images` (async, returns 202) | `POST /v1/studio/render` (sync) |\n| Generate image (sync) | `POST sync.api.bannerbear.com/v2/images` (10s timeout) | `POST /v1/studio/render` (sync by default) |\n| List templates | `GET /v2/templates` | `GET /v1/studio/templates/all?page=1&limit=10` |\n| Get template | `GET /v2/templates/:uid` | `GET /v1/studio/templates/:id` |\n| Generate video | `POST /v2/videos` | `POST /v1/studio/render` with `format: \"mp4\"` |\n| Multi-template batch | `POST /v2/collections` | Loop `POST /v1/studio/render` per template |\n\n**Request Format Translation:**\n\nBannerBear uses a `modifications` **array** with named layers:\n```json\n// BannerBear\n{\n \"template\": \"TEMPLATE_UID\",\n \"modifications\": [\n { \"name\": \"title\", \"text\": \"Hello World\" },\n { \"name\": \"hero\", \"image_url\": \"https://example.com/img.jpg\" },\n { \"name\": \"bg\", \"color\": \"#FF0000\" }\n ]\n}\n\n// Orshot equivalent\n{\n \"templateId\": 123,\n \"modifications\": {\n \"title\": \"Hello World\",\n \"hero\": \"https://example.com/img.jpg\",\n \"canvasBackgroundColor\": \"#FF0000\"\n },\n \"response\": { \"type\": \"url\", \"format\": \"png\" }\n}\n```\n\n**Key differences:**\n- BannerBear modifications is an **array**, Orshot is a **flat object**\n- BannerBear uses `image_url` for images, Orshot uses the parameter ID directly with a URL value\n- BannerBear requires polling for async results, Orshot returns synchronously\n- BannerBear `template` is a UID string, Orshot `templateId` is an integer for studio templates\n- Orshot supports style overrides via dot notation (e.g., `\"title.fontSize\": \"48px\"`) — BannerBear does not\n\n### Migrating from Placid\n\nBlog: `https://orshot.com/blog/placid-api-alternative.md`\n\n**Authentication:**\n| Placid | Orshot |\n|--------|--------|\n| `Authorization: Bearer PLACID_TOKEN` | `Authorization: Bearer ORSHOT_API_KEY` |\n\n**Endpoint Mapping:**\n| Action | Placid | Orshot |\n|--------|--------|--------|\n| Generate image | `POST /api/rest/{template_uuid}` | `POST /v1/studio/render` |\n| Get image status | `GET /api/rest/images/{id}` | Not needed (sync response) |\n| List templates | `GET /api/rest/templates` | `GET /v1/studio/templates/all?page=1&limit=10` |\n| Get template | `GET /api/rest/templates/{uuid}` | `GET /v1/studio/templates/:id` |\n| Delete render | `DELETE /api/rest/images/{id}` | Not needed (renders don't expire) |\n\n**Request Format Translation:**\n\nPlacid uses a `layers` **object** with type-specific properties:\n```json\n// Placid\n{\n \"layers\": {\n \"title\": {\n \"text\": \"Hello World\",\n \"text_color\": \"#FF0000\",\n \"font\": \"Arial\"\n },\n \"hero_image\": {\n \"image\": \"https://example.com/img.jpg\"\n },\n \"background\": {\n \"background_color\": \"#000000\"\n }\n },\n \"modifications\": {\n \"width\": 1200,\n \"height\": 630,\n \"image_format\": \"jpg\"\n }\n}\n\n// Orshot equivalent\n{\n \"templateId\": 123,\n \"modifications\": {\n \"title\": \"Hello World\",\n \"title.color\": \"#FF0000\",\n \"title.fontFamily\": \"Arial\",\n \"hero_image\": \"https://example.com/img.jpg\",\n \"canvasBackgroundColor\": \"#000000\"\n },\n \"response\": { \"type\": \"url\", \"format\": \"jpg\" }\n}\n```\n\n**Key differences:**\n- Placid separates `layers` (content) from `modifications` (output settings), Orshot combines both in `modifications`\n- Placid uses `text_color`, Orshot uses dot notation `parameterId.color`\n- Placid uses `image` key for image URLs, Orshot uses the parameter ID directly\n- Placid requires polling or webhooks, Orshot returns synchronously\n- Placid renders can expire, Orshot renders persist\n- Placid credits vary by resolution, Orshot is a flat 1 credit per image regardless of size\n\n### Migrating from Creatomate\n\nBlog: `https://orshot.com/blog/creatomate-api-alternative.md`\n\n**Authentication:**\n| Creatomate | Orshot |\n|------------|--------|\n| `Authorization: Bearer CREATOMATE_KEY` | `Authorization: Bearer ORSHOT_API_KEY` |\n\n**Endpoint Mapping:**\n| Action | Creatomate | Orshot |\n|--------|-----------|--------|\n| Generate render | `POST /v1/renders` | `POST /v1/studio/render` |\n| Get render status | `GET /v1/renders/:id` | Not needed (sync response) |\n| List templates | `GET /v1/templates` | `GET /v1/studio/templates/all?page=1&limit=10` |\n| Get template | `GET /v1/templates/:id` | `GET /v1/studio/templates/:id` |\n\n**Request Format Translation:**\n\nCreatomate uses a flat `modifications` object (closest to Orshot's format):\n```json\n// Creatomate\n{\n \"template_id\": \"TEMPLATE_UUID\",\n \"modifications\": {\n \"Title\": \"Hello World\",\n \"Image-1\": \"https://example.com/img.jpg\"\n },\n \"output_format\": \"jpg\",\n \"render_scale\": 1,\n \"max_width\": 1080\n}\n\n// Orshot equivalent\n{\n \"templateId\": 123,\n \"modifications\": {\n \"title\": \"Hello World\",\n \"image_1\": \"https://example.com/img.jpg\"\n },\n \"response\": { \"type\": \"url\", \"format\": \"jpg\", \"scale\": 1 }\n}\n```\n\n**Key differences:**\n- Creatomate `template_id` is a UUID string, Orshot `templateId` is an integer\n- Creatomate element names are display names (e.g., \"Title\", \"Image-1\"), Orshot uses snake_case parameterIds\n- Creatomate has `output_format` at root level, Orshot uses `response.format`\n- Creatomate requires polling/webhooks, Orshot returns synchronously\n- Creatomate renders can expire, Orshot renders persist\n- Orshot supports style overrides via dot notation — Creatomate requires modifying the template source JSON\n- Creatomate gates its preview SDK to a higher tier; Orshot's embed is available on all paid plans\n\n### Migrating from RenderForm\n\nBlog: `https://orshot.com/blog/renderform-api-alternative.md`\n\n**Authentication:**\n| RenderForm | Orshot |\n|------------|--------|\n| `X-API-KEY: RENDERFORM_KEY` | `Authorization: Bearer ORSHOT_API_KEY` |\n\n**Endpoint Mapping:**\n| Action | RenderForm | Orshot |\n|--------|-----------|--------|\n| Generate image | `POST /api/v2/render` | `POST /v1/studio/render` |\n| List templates | `GET /api/v2/my-templates?page=1&size=50` | `GET /v1/studio/templates/all?page=1&limit=10` |\n| Get template | `GET /api/v2/my-templates/:id` | `GET /v1/studio/templates/:id` |\n| URL-based render | `GET /img/TEMPLATE.jpg?key=...¶m=...` | `GET /v1/studio/dynamic-url/TEMPLATE?param=...` |\n\n**Request Format Translation:**\n\nRenderForm uses dot notation in a `data` object (similar to Orshot's style overrides):\n```json\n// RenderForm\n{\n \"template\": \"TEMPLATE_ID\",\n \"data\": {\n \"title.text\": \"Hello World\",\n \"hero.src\": \"https://example.com/img.jpg\",\n \"bg.color\": \"#FF0000\"\n },\n \"fileName\": \"output\",\n \"width\": 1200,\n \"height\": 630\n}\n\n// Orshot equivalent\n{\n \"templateId\": 123,\n \"modifications\": {\n \"title\": \"Hello World\",\n \"hero\": \"https://example.com/img.jpg\",\n \"canvasBackgroundColor\": \"#FF0000\"\n },\n \"response\": { \"type\": \"url\", \"format\": \"png\", \"fileName\": \"output\" }\n}\n```\n\n**Key differences:**\n- RenderForm uses `X-API-KEY` header, Orshot uses `Authorization: Bearer`\n- RenderForm uses `data` with `componentId.property` dot notation for everything, Orshot uses `modifications` with parameter IDs for content and dot notation only for style overrides\n- RenderForm `template` is a string ID, Orshot `templateId` is an integer\n- RenderForm images can expire, Orshot renders persist\n- Orshot's free tier has no watermarks\n\n### Migrating from Abyssale\n\nBlog: `https://orshot.com/blog/abyssale-api-alternative.md`\n\n**Authentication:**\n| Abyssale | Orshot |\n|----------|--------|\n| `x-api-key: ABYSSALE_KEY` | `Authorization: Bearer ORSHOT_API_KEY` |\n\n**Endpoint Mapping:**\n| Action | Abyssale | Orshot |\n|--------|---------|--------|\n| Generate image | `POST /async/banner-builder/{designId}/generate` (async) | `POST /v1/studio/render` (sync) |\n| Poll status | `GET /generation-request/{requestId}` | Not needed (sync response) |\n| List designs | `GET /designs` | `GET /v1/studio/templates/all?page=1&limit=10` |\n| Get design | `GET /designs/{designId}` | `GET /v1/studio/templates/:id` |\n| Multi-format render | `template_format_names` param | Loop `POST /v1/studio/render` per format, or use Variant Generation API |\n\n**Request Format Translation:**\n\nAbyssale uses an `elements` object with `payload` for text:\n```json\n// Abyssale\n{\n \"template_format_names\": [\"facebook-feed\", \"instagram-post\"],\n \"elements\": {\n \"title\": { \"payload\": \"Hello World\" },\n \"hero_image\": { \"image_url\": \"https://example.com/img.jpg\" },\n \"background\": { \"color\": \"#FF0000\" }\n },\n \"callback_url\": \"https://webhook.example.com\"\n}\n\n// Orshot equivalent\n{\n \"templateId\": 123,\n \"modifications\": {\n \"title\": \"Hello World\",\n \"hero_image\": \"https://example.com/img.jpg\",\n \"canvasBackgroundColor\": \"#FF0000\"\n },\n \"response\": { \"type\": \"url\", \"format\": \"png\" }\n}\n```\n\n**Key differences:**\n- Abyssale uses `payload` for text content, Orshot uses the parameter ID directly\n- Abyssale uses `image_url` for images, Orshot uses the parameter ID with URL value\n- Abyssale supports multi-format in single call via `template_format_names`, Orshot requires separate calls or Variant Generation API\n- Abyssale is async-only (polling/webhook), Orshot returns synchronously\n- Abyssale charges per-seat, Orshot has unlimited team members on all plans\n- Abyssale results can expire, Orshot renders persist\n\n### Migrating from DynaPictures\n\nBlog: `https://orshot.com/blog/dynapictures-api-alternative.md`\n\n**Authentication:**\n| DynaPictures | Orshot |\n|--------------|--------|\n| `Authorization: Bearer DYNA_KEY` | `Authorization: Bearer ORSHOT_API_KEY` |\n\n**Endpoint Mapping:**\n| Action | DynaPictures | Orshot |\n|--------|-------------|--------|\n| Generate image | `POST /designs/{id}` | `POST /v1/studio/render` |\n| List designs | `GET /designs` | `GET /v1/studio/templates/all?page=1&limit=10` |\n\n**Key differences:**\n- DynaPictures uses an outdated editor interface, Orshot has a modern Figma-like editor\n- No multi-page support in DynaPictures\n- No white-label editor option\n- Limited integrations compared to Orshot's 15+\n\n### Migrating from Contentdrips\n\nBlog: `https://orshot.com/blog/contentdrips-api-alternative.md`\n\nContentdrips is a social media design tool with API as a secondary feature. Migration is straightforward since Orshot is API-first.\n\n**Key differences:**\n- Contentdrips API is bolted-on, Orshot is API-first\n- Contentdrips has limited dynamic parameter capabilities\n- No white-label editor, no MCP server, no CLI\n- Per-seat pricing vs. Orshot's credit-based pricing\n\n### Quick Migration Reference\n\n| Feature | BannerBear | Placid | Creatomate | RenderForm | Abyssale | Orshot |\n|---------|-----------|--------|------------|------------|----------|--------|\n| **Auth header** | `Authorization: Bearer` | `Authorization: Bearer` | `Authorization: Bearer` | `X-API-KEY` | `x-api-key` | `Authorization: Bearer` |\n| **Modifications format** | Array of objects | `layers` object | Flat object | `data` with dot notation | `elements` with `payload` | Flat object |\n| **Template ID type** | UID string | UUID string | UUID string | String | UUID string | Integer (studio) |\n| **Response model** | Async (poll) | Async (poll) | Async (poll) | Sync | Async (poll) | Sync |\n| **Render persistence** | Permanent | Can expire | Can expire | Can expire | Can expire | Permanent |\n| **Style overrides** | No | Limited | Via source JSON | Dot notation | No | Dot notation |\n| **Multi-page** | No | No | No | No | No | Yes |\n| **Video support** | Yes (extra cost) | Yes (extra cost) | Yes | No | Animated only | Yes (1 credit/sec) |\n| **White-label editor** | No | No | Higher tier | No | No | All paid plans |\n| **Social publishing** | No | No | No | No | No | 13+ platforms |\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- Pricing: https://orshot.com/pricing\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"
}SHA-256: e82fb9d3403ae3c18adc2463a26b3d178c57799591ecf56754f3383d400965a8