{"components":{"headers":{"RateLimit-Limit":{"description":"Requests allowed in the current window (RFC 9349)","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window (RFC 9349)","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets (RFC 9349)","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying (RFC 9110, on 429/503)","schema":{"type":"integer"}}},"schemas":{"Problem":{"description":"RFC 7807 problem details — every API error body","properties":{"code":{"description":"Stable machine-readable error code","type":"string"},"detail":{"description":"Human-readable explanation","type":"string"},"instance":{"type":["string","null"]},"status":{"description":"HTTP status code","type":"integer"},"title":{"type":"string"},"type":{"description":"Error-type URL: https://glowdollars.com/api/errors/<code>","type":"string"}},"type":"object"}},"securitySchemes":{"PartnerApiKey":{"description":"Partner API key from Dashboard → API (`gdp_…` prefix). Server-side use only — browser Origins are rejected.","scheme":"bearer","type":"http"},"SessionCookie":{"description":"Dashboard session cookie (sign in at https://glowdollars.com/sign-in). Used only by key-management endpoints.","in":"cookie","name":"__Secure-better-auth.session_token","type":"apiKey"}}},"info":{"description":"Public API surface of glowDOLLARS, the B2B adult affiliate network (https://glowdollars.com).\n\n**Audience.** Partners (approved webmasters/affiliates) building promo sites who need catalog data for the paysites they follow, and anyone health-checking the service. Consumer-facing content questions are not served here.\n\n**Authentication.** Catalog endpoints use a partner API key: create one in the dashboard under Dashboard → API (`gdp_…` prefix, plaintext shown once), then send `Authorization: Bearer gdp_…`. Keys are granted once your partner application is approved. The API is server-side only — requests carrying an `Origin` header (browsers) are rejected. Key management endpoints (`/api/v1/keys*`) use the dashboard session cookie instead.\n\n**Rate limits.** 30 requests/minute per API key and 2,000 requests/day per partner (admin-raisable), plus a 120 requests/minute pre-auth cap per IP. Exceeding a limit returns `429` with `Retry-After` (seconds). Every catalog response carries `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` (RFC 9349) so you can self-throttle in real time.\n\n**Errors.** All API errors are RFC 7807 `application/problem+json` with a stable machine-readable `code` (`unauthorized`, `forbidden`, `not_approved`, `not_found`, `rate_limited`, `validation_failed`, `cors_not_allowed`, `service_unavailable`, `internal_error`, `test_mode_not_supported`), a human `detail`, and a `type` URL of the form `https://glowdollars.com/api/errors/<code>`. Unknown `/api/*` paths return a problem+json 404 pointing here.\n\n**Test mode.** Keys created with `mode: \"test\"` return obviously-fake fixture data on catalog endpoints — safe for integration development.\n\n**Human-readable docs.** https://glowdollars.com/docs — also available as markdown via `Accept: text/markdown` content negotiation on that route.","title":"glowDOLLARS Partner API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/api/health":{"get":{"description":"Public liveness check. No authentication.","responses":{"200":{"content":{"application/json":{"example":{"status":"ok"}}},"description":"Service is up"}},"summary":"Health check","tags":["Public"]}},"/api/v1/keys":{"get":{"description":"List your API keys (prefix + metadata only — plaintext is never returned again). Dashboard-session authenticated; agents should manage keys in the dashboard at /dashboard/api.","responses":{"200":{"content":{"application/json":{"example":{"keys":[{"created_at":1786000000,"id":1,"key_prefix":"gdp_live_ab12","mode":"live","name":"my promo site"}],"max":5}}},"description":"Key list (session required)"},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"429":{"content":{"application/json":{"example":{"error":"rate_limited"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"security":[{"SessionCookie":[]}],"summary":"List API keys (dashboard session)","tags":["Keys"]},"post":{"description":"Create an API key (max 5 active). Requires same-origin + dashboard session and an approved partner account. The plaintext key is returned exactly once.","requestBody":{"content":{"application/json":{"example":{"mode":"live","name":"my promo site"},"schema":{"properties":{"mode":{"default":"live","enum":["live","test"],"type":"string"},"name":{"maxLength":64,"minLength":1,"type":"string"}},"required":["name"],"type":"object"}}}},"responses":{"201":{"content":{"application/json":{"example":{"key":{"created_at":1786000000,"id":2,"key_prefix":"gdp_live_cd34","mode":"live","name":"my promo site"},"plaintext_key":"gdp_live_…shown once…"}}},"description":"Key created — plaintext_key is shown exactly once"},"400":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"403":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"409":{"content":{"application/json":{"example":{"error":"max_keys_reached","max":5}}},"description":"Active-key cap (5) reached — revoke one first"},"429":{"content":{"application/json":{"example":{"error":"rate_limited"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"security":[{"SessionCookie":[]}],"summary":"Create an API key (dashboard session)","tags":["Keys"]}},"/api/v1/keys/{id}":{"delete":{"description":"Revoke an API key by id. Dashboard-session + same-origin.","parameters":[{"description":"Numeric key id","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"ok":true}}},"description":"Key revoked (or was already revoked)"},"400":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"403":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"404":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"429":{"content":{"application/json":{"example":{"error":"rate_limited"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"security":[{"SessionCookie":[]}],"summary":"Revoke an API key (dashboard session)","tags":["Keys"]}},"/api/v1/models/{id}":{"get":{"description":"Model profile with scene_count for your followed sites.","parameters":[{"description":"Numeric model id","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"avatar_r2_key":"models/11/avatar.jpg","bio":"Model bio","id":900011,"name":"Test Model","profile_url":null,"scene_count":2,"site":{"id":900001,"name":"Test Site","slug":"test-site.example"}},"meta":{}}}},"description":"Model detail","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}}},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"403":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"404":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"429":{"content":{"application/problem+json":{"example":{"code":"rate_limited","detail":"Per-key rate limit reached. Retry after the cooldown.","instance":null,"retry_after":30,"scope":"key","status":429,"title":"Rate limit exceeded","type":"https://glowdollars.com/api/errors/rate_limited"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"500":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"503":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"}},"security":[{"PartnerApiKey":[]}],"summary":"Get model detail","tags":["Catalog"]}},"/api/v1/scenes":{"get":{"description":"List published scenes across your followed sites. Scope required: `catalog:read` (granted to every new key).","parameters":[{"description":"Filter by site slug","in":"query","name":"site","required":false,"schema":{"type":"string"}},{"description":"Page size (1-100)","in":"query","name":"limit","required":false,"schema":{"default":25,"maximum":100,"minimum":1,"type":"integer"}},{"description":"Pagination offset (>= 0)","in":"query","name":"offset","required":false,"schema":{"default":0,"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"id":900001,"length_seconds":1234,"publish_date":1700000000,"site":{"id":900001,"name":"Test Site","slug":"test-site.example"},"title":"Test Scene"}],"meta":{"has_more":false,"limit":25}}}},"description":"Scene page — envelope { data: Scene[], meta: { limit, has_more } }","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}}},"400":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"403":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"429":{"content":{"application/problem+json":{"example":{"code":"rate_limited","detail":"Per-key rate limit reached. Retry after the cooldown.","instance":null,"retry_after":30,"scope":"key","status":429,"title":"Rate limit exceeded","type":"https://glowdollars.com/api/errors/rate_limited"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"500":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"503":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"}},"security":[{"PartnerApiKey":[]}],"summary":"List scenes for your followed sites","tags":["Catalog"]}},"/api/v1/scenes/{id}":{"get":{"description":"Full detail for one published scene (media R2 keys included).","parameters":[{"description":"Numeric scene id","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"description":"Scene description","gallery_zip_r2_key":"scenes/1/gallery.zip","hover_preview_r2_key":"scenes/1/hover.mp4","id":900001,"length_seconds":1234,"models":[{"id":900011,"name":"Test Model","profile_url":null}],"poster_r2_key":"scenes/1/poster.jpg","publish_date":1700000000,"site":{"id":900001,"name":"Test Site","slug":"test-site.example"},"title":"Test Scene","trailer_r2_key":"scenes/1/trailer.mp4"},"meta":{}}}},"description":"Scene detail","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}}},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"403":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"404":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"429":{"content":{"application/problem+json":{"example":{"code":"rate_limited","detail":"Per-key rate limit reached. Retry after the cooldown.","instance":null,"retry_after":30,"scope":"key","status":429,"title":"Rate limit exceeded","type":"https://glowdollars.com/api/errors/rate_limited"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"500":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"503":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"}},"security":[{"PartnerApiKey":[]}],"summary":"Get scene detail","tags":["Catalog"]}},"/api/v1/sites":{"get":{"description":"The paysites you follow (id, name, slug, brand_color).","responses":{"200":{"content":{"application/json":{"example":{"data":[{"brand_color":"#ff00ff","id":900001,"name":"Test Site","slug":"test-site.example"}],"meta":{"has_more":false,"limit":100}}}},"description":"Followed sites","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}}},"401":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"403":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"429":{"content":{"application/problem+json":{"example":{"code":"rate_limited","detail":"Per-key rate limit reached. Retry after the cooldown.","instance":null,"retry_after":30,"scope":"key","status":429,"title":"Rate limit exceeded","type":"https://glowdollars.com/api/errors/rate_limited"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Rate limit exceeded — see Retry-After and the RateLimit-* headers","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"500":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"},"503":{"content":{"application/problem+json":{"example":{"code":"not_found","detail":"Scene was not found or is not available to your account.","instance":null,"status":404,"title":"Not found","type":"https://glowdollars.com/api/errors/not_found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error — RFC 7807 problem JSON (application/problem+json)"}},"security":[{"PartnerApiKey":[]}],"summary":"List followed sites","tags":["Catalog"]}}},"servers":[{"url":"https://glowdollars.com"}],"tags":[{"description":"Partner catalog data — Bearer API key, scope catalog:read","name":"Catalog"},{"description":"API key lifecycle — dashboard session cookie","name":"Keys"},{"description":"Unauthenticated endpoints","name":"Public"}]}