{"openapi":"3.1.0","info":{"title":"StyleRef Public API","version":"1.4.0","summary":"Fetch and apply reusable creative styles (visual + voice) published on StyleRef.","description":"Two tiers. Anonymous: search StyleRef's public gallery, compile any style into a ready-to-use spec for ChatGPT, Claude, Gemini, Midjourney, FLUX or Stable Diffusion, download a STYLE.md, and lint STYLE.md documents. Authenticated (OAuth 2.1 bearer): extract a new style from an image you upload and manage your own library. The same capabilities are exposed over MCP at https://styleref.io/api/mcp — see https://styleref.io/mcp.","contact":{"name":"StyleRef","url":"https://styleref.io/mcp","email":"support@styleref.io"},"termsOfService":"https://styleref.io/legal/terms"},"servers":[{"url":"https://styleref.io/api/v1"}],"paths":{"/styles":{"get":{"operationId":"searchStyles","summary":"Search the public style gallery","description":"Free-text search over StyleRef's public gallery of reusable creative styles. Feed a result's slug to /styles/{ref} to get the compiled spec.","parameters":[{"name":"query","in":"query","schema":{"type":"string","maxLength":200},"description":"Aesthetic, mood, discipline or keywords (e.g. \"warm editorial\")."},{"name":"category","in":"query","schema":{"type":"string","maxLength":80},"description":"Gallery discipline/category filter."},{"name":"sort","in":"query","schema":{"type":"string","enum":["popular","recent"]},"description":"Ordering; defaults to recent."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":25,"default":8}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":200,"default":0},"description":"Rows to skip. Pass back nextOffset from the previous page; results are ranked in memory, so paging slices one ranked list rather than keying on a column."}],"responses":{"200":{"description":"Matching styles (possibly empty, with availableCategories as a retry hint).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResults"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/styles/{ref}":{"get":{"operationId":"getStyle","summary":"Get a style compiled to a ready-to-use spec","security":[{},{"bearerAuth":[]}],"description":"Resolves a style reference and returns the compiled spec. Prepend the spec to any AI prompt as a hard constraint. format=json returns structured JSON; every other format returns text/markdown. A ref resolving to your OWN style compiles from that style's last generation on styleref.io, not its live blocks — edits reach the API after you Generate on the web (gallery styles are already frozen published snapshots and are unaffected). Supports ETag / If-None-Match revalidation.","parameters":[{"$ref":"#/components/parameters/StyleRefPath"},{"name":"format","in":"query","schema":{"type":"string","enum":["default","flux","midjourney","diffusion","stylemd","json"],"default":"default"},"description":"Output dialect: default = prose for ChatGPT/Claude/Gemini; flux/midjourney/diffusion for image tools; stylemd = STYLE.md; json = structured."},{"name":"sections","in":"query","schema":{"type":"string","maxLength":400},"description":"Comma-separated section ids (e.g. \"colors,typography\") to cut tokens."},{"name":"compact","in":"query","schema":{"type":"string","enum":["1","true","0","false"]},"description":"Strip boilerplate preamble/enforcement text to save tokens."},{"name":"raw","in":"query","schema":{"type":"string","enum":["1","true","0","false"]},"description":"Omit the metadata header/attribution wrapper and return exactly the compiled spec — for piping directly into an image tool."}],"responses":{"200":{"description":"The compiled spec. X-StyleRef-Canonical-Url carries the public page; X-Token-Count an approximate token size.","content":{"text/markdown":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"description":"The style exists but is private or pending moderation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/NotGenerated"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/styles/{ref}/style.md":{"get":{"operationId":"getStyleMd","summary":"Get a complete STYLE.md for a style","security":[{},{"bearerAuth":[]}],"description":"Returns a ready-to-commit STYLE.md body. Write it to STYLE.md in the project root and reference it from AGENTS.md so coding agents apply the style.","parameters":[{"$ref":"#/components/parameters/StyleRefPath"}],"responses":{"200":{"description":"The STYLE.md document.","content":{"text/markdown":{"schema":{"type":"string"}}}},"403":{"description":"The style exists but is private or pending moderation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/NotGenerated"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/style-md/validate":{"post":{"operationId":"validateStyleMd","summary":"Lint a STYLE.md document","description":"Validates a STYLE.md document against the format StyleRef emits: frontmatter (name/generator/sections), per-section body rules, hex-color syntax.","requestBody":{"required":true,"content":{"text/markdown":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}}}},"responses":{"200":{"description":"Lint result (a failing document still returns 200).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LintResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/extractions":{"post":{"operationId":"createExtraction","summary":"Extract a style from an uploaded image","security":[{"bearerAuth":[]}],"description":"Run StyleRef's AI extraction on an image you hold locally (no public URL needed) to derive a structured style — colors, typography, mood, lighting, composition. Asynchronous: returns an extraction id to poll at /extractions/{id}. Consumes extraction credits from the connected account. Requires the \"extract\" scope.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"The source image, max 8 MB. JPEG, PNG, GIF or WebP — the format is detected from file contents, not the declared Content-Type."},"name":{"type":"string","maxLength":120,"description":"Optional draft name; defaults to the AI-suggested title."},"text":{"type":"string","maxLength":6000,"description":"Optional extra description fused into the extraction alongside the image."}},"required":["image"]}}}},"responses":{"202":{"description":"Extraction accepted and running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionStarted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AuthRequired"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"413":{"description":"The image exceeds the 8 MB limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/extractions/{id}":{"get":{"operationId":"getExtraction","summary":"Poll an extraction and optionally save the result","security":[{"bearerAuth":[]}],"description":"Poll an extraction started by createExtraction. While running, returns status \"running\". On completion returns the compiled spec inline, or — with save_as_draft=true — saves it as a draft style in your library and returns an edit link. Saving is idempotent: polling twice with the flag will not create two drafts.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The extractionId returned by createExtraction."},{"name":"save_as_draft","in":"query","schema":{"type":"boolean","default":false},"description":"Save the completed result into your library as a draft (consumes a create_style allowance)."},{"name":"name","in":"query","schema":{"type":"string","maxLength":120},"description":"Draft name; defaults to the AI-suggested title."}],"responses":{"200":{"description":"Current state: running, failed, or complete (spec inline, or saved style when save_as_draft=true).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionStatus"}}}},"401":{"$ref":"#/components/responses/AuthRequired"},"402":{"description":"Saving as a draft is not available on the caller's plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No extraction with that id belongs to the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/styles/{ref}/save":{"post":{"operationId":"saveStyle","summary":"Save a gallery style to your library","security":[{"bearerAuth":[]}],"description":"Save (like) a public gallery style so it appears under My Styles → Saved. Idempotent — saving an already-saved style succeeds with alreadySaved: true. Requires the \"styles:write\" scope.","parameters":[{"$ref":"#/components/parameters/StyleRefPath"}],"responses":{"200":{"description":"The style is in the caller's library.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveResult"}}}},"401":{"$ref":"#/components/responses/AuthRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/me/styles":{"get":{"operationId":"listMyStyles","summary":"List the styles in your own library","security":[{"bearerAuth":[]}],"description":"The connected account's own library — including private styles. Each item carries a ref (share slug, or id when the style has no active link) usable with getStyle. Requires the \"styles:read\" scope.","parameters":[{"name":"query","in":"query","schema":{"type":"string","maxLength":200},"description":"Narrow the library to styles whose name contains this text (case-insensitive substring)."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","schema":{"type":"string","maxLength":120},"description":"Opaque cursor — pass back nextCursor from the previous page."}],"responses":{"200":{"description":"A page of the caller's styles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyStyles"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AuthRequired"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/me/saved-styles":{"get":{"operationId":"listSavedStyles","summary":"List the gallery styles you saved","security":[{"bearerAuth":[]}],"description":"The sibling of listMyStyles: styles you *saved* from the gallery rather than authored. Same card shape as searchStyles, so a picker can render \"Saved\" as a third tab next to Recent and Popular. Requires the \"styles:read\" scope.","parameters":[{"name":"query","in":"query","schema":{"type":"string","maxLength":200}},{"name":"category","in":"query","schema":{"type":"string","maxLength":80}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":25,"default":8}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":500,"default":0},"description":"Rows to skip; pass back nextOffset from the previous page."}],"responses":{"200":{"description":"A page of saved styles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResults"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AuthRequired"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/me/extractions":{"get":{"operationId":"listMyExtractions","summary":"List your past extractions","security":[{"bearerAuth":[]}],"description":"Metadata for extractions you have already run — so you can re-read a result instead of paying to extract the same source twice. Free plan: pass ?style= to read one style's history (the newest entry is openable, older ones are listed with locked: true); the cross-style archive is Pro. Requires \"styles:read\".","parameters":[{"name":"style","in":"query","schema":{"type":"string","format":"uuid"},"description":"Limit to one style (its id, from listMyStyles)."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","schema":{"type":"string","maxLength":120},"description":"Opaque cursor — pass back nextCursor from the previous page."}],"responses":{"200":{"description":"A page of extraction history entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionHistory"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AuthRequired"},"403":{"$ref":"#/components/responses/PlanLimited"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/me/extractions/{id}":{"get":{"operationId":"getMyExtraction","summary":"Read one past extraction, with its blocks","security":[{"bearerAuth":[]}],"description":"The full block set of an extraction you already own — free to re-read, no credits. Blocks are re-capped against your CURRENT plan, so upgrading retroactively unlocks items withheld at extraction time. Requires \"styles:read\".","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"format","in":"query","schema":{"type":"string","enum":["blocks","default","stylemd"],"default":"blocks"},"description":"Also compile the usable blocks into a spec (default) or a STYLE.md."}],"responses":{"200":{"description":"The stored extraction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionHistoryEntry"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/AuthRequired"},"403":{"$ref":"#/components/responses/PlanLimited"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A StyleRef OAuth 2.1 access token. Optional on the gallery read endpoints, where it additionally makes your own private styles resolvable (by their share slug, id, or name) with the styles:read scope. Required on /extractions (scope: extract) and /me/styles (scope: styles:read). Get one via the MCP OAuth flow or `npx styleref login` — see https://styleref.io/mcp. Anonymous requests keep working for public styles."}},"parameters":{"StyleRefPath":{"name":"ref","in":"path","required":true,"schema":{"type":"string"},"description":"A style reference — always an opaque id or a link containing one. Accepted: a share slug (\"1an6edgp-c42c0cba5315\"), a styleref.io /share URL, or, for your own styles with a bearer token, the style's UUID id or its /styles/{id} builder URL. URL-encode the whole value when it contains \"/\" or \":\"."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable code (invalid_request, not_found, auth_required, styleref_not_generated, plan_limit_reached, rate_limited, internal_error)."},"message":{"type":"string"}},"required":["error","message"]},"SearchResults":{"type":"object","properties":{"count":{"type":"integer"},"styles":{"type":"array","items":{"$ref":"#/components/schemas/StyleCard"}},"availableCategories":{"type":"array","items":{"type":"string"},"description":"Present only when the search matched nothing — valid category filters to retry with."},"offset":{"type":"integer","description":"The offset this page started at."},"hasMore":{"type":"boolean"},"nextOffset":{"type":["integer","null"],"description":"Pass as ?offset= for the next page; null on the last page."},"attribution":{"type":"string"}},"required":["count","styles","attribution"]},"StyleCard":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"author":{"type":["string","null"],"description":"\"@handle\" of the publisher."},"category":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"url":{"type":"string","description":"Canonical public page for the style."},"heroImage":{"type":["string","null"]},"palette":{"type":"array","items":{"type":"string"},"description":"Up to 6 hex colors from the style palette, for painting swatches without a second call."}},"required":["name","slug","url"]},"ExtractionStarted":{"type":"object","properties":{"extractionId":{"type":"string","description":"Signed, account-bound handle. Poll it at /extractions/{id}; it is not usable by any other account."},"statusUrl":{"type":"string","description":"Absolute URL to poll."},"status":{"type":"string","enum":["running"]},"creditsRemaining":{"type":"integer","description":"Balance after this extraction. -1 means unlimited (Pro)."},"image":{"type":"object","description":"Dimensions of the accepted image after sanitization.","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"mediaType":{"type":"string"}}},"message":{"type":"string"}},"required":["extractionId","statusUrl","status"]},"ExtractionStatus":{"type":"object","properties":{"extractionId":{"type":"string"},"status":{"type":"string","enum":["running","failed","complete"]},"saved":{"type":"boolean","description":"True when the result was saved to the library (save_as_draft=true)."},"idempotentReplay":{"type":"boolean","description":"True when an earlier poll already created this draft — the same style is returned, not a duplicate."},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"blocks":{"type":"array","description":"Extracted style blocks (absent when saved=true).","items":{"type":"object","properties":{"section":{"type":"string"},"type":{"type":"string"},"value":{"type":"string"}}}},"spec":{"type":"string","description":"The compiled, ready-to-use style spec (absent when saved=true)."},"styleMd":{"type":"string","description":"The same style as a STYLE.md document (absent when saved=true)."},"style":{"type":"object","description":"Present when saved=true.","properties":{"ref":{"type":"string","description":"The style id — pass to getStyle."},"name":{"type":"string"},"slug":{"type":["string","null"]},"blockCount":{"type":"integer"}}},"editUrl":{"type":"string","description":"Builder link for the saved draft."},"lockedBlockCount":{"type":"integer","description":"How many style items the plan cap withheld from this result. 0 when nothing was withheld."},"note":{"type":"string","description":"Present only when lockedBlockCount > 0 — a human sentence naming the cap and the upgrade. Render verbatim."},"message":{"type":"string"}},"required":["extractionId","status"]},"SaveResult":{"type":"object","properties":{"saved":{"type":"boolean"},"alreadySaved":{"type":"boolean","description":"True when the style was in the library before this call."},"name":{"type":"string"},"slug":{"type":"string"},"libraryUrl":{"type":"string"}},"required":["saved","name","slug","libraryUrl"]},"MyStyles":{"type":"object","properties":{"count":{"type":"integer"},"styles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":["string","null"]},"ref":{"type":"string","description":"Share slug, or id — pass to getStyle."},"visibility":{"type":"string","enum":["public","link-shared","private"]},"updatedAt":{"type":"string","format":"date-time"},"lastGeneratedAt":{"type":["string","null"],"format":"date-time","description":"When the style was last generated on styleref.io; null if never. Fetching a never-generated style of your own returns 409 styleref_not_generated, so check this before offering the style as loadable."},"url":{"type":"string"}},"required":["name","ref","visibility","updatedAt","lastGeneratedAt","url"]}},"nextCursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last page."}},"required":["count","styles","nextCursor"]},"ExtractionHistory":{"type":"object","properties":{"count":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"styleId":{"type":["string","null"],"description":"Null when the style was deleted."},"createdAt":{"type":"string","format":"date-time"},"model":{"type":["string","null"]},"suggestedTitle":{"type":["string","null"]},"inputTextPreview":{"type":["string","null"]},"blockCount":{"type":"integer"},"appliedCount":{"type":"integer"},"locked":{"type":"boolean","description":"Present when the plan does not allow opening this entry."}},"required":["id","createdAt","blockCount"]}},"lockedCount":{"type":"integer"},"fullAccess":{"type":"boolean","description":"True on plans with the full archive."},"nextCursor":{"type":["string","null"]}},"required":["count","entries","lockedCount","fullAccess","nextCursor"]},"ExtractionHistoryEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"styleId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"model":{"type":["string","null"]},"suggestedTitle":{"type":["string","null"]},"suggestedDescription":{"type":["string","null"]},"inputTextPreview":{"type":["string","null"]},"appliedBlockIds":{"type":"array","items":{"type":"string"}},"blocks":{"type":"array","items":{"type":"object","properties":{"section":{"type":"string"},"type":{"type":"string"},"value":{},"locked":{"type":"boolean","description":"Withheld by the plan cap — shown so you can see what an upgrade unlocks; not included in the compiled spec."}}}},"lockedBlockCount":{"type":"integer"},"note":{"type":"string","description":"Present only when blocks are locked. Render verbatim."},"spec":{"type":"string","description":"Present with ?format=default."},"styleMd":{"type":"string","description":"Present with ?format=stylemd."}},"required":["id","createdAt","blocks","lockedBlockCount"]},"LintResult":{"type":"object","properties":{"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}},"meta":{"type":"object","properties":{"name":{"type":["string","null"]},"generator":{"type":["string","null"]},"sections":{"type":"array","items":{"type":"string"}}}}},"required":["valid","errors","warnings"]}},"responses":{"BadRequest":{"description":"Malformed query or body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No public style matched the reference.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; honor Retry-After. Every response (success included) carries X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset (unix seconds).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AuthRequired":{"description":"Missing, invalid, or under-scoped bearer token. `message` is written for a human — render it verbatim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotGenerated":{"description":"The style is yours but has never been generated on styleref.io, so there is no output to serve. `error` is \"styleref_not_generated\"; render `message` verbatim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanLimited":{"description":"The account plan does not include this read. `message` and `pricingUrl` are the upgrade prompt — render them verbatim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InsufficientCredits":{"description":"The account is out of extraction credits. `message` and `pricingUrl` are the upgrade prompt — render them verbatim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}