openapi: "3.1.0"
info:
  title: Pictify API
  version: 1.0.0
  description: |
    Generate images, GIFs, and PDFs from HTML templates programmatically.

    ## Authentication
    All API requests require a Bearer token in the Authorization header:
    ```
    Authorization: Bearer pk_live_your_api_key
    ```

    ## Rate Limits
    - Free: 60 requests/minute
    - Pro: 300 requests/minute
    - Business: 1,000 requests/minute

    Rate limit headers are included in all responses.
  contact:
    email: support@pictify.io
    url: https://pictify.io
  license:
    name: MIT

servers:
  - url: https://api.pictify.io
    description: Production

security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key obtained from the Pictify dashboard

  schemas:
    Error:
      type: object
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri
          description: Link to error documentation
          example: https://docs.pictify.io/errors/invalid-api-key
        title:
          type: string
          description: Short error title
          example: Invalid API Key
        status:
          type: integer
          description: HTTP status code
          example: 401
        detail:
          type: string
          description: Detailed error message
          example: The provided API key is invalid or has been revoked.
        instance:
          type: string
          description: Request path that caused the error
          example: /image

    ImageRequest:
      type: object
      properties:
        html:
          type: string
          description: HTML content to render
        url:
          type: string
          format: uri
          description: URL to screenshot (mutually exclusive with html)
        template:
          type: string
          description: Template UID to render
        variables:
          type: object
          additionalProperties: true
          description: Variables to populate in the template
        width:
          type: integer
          minimum: 1
          maximum: 4000
          default: 1200
          description: Output width in pixels
        height:
          type: integer
          minimum: 1
          maximum: 4000
          default: 630
          description: Output height in pixels
        selector:
          type: string
          description: CSS selector to capture specific element
        fileExtension:
          type: string
          enum: [png, jpg, jpeg, webp]
          default: png
          description: Output image format

    ImageResponse:
      type: object
      properties:
        url:
          type: string
          format: uri
          description: URL of the generated image
          example: https://cdn.pictify.io/renders/abc123.png
        userStorageUrl:
          type: string
          format: uri
          description: URL in user's configured storage (if configured)
        id:
          type: string
          description: Unique image identifier
          example: img_abc123
        createdAt:
          type: string
          format: date-time
          description: Timestamp when image was created

    RenderResponse:
      type: object
      description: Render response. Always returns a `results` array, even for single-layout renders.
      properties:
        results:
          type: array
          description: Array of rendered images, one per layout
          items:
            type: object
            properties:
              layout:
                type: string
                description: Layout key ('default' for base layout)
                example: twitter-post
              name:
                type: string
                description: Display name of the layout
                example: Twitter/X Post
              url:
                type: string
                format: uri
                description: URL of the rendered image
                example: https://cdn.pictify.io/renders/abc123.png
              width:
                type: integer
                description: Image width in pixels
              height:
                type: integer
                description: Image height in pixels
              format:
                type: string
                description: Image format
                example: png
              id:
                type: string
                description: Unique render identifier
        errors:
          type: array
          description: Layout keys that failed to render
          items:
            type: object
            properties:
              layout:
                type: string
              error:
                type: string
        totalLayouts:
          type: integer
          description: Total number of layouts requested
        totalRendered:
          type: integer
          description: Number of successfully rendered layouts
        totalErrors:
          type: integer
          description: Number of failed layouts
        templateUid:
          type: string
          description: Template ID that was rendered

    GifRequest:
      type: object
      properties:
        html:
          type: string
          description: HTML content with CSS animations
        url:
          type: string
          format: uri
          description: URL to capture
        template:
          type: string
          description: Template UID
        variables:
          type: object
          additionalProperties: true
        width:
          type: integer
          minimum: 1
          maximum: 2000
          default: 800
        height:
          type: integer
          minimum: 1
          maximum: 2000
          default: 600

    GifCaptureRequest:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          description: URL to capture
        width:
          type: integer
          minimum: 1
          maximum: 2000
          default: 800
        height:
          type: integer
          minimum: 1
          maximum: 2000
          default: 600
        quality:
          type: string
          enum: [low, medium, high]
          default: medium
          description: Quality preset
        frameDurationSeconds:
          type: number
          minimum: 1
          maximum: 30
          description: Recording duration in seconds

    GifResponse:
      type: object
      properties:
        gif:
          type: object
          properties:
            url:
              type: string
              format: uri
            uid:
              type: string
            width:
              type: integer
            height:
              type: integer
            animationLength:
              type: number
              description: Animation duration in seconds

    PdfRenderRequest:
      type: object
      required:
        - templateUid
      properties:
        templateUid:
          type: string
          description: Template UID
        variables:
          type: object
          additionalProperties: true
        options:
          type: object
          properties:
            preset:
              type: string
              enum: [A4, Letter, Legal, A3, A5, A3_LANDSCAPE, CUSTOM]
              default: A4
            customSize:
              type: object
              properties:
                width:
                  type: integer
                height:
                  type: integer
            margins:
              type: object
              properties:
                top:
                  type: integer
                bottom:
                  type: integer
                left:
                  type: integer
                right:
                  type: integer
            title:
              type: string

    PdfMultiPageRequest:
      type: object
      required:
        - templateUid
        - variableSets
      properties:
        templateUid:
          type: string
        variableSets:
          type: array
          items:
            type: object
            additionalProperties: true
          minItems: 1
          maxItems: 100
        options:
          $ref: '#/components/schemas/PdfRenderRequest/properties/options'

    PdfResponse:
      type: object
      properties:
        success:
          type: boolean
        url:
          type: string
          format: uri
        userStorageUrl:
          type: string
          format: uri
        pageCount:
          type: integer
        preset:
          type: string
        pageSize:
          type: object
          properties:
            width:
              type: integer
            height:
              type: integer

    Template:
      type: object
      properties:
        uid:
          type: string
        name:
          type: string
        engine:
          type: string
          enum: [fabric, html]
          description: |
            Which renderer owns this template.
            - `fabric` — legacy canvas templates (fabricJSData)
            - `html` — Handlebars + HTML templates (html field, supports `{{var}}`, `{{#each}}`, `{{#if}}`, and the built-in helper library)
        html:
          type: string
          description: Handlebars source for `engine=html` templates.
        fabricJSData:
          type: object
          description: FabricJS canvas JSON for `engine=fabric` templates.
          additionalProperties: true
        jsEnabled:
          type: boolean
          description: |
            Only meaningful for `engine=html`. When true, scripts inside the template
            execute during render (Chart.js, KaTeX, animated SVG). Off by default
            to prevent runaway loops; a 30s hard timeout still applies.
          default: false
        strictVariables:
          type: boolean
          description: |
            Only meaningful for `engine=html`. When true, rendering fails with HTTP 422
            if a root-level variable referenced in the template was not supplied.
            Leave off if you rely on `{{#if optional}}` guards.
          default: false
        type:
          type: string
        category:
          type: string
        tags:
          type: array
          items:
            type: string
        width:
          type: integer
        height:
          type: integer
        thumbnail:
          type: string
          format: uri
        outputFormat:
          type: string
          enum: [image, pdf]
          default: image
        pdfPreset:
          type: string
          description: Page preset used when `outputFormat=pdf`.
          enum: [A4, A4_LANDSCAPE, LETTER, LETTER_LANDSCAPE, LEGAL, A3, TABLOID]
          default: A4
        variableDefinitions:
          type: array
          items:
            $ref: '#/components/schemas/VariableDefinition'
        pages:
          type: array
          description: Multi-page canvas pages (fabric engine).
          items:
            type: object
            properties:
              pageNumber:
                type: integer
              name:
                type: string
              fabricJSData:
                type: object
                additionalProperties: true
        layouts:
          type: object
          description: |
            Named layout variants. Keys are short identifiers matching
            `^[a-z0-9-]{1,64}$` (e.g. `twitter-post`, `og-image`). Each value
            overrides `width`, `height`, and renderer-specific data for that
            variant. Limit: 20 layouts per template.
          additionalProperties:
            type: object
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    VariableDefinition:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Variable identifier. Must match the name used in `{{name}}` tokens.
        type:
          type: string
          enum: [text, image, color, chart, table, array, object]
          default: text
        defaultValue:
          description: |
            Fallback value when the variable is not supplied at render time.
            Typed according to `type` — string for text/image/color/url, number for number,
            boolean for boolean, ISO date string for date, JSON array/object for array/object.
        description:
          type: string
        allowRawHtml:
          type: boolean
          description: |
            Only meaningful for `type=text` on `engine=html` templates. When true the value
            is NOT HTML-escaped at render time. Use with care — treat as trusted input.
          default: false
        validation:
          type: object
          properties:
            required:
              type: boolean
            minLength:
              type: integer
            maxLength:
              type: integer

    BatchRenderRequest:
      type: object
      required:
        - variableSets
      properties:
        variableSets:
          type: array
          items:
            type: object
            additionalProperties: true
          minItems: 1
          maxItems: 100
        format:
          type: string
          enum: [png, jpeg, webp]
          default: png
        quality:
          type: number
          minimum: 0.1
          maximum: 1
          default: 0.9
        layout:
          type: string
          pattern: '^[a-z0-9-]{1,64}$'
          description: Layout variant to use for all batch items. Omit for default layout.
        layouts:
          type: array
          items:
            type: string
            pattern: '^[a-z0-9-]{1,64}$'
          description: Array of layout variant names to render for all batch items. Use instead of `layout` to render multiple layouts per item.
        concurrency:
          type: integer
          minimum: 1
          maximum: 10
          default: 5

    BatchResponse:
      type: object
      properties:
        batchId:
          type: string
        status:
          type: string
          enum: [pending, processing, completed, failed, partial, cancelled]
        totalItems:
          type: integer
        message:
          type: string

    BatchStatus:
      type: object
      properties:
        batchId:
          type: string
        status:
          type: string
          enum: [pending, processing, completed, failed, partial, cancelled]
        progress:
          type: integer
          minimum: 0
          maximum: 100
        totalItems:
          type: integer
        completedItems:
          type: integer
        failedItems:
          type: integer
        results:
          type: array
          items:
            type: object
            properties:
              index:
                type: integer
                description: Zero-based index of this item in the batch
              success:
                type: boolean
                description: Whether this batch item rendered successfully
              variables:
                type: array
                items:
                  type: string
                description: Variable names used for this item
              results:
                type: array
                description: Rendered images, one per requested layout
                items:
                  type: object
                  properties:
                    layout:
                      type: string
                      description: Layout key ('default' for base layout)
                      example: twitter-post
                    name:
                      type: string
                      description: Display name of the layout
                      example: Twitter/X Post
                    url:
                      type: string
                      format: uri
                      description: URL of the rendered image
                    width:
                      type: integer
                      description: Image width in pixels
                    height:
                      type: integer
                      description: Image height in pixels
                    format:
                      type: string
                      description: Image format
                      example: png
                    id:
                      type: string
                      description: Unique render identifier
              errors:
                type: array
                description: Layout keys that failed to render for this item
                items:
                  type: object
                  properties:
                    layout:
                      type: string
                    error:
                      type: string
        createdAt:
          type: string
          format: date-time
        startedAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time

    WebhookSubscription:
      type: object
      properties:
        uid:
          type: string
        event:
          type: string
          enum: [render.completed, render.failed, binding.updated, binding.failed]
        targetUrl:
          type: string
          format: uri
        platform:
          type: string
          enum: [zapier, make, n8n, pipedream, custom]
        status:
          type: string
          enum: [active, paused, failed]
        filters:
          type: object
          properties:
            templateId:
              type: string
            bindingId:
              type: string
        deliveryCount:
          type: integer
        failureCount:
          type: integer
        lastDeliveryAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time

    Binding:
      type: object
      properties:
        uid:
          type: string
        name:
          type: string
        templateId:
          type: string
        dataSource:
          type: object
          properties:
            type:
              type: string
              enum: [http, webhook, static]
            url:
              type: string
              format: uri
            method:
              type: string
              enum: [GET, POST]
        mapping:
          type: object
          additionalProperties:
            type: string
        defaults:
          type: object
          additionalProperties: true
        refreshPolicy:
          type: object
          properties:
            type:
              type: string
              enum: [ttl, etag, webhook, manual]
            ttlSeconds:
              type: integer
            onError:
              type: string
              enum: [serve_stale, serve_error, serve_default]
        status:
          type: string
          enum: [active, paused, error]
        renderUrl:
          type: string
          format: uri
        webhookUrl:
          type: string
          format: uri
        createdAt:
          type: string
          format: date-time

    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer
        hasNext:
          type: boolean
        hasPrev:
          type: boolean

paths:
  /image:
    post:
      operationId: createImage
      summary: Generate an image
      description: Generate an image from HTML, URL, or template
      tags:
        - Images
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImageRequest'
      responses:
        '200':
          description: Image generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImageResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    get:
      operationId: listImages
      summary: List images
      description: Get a paginated list of generated images
      tags:
        - Images
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 30
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        '200':
          description: List of images
          content:
            application/json:
              schema:
                type: object
                properties:
                  images:
                    type: array
                    items:
                      $ref: '#/components/schemas/ImageResponse'
                  pagination:
                    type: object
                    properties:
                      total:
                        type: integer
                      limit:
                        type: integer
                      offset:
                        type: integer
                      hasMore:
                        type: boolean

  /image/{uid}:
    get:
      operationId: getImage
      summary: Get image details
      tags:
        - Images
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Image details
          content:
            application/json:
              schema:
                type: object
                properties:
                  image:
                    $ref: '#/components/schemas/ImageResponse'
        '404':
          description: Image not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /image/canvas:
    post:
      operationId: createCanvasImage
      summary: Generate image from FabricJS canvas
      description: Render an image from FabricJS canvas data
      tags:
        - Images
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - fabricJSData
              properties:
                fabricJSData:
                  type: object
                  description: FabricJS canvas JSON
                variables:
                  type: object
                  additionalProperties: true
                variableDefinitions:
                  type: array
                  items:
                    $ref: '#/components/schemas/VariableDefinition'
                width:
                  type: integer
                height:
                  type: integer
                fileExtension:
                  type: string
                  enum: [png, jpg, jpeg, webp]
      responses:
        '200':
          description: Canvas image generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImageResponse'

  /gif:
    post:
      operationId: createGif
      summary: Generate a GIF
      description: Generate an animated GIF from HTML with CSS animations
      tags:
        - GIFs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GifRequest'
      responses:
        '200':
          description: GIF generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GifResponse'
    get:
      operationId: listGifs
      summary: List GIFs
      tags:
        - GIFs
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 30
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of GIFs
          content:
            application/json:
              schema:
                type: object
                properties:
                  gifs:
                    type: array
                    items:
                      $ref: '#/components/schemas/GifResponse/properties/gif'

  /gif/capture:
    post:
      operationId: captureGif
      summary: Capture GIF from URL
      description: Record a GIF from a live webpage
      tags:
        - GIFs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GifCaptureRequest'
      responses:
        '200':
          description: GIF captured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GifResponse'

  /pdf/render:
    post:
      operationId: renderPdf
      summary: Generate a PDF
      description: Generate a single-page PDF from a template
      tags:
        - PDFs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PdfRenderRequest'
      responses:
        '200':
          description: PDF generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PdfResponse'

  /pdf/multi-page:
    post:
      operationId: renderMultiPagePdf
      summary: Generate multi-page PDF
      description: Generate a PDF with multiple pages from a template
      tags:
        - PDFs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PdfMultiPageRequest'
      responses:
        '200':
          description: Multi-page PDF generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PdfResponse'

  /pdf/presets:
    get:
      operationId: getPdfPresets
      summary: Get PDF presets
      description: Get available PDF size presets
      tags:
        - PDFs
      responses:
        '200':
          description: PDF presets
          content:
            application/json:
              schema:
                type: object
                properties:
                  presets:
                    type: object

  /templates:
    get:
      operationId: listTemplates
      summary: List templates
      tags:
        - Templates
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 100
            default: 12
        - name: sort
          in: query
          schema:
            type: string
            enum: [newest, oldest, name]
            default: newest
        - name: outputFormat
          in: query
          schema:
            type: string
            enum: [all, image, pdf]
            default: all
      responses:
        '200':
          description: List of templates
          content:
            application/json:
              schema:
                type: object
                properties:
                  templates:
                    type: array
                    items:
                      $ref: '#/components/schemas/Template'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
    post:
      operationId: createTemplate
      summary: Create a template
      description: |
        Create a new template. Pictify supports two template engines:

        - `engine: "html"` — Handlebars + HTML. Author a template with `{{variable}}` tokens,
          `{{#each}}` / `{{#if}}` blocks, and the built-in helper library. Supplied `html`
          is compile-validated on save; any identifier referenced in the template body
          but missing from `variableDefinitions` is auto-added as a text variable and
          returned in `addedVariables` on the response.
        - `engine: "fabric"` (default) — FabricJS canvas. Provide `fabricJSData`.

        If `engine` is omitted the server defaults to `fabric` for backwards compatibility.
        New HTML templates should set `engine: "html"` explicitly.
      tags:
        - Templates
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Human-readable template name. Shown in the dashboard.
                engine:
                  type: string
                  enum: [fabric, html]
                  default: fabric
                  description: |
                    Template engine. Set `html` to author with Handlebars + HTML.
                html:
                  type: string
                  description: |
                    Required for `engine=html`. Handlebars source. Must compile.
                    Undeclared `{{identifier}}` references are auto-added as text variables.
                fabricJSData:
                  type: object
                  description: Required for `engine=fabric`. FabricJS canvas JSON.
                  additionalProperties: true
                width:
                  type: integer
                  description: Render width in px.
                height:
                  type: integer
                  description: Render height in px.
                type:
                  type: string
                  description: Template type tag (e.g. `og-image`, `twitter-card`). Free-form.
                category:
                  type: string
                tags:
                  type: array
                  items:
                    type: string
                variableDefinitions:
                  type: array
                  description: |
                    Variable schema. For `engine=html`, identifiers referenced in the template
                    body but not declared here are auto-added as text variables and returned
                    under `addedVariables` in the response.
                  items:
                    $ref: '#/components/schemas/VariableDefinition'
                jsEnabled:
                  type: boolean
                  default: false
                  description: |
                    `engine=html` only. When true, scripts inside the template execute at
                    render time. Enables Chart.js, KaTeX, and animated SVG. Disables
                    infinite-loop protection; the 30s hard timeout still applies.
                strictVariables:
                  type: boolean
                  default: false
                  description: |
                    `engine=html` only. When true, missing root-level variables throw
                    HTTP 422 at render time. Leave off if you rely on `{{#if optional}}` guards.
                outputFormat:
                  type: string
                  enum: [image, pdf]
                  default: image
                pdfPreset:
                  type: string
                  enum: [A4, A4_LANDSCAPE, LETTER, LETTER_LANDSCAPE, LEGAL, A3, TABLOID]
                  default: A4
                  description: Page preset when `outputFormat=pdf`.
                pages:
                  type: array
                  description: Multi-page canvas pages (`engine=fabric` only).
                  items:
                    type: object
                    properties:
                      pageNumber:
                        type: integer
                      name:
                        type: string
                      fabricJSData:
                        type: object
                        additionalProperties: true
            examples:
              handlebars:
                summary: Handlebars HTML template
                value:
                  name: OG Image
                  engine: html
                  width: 1200
                  height: 630
                  html: |
                    <div style="width:1200px;height:630px;padding:64px;font-family:Inter">
                      <h1 style="font-size:72px">{{title}}</h1>
                      <p style="font-size:32px;color:#6b7280">{{subtitle}}</p>
                      {{#if showCta}}<button>{{ctaText}}</button>{{/if}}
                    </div>
                  variableDefinitions:
                    - name: title
                      type: text
                      defaultValue: Hello world
                    - name: subtitle
                      type: text
                    - name: showCta
                      type: boolean
                      defaultValue: false
                    - name: ctaText
                      type: text
                      defaultValue: Learn more
                  strictVariables: false
                  jsEnabled: false
              fabric:
                summary: FabricJS canvas template
                value:
                  name: Quote card
                  engine: fabric
                  width: 1080
                  height: 1080
                  fabricJSData: { version: "5.3.0", objects: [] }
      responses:
        '200':
          description: Template created
          content:
            application/json:
              schema:
                type: object
                properties:
                  template:
                    $ref: '#/components/schemas/Template'
                  addedVariables:
                    type: array
                    description: |
                      `engine=html` only. Names of identifiers referenced in the template
                      body that were auto-added to `variableDefinitions` during save. Omitted
                      when the array would be empty.
                    items:
                      type: string
        '422':
          description: |
            Validation failed. Typical causes for `engine=html`: unclosed Handlebars block,
            unknown helper, or (when `strictVariables=true`) a referenced variable without
            a definition.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
                  context:
                    type: object
                    additionalProperties: true

  /templates/{uid}:
    get:
      operationId: getTemplate
      summary: Get template
      tags:
        - Templates
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Template details
          content:
            application/json:
              schema:
                type: object
                properties:
                  template:
                    $ref: '#/components/schemas/Template'
        '404':
          description: Template not found
    put:
      operationId: updateTemplate
      summary: Update template
      tags:
        - Templates
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                html:
                  type: string
                fabricJSData:
                  type: object
                width:
                  type: integer
                height:
                  type: integer
                variableDefinitions:
                  type: array
                  items:
                    $ref: '#/components/schemas/VariableDefinition'
      responses:
        '200':
          description: Template updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  template:
                    $ref: '#/components/schemas/Template'
    delete:
      operationId: deleteTemplate
      summary: Delete template
      tags:
        - Templates
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Template deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /templates/{uid}/render:
    post:
      operationId: renderTemplate
      summary: Render a template
      description: Generate an image from a template with variables
      tags:
        - Templates
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                variables:
                  type: object
                  additionalProperties: true
                  description: Key-value pairs of template variable values
                format:
                  type: string
                  enum: [png, jpeg, webp, pdf]
                  default: png
                  description: Output image format
                quality:
                  type: number
                  minimum: 0.1
                  maximum: 1
                  default: 0.9
                  description: Image quality (0.1-1.0)
                layout:
                  type: string
                  pattern: '^[a-z0-9-]{1,64}$'
                  description: Render a specific layout variant (e.g. 'twitter-post', 'facebook-post'). Omit for default layout.
                layouts:
                  type: array
                  items:
                    type: string
                  maxItems: 20
                  description: Render multiple layout variants in one request. Use 'default' for the base layout. Returns an array of results.
                  example: ["default", "twitter-post", "facebook-post"]
      responses:
        '200':
          description: Rendered image(s). Always returns a `results` array.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RenderResponse'

  /templates/{uid}/variables:
    get:
      operationId: getTemplateVariables
      summary: Get template variables
      tags:
        - Templates
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Template variables
          content:
            application/json:
              schema:
                type: object
                properties:
                  templateUid:
                    type: string
                  templateName:
                    type: string
                  variables:
                    type: array
                    items:
                      $ref: '#/components/schemas/VariableDefinition'

  /templates/{uid}/batch-render:
    post:
      operationId: batchRenderTemplate
      summary: Batch render template
      description: Generate multiple images from a template in one request
      tags:
        - Batch
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchRenderRequest'
      responses:
        '202':
          description: Batch job created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchResponse'

  /templates/batch/{batchId}/results:
    get:
      operationId: getBatchResults
      summary: Get batch job results
      tags:
        - Batch
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Batch job status and results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchStatus'

  /templates/batch/{batchId}/cancel:
    post:
      operationId: cancelBatch
      summary: Cancel batch job
      tags:
        - Batch
      parameters:
        - name: batchId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Batch job cancelled
          content:
            application/json:
              schema:
                type: object
                properties:
                  batchId:
                    type: string
                  status:
                    type: string
                  message:
                    type: string

  /webhook-subscriptions:
    get:
      operationId: listWebhookSubscriptions
      summary: List webhook subscriptions
      tags:
        - Webhooks
      parameters:
        - name: event
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [active, paused, failed]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: List of subscriptions
          content:
            application/json:
              schema:
                type: object
                properties:
                  subscriptions:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscription'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
    post:
      operationId: createWebhookSubscription
      summary: Create webhook subscription
      tags:
        - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - event
                - targetUrl
              properties:
                event:
                  type: string
                  enum: [render.completed, render.failed, binding.updated, binding.failed]
                targetUrl:
                  type: string
                  format: uri
                platform:
                  type: string
                  enum: [zapier, make, n8n, pipedream, custom]
                  default: custom
                filters:
                  type: object
                  properties:
                    templateId:
                      type: string
                    bindingId:
                      type: string
      responses:
        '201':
          description: Subscription created
          content:
            application/json:
              schema:
                type: object
                properties:
                  subscription:
                    allOf:
                      - $ref: '#/components/schemas/WebhookSubscription'
                      - type: object
                        properties:
                          secret:
                            type: string
                            description: Webhook signing secret (only returned on creation)

  /webhook-subscriptions/{uid}:
    get:
      operationId: getWebhookSubscription
      summary: Get webhook subscription
      tags:
        - Webhooks
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Subscription details
          content:
            application/json:
              schema:
                type: object
                properties:
                  subscription:
                    $ref: '#/components/schemas/WebhookSubscription'
    put:
      operationId: updateWebhookSubscription
      summary: Update webhook subscription
      tags:
        - Webhooks
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                targetUrl:
                  type: string
                  format: uri
                filters:
                  type: object
                status:
                  type: string
                  enum: [active, paused]
      responses:
        '200':
          description: Subscription updated
    delete:
      operationId: deleteWebhookSubscription
      summary: Delete webhook subscription
      tags:
        - Webhooks
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Subscription deleted

  /bindings:
    get:
      operationId: listBindings
      summary: List bindings
      tags:
        - Bindings
      parameters:
        - name: templateId
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [active, paused, error]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: List of bindings
          content:
            application/json:
              schema:
                type: object
                properties:
                  bindings:
                    type: array
                    items:
                      $ref: '#/components/schemas/Binding'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
    post:
      operationId: createBinding
      summary: Create a binding
      tags:
        - Bindings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - templateId
                - dataSource
                - mapping
              properties:
                templateId:
                  type: string
                name:
                  type: string
                dataSource:
                  type: object
                  required:
                    - type
                  properties:
                    type:
                      type: string
                      enum: [http, webhook, static]
                    url:
                      type: string
                      format: uri
                    method:
                      type: string
                      enum: [GET, POST]
                    headers:
                      type: object
                      additionalProperties:
                        type: string
                    credentials:
                      type: object
                      properties:
                        type:
                          type: string
                          enum: [api_key, bearer_token, basic_auth, custom_header]
                        value:
                          type: string
                mapping:
                  type: object
                  additionalProperties:
                    type: string
                defaults:
                  type: object
                refreshPolicy:
                  type: object
                  properties:
                    type:
                      type: string
                      enum: [ttl, etag, webhook, manual]
                    ttlSeconds:
                      type: integer
                    onError:
                      type: string
                      enum: [serve_stale, serve_error, serve_default]
                outputConfig:
                  type: object
                  properties:
                    format:
                      type: string
                      enum: [png, jpeg, webp, pdf]
                    quality:
                      type: integer
      responses:
        '201':
          description: Binding created
          content:
            application/json:
              schema:
                type: object
                properties:
                  binding:
                    $ref: '#/components/schemas/Binding'

  /bindings/{uid}:
    get:
      operationId: getBinding
      summary: Get binding
      tags:
        - Bindings
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Binding details
          content:
            application/json:
              schema:
                type: object
                properties:
                  binding:
                    $ref: '#/components/schemas/Binding'
    put:
      operationId: updateBinding
      summary: Update binding
      tags:
        - Bindings
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                dataSource:
                  type: object
                mapping:
                  type: object
                defaults:
                  type: object
                refreshPolicy:
                  type: object
                outputConfig:
                  type: object
                status:
                  type: string
                  enum: [active, paused]
      responses:
        '200':
          description: Binding updated
    delete:
      operationId: deleteBinding
      summary: Delete binding
      tags:
        - Bindings
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Binding deleted

  /gif/{uid}:
    get:
      operationId: getGif
      summary: Get GIF details
      description: Retrieve details of a specific GIF by its UID. No authentication required.
      tags:
        - GIFs
      security: []
      parameters:
        - name: uid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: GIF details
          content:
            application/json:
              schema:
                type: object
                properties:
                  gif:
                    $ref: '#/components/schemas/GifResponse'
        '404':
          description: GIF not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

tags:
  - name: Images
    description: Image generation endpoints
  - name: GIFs
    description: GIF generation and capture
  - name: PDFs
    description: PDF generation
  - name: Templates
    description: Template management
  - name: Batch
    description: Batch rendering operations
  - name: Webhooks
    description: Webhook subscription management
  - name: Bindings
    description: Data binding for auto-rendering
