{
  "openapi": "3.1.0",
  "info": {
    "title": "PostNitro Embed API",
    "description": "REST API for programmatically generating social media carousels with PostNitro. Generation is asynchronous: initiate a post (AI generation or content import), poll its status until COMPLETED, then fetch the output files. Human-readable documentation: https://postnitro.ai/docs/embed/api (markdown: https://postnitro.ai/docs/embed/api.md, full docs index: https://postnitro.ai/docs/llms.txt). AI assistants can alternatively connect to the PostNitro MCP server at https://mcp.postnitro.ai/mcp (same API key as Bearer token) — see https://postnitro.ai/docs/embed/mcp.md",
    "version": "1.0.0",
    "contact": {
      "name": "PostNitro Support",
      "email": "support@postnitro.ai",
      "url": "https://postnitro.ai"
    }
  },
  "servers": [
    {
      "url": "https://embed-api.postnitro.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "embedApiKey": []
    }
  ],
  "paths": {
    "/post/initiate/generate": {
      "post": {
        "operationId": "initiateGenerate",
        "summary": "Initiate AI carousel generation",
        "description": "Generates a carousel post using AI. Returns immediately with an embedPostId while generation runs in the background; poll GET /post/status/{embedPostId} until status is COMPLETED, then call GET /post/output/{embedPostId}. AI generation costs 2 credits per slide. Docs: https://postnitro.ai/docs/embed/api/initiate/generate.md",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["postType", "templateId", "brandId", "presetId", "aiGeneration"],
                "properties": {
                  "postType": {
                    "type": "string",
                    "enum": ["CAROUSEL"],
                    "description": "Type of post to generate"
                  },
                  "requestorId": {
                    "type": "string",
                    "description": "Optional custom identifier for tracking which automation or user generated this post"
                  },
                  "templateId": {
                    "type": "string",
                    "description": "ID of the design template to use (from your PostNitro dashboard)"
                  },
                  "brandId": {
                    "type": "string",
                    "description": "ID of the brand configuration to apply (from your PostNitro dashboard)"
                  },
                  "presetId": {
                    "type": "string",
                    "description": "ID of the AI configuration preset (from your PostNitro dashboard)"
                  },
                  "responseType": {
                    "type": "string",
                    "enum": ["PDF", "PNG"],
                    "default": "PDF",
                    "description": "Output format: PNG produces one image per slide, PDF produces a single document"
                  },
                  "aiGeneration": {
                    "$ref": "#/components/schemas/AiGeneration"
                  }
                }
              },
              "example": {
                "postType": "CAROUSEL",
                "templateId": "your-template-id",
                "brandId": "your-brand-id",
                "presetId": "your-ai-preset-id",
                "responseType": "PNG",
                "aiGeneration": {
                  "type": "text",
                  "context": "Create a LinkedIn carousel about digital marketing tips",
                  "instructions": "Focus on actionable tips for small businesses"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generation initiated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InitiateResponse"
                }
              }
            }
          }
        }
      }
    },
    "/post/initiate/import": {
      "post": {
        "operationId": "initiateImport",
        "summary": "Initiate carousel creation from your own slide content",
        "description": "Creates a carousel post from caller-provided slide content. Slide order rules: exactly one starting_slide first, at least one body_slide in the middle, exactly one ending_slide last. Returns immediately with an embedPostId; poll GET /post/status/{embedPostId} until COMPLETED, then call GET /post/output/{embedPostId}. Content import costs 1 credit per slide. Docs: https://postnitro.ai/docs/embed/api/initiate/import.md",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["postType", "templateId", "brandId", "slides"],
                "properties": {
                  "postType": {
                    "type": "string",
                    "enum": ["CAROUSEL"],
                    "description": "Type of post to generate"
                  },
                  "requestorId": {
                    "type": "string",
                    "description": "Optional custom identifier for tracking"
                  },
                  "templateId": {
                    "type": "string",
                    "description": "ID of the design template to use"
                  },
                  "brandId": {
                    "type": "string",
                    "description": "ID of the brand configuration to apply"
                  },
                  "responseType": {
                    "type": "string",
                    "enum": ["PDF", "PNG"],
                    "default": "PDF",
                    "description": "Output format: PNG produces one image per slide, PDF produces a single document"
                  },
                  "slides": {
                    "type": "array",
                    "description": "Slides in display order: exactly 1 starting_slide (first), 1+ body_slide, exactly 1 ending_slide (last)",
                    "items": {
                      "$ref": "#/components/schemas/Slide"
                    }
                  }
                }
              },
              "example": {
                "postType": "CAROUSEL",
                "templateId": "your-template-id",
                "brandId": "your-brand-id",
                "responseType": "PNG",
                "slides": [
                  {
                    "type": "starting_slide",
                    "heading": "Welcome to the Carousel!",
                    "sub_heading": "My Awesome Subtitle",
                    "description": "This is how you start with a bang.",
                    "cta_button": "Swipe to learn more"
                  },
                  {
                    "type": "body_slide",
                    "heading": "Section 1: The Core Idea",
                    "description": "Explain your first key point here.",
                    "image": "https://example.com/image1.jpg"
                  },
                  {
                    "type": "ending_slide",
                    "heading": "Get Started Today!",
                    "description": "A final encouraging message.",
                    "cta_button": "Visit Our Website"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generation initiated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InitiateResponse"
                }
              }
            }
          }
        }
      }
    },
    "/post/status/{embedPostId}": {
      "get": {
        "operationId": "getPostStatus",
        "summary": "Check post generation status",
        "description": "Returns the current status (PENDING, COMPLETED, or FAILED) and processing logs for a post. Poll this after initiating until status is COMPLETED, then fetch the output. On FAILED, inspect the logs array for the failing step. Docs: https://postnitro.ai/docs/embed/api/request-status.md",
        "parameters": [
          {
            "name": "embedPostId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID returned by an initiate endpoint"
          }
        ],
        "responses": {
          "200": {
            "description": "Current status and logs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "embedPostId": {
                          "type": "string"
                        },
                        "embedPost": {
                          "$ref": "#/components/schemas/EmbedPost"
                        },
                        "logs": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PostLog"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/post/output/{embedPostId}": {
      "get": {
        "operationId": "getPostOutput",
        "summary": "Retrieve generated carousel files",
        "description": "Returns the output of a completed post. Call only after status is COMPLETED. For responseType PNG, result.data is an array of image URLs (one per slide); for PDF, result.data is a single document URL. Docs: https://postnitro.ai/docs/embed/api/post-output.md",
        "parameters": [
          {
            "name": "embedPostId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID returned by an initiate endpoint"
          }
        ],
        "responses": {
          "200": {
            "description": "Generated output files",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "embedPost": {
                          "$ref": "#/components/schemas/EmbedPost"
                        },
                        "result": {
                          "$ref": "#/components/schemas/PostResult"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "embedApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "embed-api-key",
        "description": "Embed API key generated from your PostNitro dashboard. See https://postnitro.ai/docs/embed/obtaining-an-api-key.md"
      }
    },
    "schemas": {
      "AiGeneration": {
        "type": "object",
        "required": ["type", "context"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["text", "article", "x"],
            "description": "Source type: 'text' = context is a plain-text topic/prompt, 'article' = context is an article URL, 'x' = context is an X (Twitter) post URL"
          },
          "context": {
            "type": "string",
            "description": "The prompt, article URL, or X post URL depending on type"
          },
          "instructions": {
            "type": "string",
            "description": "Optional additional instructions for the AI"
          }
        }
      },
      "Slide": {
        "type": "object",
        "required": ["type", "heading"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["starting_slide", "body_slide", "ending_slide"],
            "description": "Slide role. Exactly 1 starting_slide (must be first), at least 1 body_slide, exactly 1 ending_slide (must be last)"
          },
          "heading": {
            "type": "string",
            "description": "Main heading text"
          },
          "sub_heading": {
            "type": "string",
            "description": "Subtitle text"
          },
          "description": {
            "type": "string",
            "description": "Description / body text"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "description": "Image URL. Ignored when layoutType is 'infographic'"
          },
          "background_image": {
            "type": "string",
            "format": "uri",
            "description": "Background image URL"
          },
          "cta_button": {
            "type": "string",
            "description": "Call-to-action button text"
          },
          "layoutType": {
            "type": "string",
            "enum": ["default", "infographic"],
            "description": "'infographic' renders structured column data instead of the image"
          },
          "layoutConfig": {
            "$ref": "#/components/schemas/LayoutConfig"
          }
        }
      },
      "LayoutConfig": {
        "type": "object",
        "description": "Required when layoutType is 'infographic'",
        "required": ["columnCount", "columnDisplay", "displayCounterAs", "hasHeader"],
        "properties": {
          "columnCount": {
            "type": "integer",
            "enum": [1, 2, 3],
            "description": "Number of columns (max 3)"
          },
          "columnDisplay": {
            "type": "string",
            "enum": ["cycle", "grid"],
            "description": "'cycle' for sequential/step-by-step, 'grid' for comparative layout. With 'cycle', put ALL data in the first columnData entry only"
          },
          "displayCounterAs": {
            "type": "string",
            "enum": ["none", "counter"],
            "description": "Whether to show numbered counters"
          },
          "hasHeader": {
            "type": "boolean",
            "description": "Whether to show a header for each column"
          },
          "columnData": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["header", "content"],
              "properties": {
                "header": {
                  "type": "string",
                  "description": "Column header text"
                },
                "content": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": ["title", "description"],
                    "properties": {
                      "title": {
                        "type": "string"
                      },
                      "description": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "InitiateResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "message": {
            "type": "string",
            "example": "CAROUSEL generation initiated"
          },
          "data": {
            "type": "object",
            "properties": {
              "embedPostId": {
                "type": "string",
                "description": "Use this ID to poll status and fetch output"
              },
              "status": {
                "$ref": "#/components/schemas/PostStatus"
              }
            }
          }
        }
      },
      "EmbedPost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "postType": {
            "type": "string",
            "enum": ["CAROUSEL"]
          },
          "responseType": {
            "type": "string",
            "enum": ["PDF", "PNG"]
          },
          "status": {
            "$ref": "#/components/schemas/PostStatus"
          },
          "credits": {
            "type": "integer",
            "description": "Credits consumed by this post"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PostStatus": {
        "type": "string",
        "enum": ["PENDING", "COMPLETED", "FAILED"],
        "description": "PENDING = queued or generating (keep polling), COMPLETED = fetch output, FAILED = inspect status logs"
      },
      "PostLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "embedPostId": {
            "type": "string"
          },
          "step": {
            "type": "string",
            "description": "Processing step, e.g. INITIATED, PROCESSING, COMPLETED"
          },
          "status": {
            "type": "string",
            "description": "Step result, e.g. SUCCESS"
          },
          "message": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PostResult": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Design name (from template, or 'Untitled')"
          },
          "size": {
            "type": "string",
            "description": "Aspect ratio, e.g. '4:5'"
          },
          "type": {
            "type": "string",
            "enum": ["png", "pdf"]
          },
          "mimeType": {
            "type": "string",
            "enum": ["image/png", "application/pdf"]
          },
          "data": {
            "description": "PNG: array of image URLs (one per slide). PDF: a single document URL",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          }
        }
      }
    }
  }
}
