{"openapi":"3.1.0","info":{"title":"Listing Assistant API","version":"1.0.0","description":"Lightweight, serverless API for generating platform-optimized listing titles, tags, and image analysis\nfor marketplace listings (Etsy, eBay, Vinted).\n\n## Authentication\n\nMost endpoints require an API key via the `x-api-key` header. Admin endpoints require `x-admin-key`.\n\n## Rate Limits\n\n- FREE: 60 requests per 10 minutes, 20 runs per day\n- PRO: 600 requests per 10 minutes, 200 runs per day\n- BUSINESS: 6,000 requests per 10 minutes, 2000 runs per day\n\n## Correlation IDs\n\nSend `x-correlation-id` header for request tracing, or one will be auto-generated.\n","contact":{"name":"API Support"},"license":{"name":"ISC"}},"servers":[{"url":"https://listinghelper.kreativschicht.de","description":"Production server"}],"tags":[{"name":"analyze","description":"Product analysis and optimization"},{"name":"account","description":"API key account management"},{"name":"billing","description":"Subscription and billing management"},{"name":"admin","description":"Administrative operations (requires admin key)"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"API key for authenticating user requests"},"AdminKeyAuth":{"type":"apiKey","in":"header","name":"x-admin-key","description":"Admin key for administrative operations"}},"schemas":{"ProductDraft":{"type":"object","properties":{"title":{"type":"string","example":"Handmade Leather Wallet"},"description":{"type":"string","example":"Premium quality leather wallet"},"category":{"type":"string","example":"Accessories"},"brand":{"type":"string","example":"Artisan Goods"},"condition":{"type":"string","example":"New"},"price":{"type":"number","format":"float","example":49.99},"color":{"type":"string","example":"Brown"},"size":{"type":"string","example":"Standard"},"material":{"type":"string","example":"Leather"}},"additionalProperties":true},"Platform":{"type":"string","enum":["etsy","ebay","vinted"]},"PlanTier":{"type":"string","enum":["FREE","PRO","BUSINESS"]},"FeatureFlags":{"type":"object","required":["enableTitles","enableTags","enableImageChecks"],"properties":{"enableTitles":{"type":"boolean"},"enableTags":{"type":"boolean"},"enableImageChecks":{"type":"boolean"}}},"AnalyzeRequest":{"type":"object","required":["productDraft","platforms","flags"],"properties":{"productDraft":{"$ref":"#/components/schemas/ProductDraft"},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/Platform"},"minItems":1},"flags":{"$ref":"#/components/schemas/FeatureFlags"},"images":{"type":"array","items":{"type":"string","format":"uri"},"description":"Optional array of image URLs to analyze"}}},"TitleSuggestion":{"type":"object","required":["platform","title","score","reasons"],"properties":{"platform":{"$ref":"#/components/schemas/Platform"},"title":{"type":"string"},"score":{"type":"number","format":"int32","description":"Score from 0-120"},"reasons":{"type":"array","items":{"type":"string"}},"issues":{"type":"array","items":{"type":"string"}}}},"TagGrouped":{"type":"object","properties":{"material":{"type":"array","items":{"type":"string"}},"style":{"type":"array","items":{"type":"string"}},"usecase":{"type":"array","items":{"type":"string"}},"audience":{"type":"array","items":{"type":"string"}},"occasion":{"type":"array","items":{"type":"string"}},"generic":{"type":"array","items":{"type":"string"}}}},"TagSuggestion":{"type":"object","required":["platform","tags","normalized","tagsCommaSeparated"],"properties":{"platform":{"$ref":"#/components/schemas/Platform"},"tags":{"type":"array","items":{"type":"string"}},"normalized":{"type":"array","items":{"type":"string"}},"tagsCommaSeparated":{"type":"string","description":"Tags as comma-separated string for easy copying"},"grouped":{"$ref":"#/components/schemas/TagGrouped"}}},"ImageMetrics":{"type":"object","required":["width","height","aspectRatio","fileSizeBytes","brightness","blurScore","hasWatermarkLikely"],"properties":{"width":{"type":"integer"},"height":{"type":"integer"},"aspectRatio":{"type":"number","format":"float"},"fileSizeBytes":{"type":"integer"},"brightness":{"type":"number","format":"float"},"blurScore":{"type":"number","format":"float"},"hasWatermarkLikely":{"type":"boolean"}}},"ImageIssue":{"type":"object","required":["severity","message"],"properties":{"severity":{"type":"string","enum":["error","warning","info"]},"message":{"type":"string"},"suggestedFix":{"type":"string"}}},"ImageReport":{"type":"object","required":["url","hash","metrics","issues"],"properties":{"url":{"type":"string","format":"uri"},"hash":{"type":"string","description":"SHA-256 hash of image"},"metrics":{"$ref":"#/components/schemas/ImageMetrics"},"issues":{"type":"array","items":{"$ref":"#/components/schemas/ImageIssue"}}}},"CacheDiagnostics":{"type":"object","properties":{"hit":{"type":"boolean"},"keyPrefix":{"type":"string"},"ageSeconds":{"type":"integer"}}},"Diagnostics":{"type":"object","required":["timings","rulesVersion"],"properties":{"timings":{"type":"object","required":["total"],"properties":{"total":{"type":"integer","description":"Total time in milliseconds"},"titles":{"type":"integer"},"tags":{"type":"integer"},"images":{"type":"integer"}}},"rulesVersion":{"type":"string"},"cache":{"$ref":"#/components/schemas/CacheDiagnostics"},"llmWarning":{"type":"string","description":"Warning message when LLM is disabled due to budget"}}},"UsageInfo":{"type":"object","properties":{"runsToday":{"type":"integer"},"runsRemaining":{"type":"integer"},"plan":{"$ref":"#/components/schemas/PlanTier"}}},"AnalyzeResponse":{"type":"object","required":["titles","tags","imageReport","diagnostics"],"properties":{"titles":{"type":"array","items":{"$ref":"#/components/schemas/TitleSuggestion"}},"tags":{"type":"array","items":{"$ref":"#/components/schemas/TagSuggestion"}},"imageReport":{"type":"array","items":{"$ref":"#/components/schemas/ImageReport"}},"diagnostics":{"$ref":"#/components/schemas/Diagnostics"},"usage":{"$ref":"#/components/schemas/UsageInfo"}}},"ApiKeyInfo":{"type":"object","required":["id","keyPrefix","name","plan","createdAt","isActive"],"properties":{"id":{"type":"string","example":"key_1234567890_abcdef1234567890"},"keyPrefix":{"type":"string","example":"ak_12345678..."},"name":{"type":"string","example":"Production Key"},"plan":{"$ref":"#/components/schemas/PlanTier"},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":"string","format":"date-time"},"isActive":{"type":"boolean"}}},"UsageRecord":{"type":"object","properties":{"date":{"type":"string","format":"date"},"runs":{"type":"integer"},"tokensUsed":{"type":"integer"}}},"UsageResponse":{"type":"object","required":["success","apiKey","usage"],"properties":{"success":{"type":"boolean"},"apiKey":{"$ref":"#/components/schemas/ApiKeyInfo"},"usage":{"type":"array","items":{"$ref":"#/components/schemas/UsageRecord"}},"summary":{"type":"object","properties":{"totalRuns":{"type":"integer"},"totalTokens":{"type":"integer"},"daysQueried":{"type":"integer"}}}}},"RotateKeyResponse":{"type":"object","required":["success","apiKey","keyId","keyPrefix","message"],"properties":{"success":{"type":"boolean"},"apiKey":{"type":"string","description":"New API key (shown only once)","example":"ak_newkey567890abcdef..."},"keyId":{"type":"string"},"keyPrefix":{"type":"string"},"message":{"type":"string"}}},"CreateKeyRequest":{"type":"object","required":["name","plan"],"properties":{"name":{"type":"string","example":"Production Key"},"plan":{"$ref":"#/components/schemas/PlanTier"}}},"CreateKeyResponse":{"type":"object","required":["success","apiKey","keyId","keyPrefix","plan","name","message"],"properties":{"success":{"type":"boolean"},"apiKey":{"type":"string","description":"API key (shown only once)"},"keyId":{"type":"string"},"keyPrefix":{"type":"string"},"plan":{"$ref":"#/components/schemas/PlanTier"},"name":{"type":"string"},"message":{"type":"string"}}},"ListKeysResponse":{"type":"object","required":["success","keys","count"],"properties":{"success":{"type":"boolean"},"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyInfo"}},"count":{"type":"integer"}}},"CheckoutRequest":{"type":"object","required":["plan"],"properties":{"plan":{"type":"string","enum":["PRO","BUSINESS"]}}},"CheckoutResponse":{"type":"object","required":["success","checkoutUrl","sessionId"],"properties":{"success":{"type":"boolean"},"checkoutUrl":{"type":"string","format":"uri"},"sessionId":{"type":"string"}}},"PortalResponse":{"type":"object","required":["success","portalUrl"],"properties":{"success":{"type":"boolean"},"portalUrl":{"type":"string","format":"uri"}}},"ErrorResponse":{"type":"object","required":["error","code","message"],"properties":{"error":{"type":"string","example":"Bad Request"},"code":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true},"retryAfterSeconds":{"type":"integer","description":"Present for rate limit errors (429)"},"requestCount":{"type":"integer","description":"Present for rate limit errors (429)"},"limit":{"type":"integer","description":"Present for rate limit errors (429)"}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Unauthorized","code":"INVALID_API_KEY","message":"Invalid or expired API key"}}}},"PaymentRequired":{"description":"Plan limits exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Payment Required","code":"PLAN_LIMIT_EXCEEDED","message":"Your plan allows maximum 1 platform per request. You requested 2."}}}},"TooManyRequests":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Too Many Requests","code":"RATE_LIMIT_EXCEEDED","message":"Rate limit exceeded. You have made 61 requests in the current 10-minute window.","retryAfterSeconds":420,"requestCount":61,"limit":60}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying"}}},"InternalServerError":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Internal Server Error","code":"INTERNAL_ERROR","message":"An unexpected error occurred"}}}}},"parameters":{"CorrelationId":{"name":"x-correlation-id","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional correlation ID for request tracing"}}},"paths":{"/api/analyze":{"post":{"tags":["analyze"],"summary":"Analyze product draft","description":"Generate platform-optimized titles, tags, and image analysis for a product draft.\n\nThis endpoint is the core of the Listing Assistant. It takes product information,\nplatform targets, and feature flags, then returns optimized suggestions.\n","operationId":"analyzeProduct","security":[{"ApiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CorrelationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeRequest"},"example":{"productDraft":{"title":"Handmade Leather Wallet","description":"Premium quality leather wallet","category":"Accessories","brand":"Artisan Goods","condition":"New","price":49.99},"platforms":["etsy","ebay"],"flags":{"enableTitles":true,"enableTags":true,"enableImageChecks":false},"images":[]}}}},"responses":{"200":{"description":"Successful analysis","headers":{"X-Correlation-Id":{"schema":{"type":"string"},"description":"Correlation ID for this request"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeResponse"}}}},"400":{"description":"Bad request - validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/me":{"get":{"tags":["account"],"summary":"Get API key information","description":"Returns information about the authenticated API key (plan, creation date, usage stats)","operationId":"getMe","security":[{"ApiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CorrelationId"}],"responses":{"200":{"description":"API key information","content":{"application/json":{"schema":{"type":"object","required":["success","apiKey"],"properties":{"success":{"type":"boolean"},"apiKey":{"$ref":"#/components/schemas/ApiKeyInfo"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/me/usage":{"get":{"tags":["account"],"summary":"Get usage statistics","description":"Returns usage statistics for the authenticated API key over a specified time period","operationId":"getUsage","security":[{"ApiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CorrelationId"},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":90,"default":30},"description":"Number of days to retrieve usage for (max 90)"}],"responses":{"200":{"description":"Usage statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/me/rotate-key":{"post":{"tags":["account"],"summary":"Rotate API key","description":"Generates a new API key and invalidates the current one.\nThe new key is returned in the response and must be saved immediately.\n","operationId":"rotateKey","security":[{"ApiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CorrelationId"}],"responses":{"200":{"description":"Key rotated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateKeyResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/billing/checkout":{"post":{"tags":["billing"],"summary":"Create checkout session","description":"Creates a Stripe Checkout session to upgrade the API key to PRO or BUSINESS plan.\nReturns a URL to redirect the user to complete payment.\n","operationId":"createCheckout","security":[{"ApiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CorrelationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"},"example":{"plan":"PRO"}}}},"responses":{"200":{"description":"Checkout session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"400":{"description":"Invalid plan or already subscribed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/billing/portal":{"post":{"tags":["billing"],"summary":"Create customer portal session","description":"Creates a Stripe Customer Portal session for managing subscriptions.\nReturns a URL to redirect the user to the portal.\n","operationId":"createPortal","security":[{"ApiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/CorrelationId"}],"responses":{"200":{"description":"Portal session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalResponse"}}}},"400":{"description":"No active subscription found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/billing/webhook":{"post":{"tags":["billing"],"summary":"Stripe webhook endpoint","description":"**Server-to-server only** - This endpoint is called by Stripe, not by clients.\n\nHandles Stripe webhook events for subscription lifecycle management.\nRequires valid Stripe signature in the `stripe-signature` header.\n","operationId":"handleWebhook","parameters":[{"name":"stripe-signature","in":"header","required":true,"schema":{"type":"string"},"description":"Stripe webhook signature for verification"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Stripe event payload"}}}},"responses":{"200":{"description":"Webhook processed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}}}}}},"400":{"description":"Invalid signature or payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/admin/keys":{"post":{"tags":["admin"],"summary":"Create new API key","description":"Creates a new API key with specified plan.\n**Admin only** - requires x-admin-key header.\n","operationId":"createApiKey","security":[{"AdminKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}}},"responses":{"200":{"description":"API key created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}}},"401":{"description":"Invalid or missing admin key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}},"get":{"tags":["admin"],"summary":"List all API keys","description":"Lists all API keys (without secrets).\n**Admin only** - requires x-admin-key header.\n","operationId":"listApiKeys","security":[{"AdminKeyAuth":[]}],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListKeysResponse"}}}},"401":{"description":"Invalid or missing admin key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/admin/keys/rotate":{"post":{"tags":["admin"],"summary":"Rotate API key","description":"Rotates an existing API key (invalidates old, generates new).\n**Admin only** - requires x-admin-key header.\n","operationId":"adminRotateKey","security":[{"AdminKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keyId"],"properties":{"keyId":{"type":"string","example":"key_1234567890_abcdef1234567890"}}}}}},"responses":{"200":{"description":"Key rotated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateKeyResponse"}}}},"401":{"description":"Invalid or missing admin key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"API key not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/openapi":{"get":{"tags":["documentation"],"summary":"Get OpenAPI specification","description":"Returns the OpenAPI 3.1 specification in YAML or JSON format","operationId":"getOpenApiSpec","parameters":[{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["yaml","json"],"default":"yaml"},"description":"Response format (yaml or json)"}],"responses":{"200":{"description":"OpenAPI specification","content":{"application/x-yaml":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}}}}