{
  "openapi": "3.1.0",
  "info": {
    "title": "Photo to Listing API",
    "description": "AI-powered API that analyzes product photos and generates optimized marketplace listings for 12+ platforms (Leboncoin, Vinted, eBay, Etsy, etc.). Upload base64-encoded photos and receive ready-to-publish listing titles, descriptions, prices, and attributes.",
    "version": "1.0.0",
    "contact": {
      "name": "Photo to Listing",
      "url": "https://phototolisting.com/developers"
    },
    "x-llm-description": "Use this API to convert product photos into marketplace listings. Send 1-4 base64 photos and specify target platforms. The AI analyzes the product, identifies attributes (brand, size, condition, color, material), suggests a price based on market data, and writes platform-optimized listing titles and descriptions."
  },
  "servers": [
    {
      "url": "https://phototolisting.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/v1/listings": {
      "post": {
        "operationId": "generateListings",
        "summary": "Generate marketplace listings from product photos",
        "description": "Upload 1-4 base64-encoded product photos and specify target marketplaces. The AI analyzes the photos, identifies the product, and generates optimized listing titles, descriptions, and prices for each platform.",
        "x-llm-hint": "Use this endpoint to create marketplace listings. Send product photos as base64 strings in the 'photos' array and specify which platforms you want listings for. The response includes ready-to-publish listings with titles, descriptions, prices, and detected product attributes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateRequest"
              },
              "example": {
                "photos": ["data:image/jpeg;base64,/9j/4AAQ..."],
                "platforms": ["leboncoin", "vinted"],
                "language": "fr",
                "tone": "friendly"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Listings generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — missing photos, invalid platform, or too many photos (max 4)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Missing or invalid \"photos\" field. Must be a non-empty array of base64-encoded images."
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing, invalid, or revoked API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Invalid or missing API key. Include X-API-Key header."
                }
              }
            }
          },
          "402": {
            "description": "Payment required — quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 402,
                  "message": "Quota exceeded. Upgrade your plan or purchase credits."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server error — AI generation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Check remaining quota and plan information",
        "description": "Returns the current user's usage statistics including free generations used, credits remaining, subscription status, and next reset date.",
        "x-llm-hint": "Call this endpoint to check how many generations the user has remaining before making a listings request. Useful for displaying quota information or deciding whether to proceed with a generation.",
        "responses": {
          "200": {
            "description": "Usage information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                },
                "example": {
                  "success": true,
                  "data": {
                    "freeUsed": 2,
                    "freeLimit": 3,
                    "creditsRemaining": 0,
                    "isSubscribed": false,
                    "planType": "free",
                    "canGenerate": true,
                    "nextResetDate": "2026-03-01T00:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key with 'ptl_' prefix. Create at https://phototolisting.com/account/api-keys"
      }
    },
    "schemas": {
      "GenerateRequest": {
        "type": "object",
        "required": ["photos", "platforms"],
        "properties": {
          "photos": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 4,
            "description": "Base64-encoded product images. Include data URI prefix (e.g., 'data:image/jpeg;base64,...')"
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": ["leboncoin", "vinted", "marketplace", "ebay", "etsy", "vestiaire", "selency", "poshmark", "mercari", "depop", "craigslist", "offerup"]
            },
            "minItems": 1,
            "description": "Target marketplace IDs for listing generation"
          },
          "language": {
            "type": "string",
            "enum": ["fr", "en"],
            "default": "fr",
            "description": "Language for generated listings"
          },
          "tone": {
            "type": "string",
            "enum": ["casual", "professional", "friendly"],
            "default": "friendly",
            "description": "Writing tone for listings"
          },
          "quality": {
            "type": "string",
            "enum": ["standard", "high"],
            "default": "standard",
            "description": "Generation quality — 'high' gives more detailed analysis"
          },
          "attributes": {
            "type": "object",
            "description": "Pre-fill known product attributes to improve listing quality",
            "properties": {
              "brand": {
                "type": "string",
                "description": "Product brand name"
              },
              "size": {
                "type": "string",
                "description": "Product size (e.g., 'M', '42', '10 US')"
              },
              "condition": {
                "type": "string",
                "description": "Product condition (e.g., 'new', 'good', 'fair')"
              },
              "color": {
                "type": "string",
                "description": "Primary color"
              },
              "material": {
                "type": "string",
                "description": "Primary material"
              }
            }
          }
        }
      },
      "GenerateResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "listings": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PlatformListing"
                },
                "description": "One listing per requested platform"
              },
              "attributes": {
                "type": "object",
                "description": "AI-detected product attributes",
                "properties": {
                  "name": { "type": "string" },
                  "brand": { "type": "string" },
                  "size": { "type": "string" },
                  "condition": { "type": "string" },
                  "color": { "type": "string" },
                  "material": { "type": "string" }
                }
              },
              "suggestedPrice": {
                "type": "number",
                "description": "AI-suggested price based on market data"
              },
              "priceRationale": {
                "type": "string",
                "description": "Explanation for the suggested price"
              }
            }
          },
          "usage": {
            "$ref": "#/components/schemas/UsageData"
          }
        }
      },
      "PlatformListing": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string",
            "description": "Platform ID this listing is for"
          },
          "title": {
            "type": "string",
            "description": "Listing title optimized for the platform"
          },
          "description": {
            "type": "string",
            "description": "Full listing description"
          },
          "price": {
            "type": "number",
            "description": "Suggested price in local currency"
          },
          "category": {
            "type": "string",
            "description": "Platform-specific category"
          },
          "condition": {
            "type": "string",
            "description": "Item condition label for the platform"
          }
        }
      },
      "UsageResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "$ref": "#/components/schemas/UsageData"
          }
        }
      },
      "UsageData": {
        "type": "object",
        "properties": {
          "freeUsed": {
            "type": "integer",
            "description": "Free generations used this month"
          },
          "freeLimit": {
            "type": "integer",
            "description": "Monthly free generation limit"
          },
          "creditsRemaining": {
            "type": "integer",
            "description": "Purchased credits remaining"
          },
          "isSubscribed": {
            "type": "boolean",
            "description": "Whether user has active Pro subscription"
          },
          "planType": {
            "type": "string",
            "description": "Current plan: 'free', 'pro_monthly', or 'pro_annual'"
          },
          "canGenerate": {
            "type": "boolean",
            "description": "Whether the user can make another generation request"
          },
          "nextResetDate": {
            "type": "string",
            "format": "date-time",
            "description": "When free quota resets (ISO 8601)"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}
