{"openapi":"3.1.0","info":{"title":"Raster REST API","version":"2026-07-08","description":"Resource-oriented HTTP API for Raster libraries and assets. Every response uses a `{ data }` (success) or `{ error: { code, message } }` (failure) envelope — `code` is a stable machine-readable identifier, `message` is human-readable copy that may be reworded at any time. Branch on `code`, never on `message`.\n\n**Versioning.** Every request must send an `Api-Version` header holding a date string (e.g. `Api-Version: 2026-07-08`). Responses carry `Vary: Api-Version`. During the Alpha every value receives the current behavior, so a breaking change reaches every integration.\n\n**Deprecation policy.** A version is announced as deprecated at least 180 days before it stops being served. During that window every response to a request pinning it carries an RFC 9745 `Deprecation` header, an RFC 8594 `Sunset` header holding the retirement date, and a `Link` header with `rel=\"deprecation\"` pointing at the migration notes. Nothing is retired without those headers appearing first. Policy: https://raster.app/docs/api/rest/versioning\n\n**Rate limits.** Every response carries `RateLimit-Policy`, and every credentialed response also carries `RateLimit` (draft-ietf-httpapi-ratelimit-headers), so a client can self-throttle. A credential over its limit gets `429` with `Retry-After` in seconds; anonymous traffic is limited at the network edge. Policy: https://raster.app/docs/api/rest/rate-limits\n\nReference: https://raster.app/docs/api/rest","contact":{"email":"support@raster.app","url":"https://raster.app/docs/api"}},"externalDocs":{"description":"Raster REST API reference","url":"https://raster.app/docs/api/rest"},"servers":[{"url":"/"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Account"},{"name":"Libraries"},{"name":"Assets"},{"name":"Tags"},{"name":"Search"},{"name":"No account"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An organization API key, scoped to an allowlist of libraries, or an OAuth access token."}},"parameters":{"ApiVersion":{"name":"Api-Version","in":"header","required":true,"schema":{"type":"string","examples":["2026-07-08"]}},"OrganizationId":{"name":"organizationId","in":"path","required":true,"schema":{"type":"string"}},"LibraryId":{"name":"libraryId","in":"path","required":true,"schema":{"type":"string"}},"AssetId":{"name":"assetId","in":"path","required":true,"schema":{"type":"string"}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key or access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The credential may not perform this operation, or the organization's plan does not allow it. A key without access to a library gets 404, not 403.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found, or the credential is not authorized for it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"Invalid input (BAD_USER_INPUT), quota, or API-version error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Rate limit exceeded. Wait for the number of seconds in `Retry-After` before retrying.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit":{"description":"Remaining quota for the active policy, e.g. `\"key\";r=0;t=17`. The legacy `RateLimit-Limit` / `-Remaining` / `-Reset` triplet is sent alongside it.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The quota policy in force, e.g. `\"key\";q=600;w=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Unexpected server fault (INTERNAL_SERVER_ERROR). Safe to retry an idempotent operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","examples":["API_KEY_NOT_AUTHORIZED_FOR_LIBRARY"]},"message":{"type":"string"}}}}},"Me":{"description":"What the credential can act on. An API key, or an OAuth token bound to one organization, resolves that organization; a token granted the whole account resolves every organization it belongs to under `organizations`. An OAuth token also carries `user`; an API key does not.","oneOf":[{"type":"object","required":["organizationId","organizationName","plan","libraries"],"properties":{"organizationId":{"type":"string"},"organizationName":{"type":["string","null"]},"plan":{"type":["string","null"]},"libraries":{"type":"array","items":{"type":"object","required":["library","access"],"properties":{"library":{"type":"string"},"access":{"type":"string","enum":["read","write"]}}}},"user":{"type":"object","description":"The account an OAuth access token acts as.","required":["name"],"properties":{"name":{"type":["string","null"]}}}}},{"type":"object","required":["organizations","user"],"properties":{"organizations":{"type":"array","items":{"type":"object","required":["organizationId","organizationName","plan","libraries"],"properties":{"organizationId":{"type":"string"},"organizationName":{"type":["string","null"]},"plan":{"type":["string","null"]},"libraries":{"type":"array","items":{"type":"object","required":["library","access"],"properties":{"library":{"type":"string"},"access":{"type":"string","enum":["read","write"]}}}}}}},"user":{"type":"object","description":"The account an OAuth access token acts as.","required":["name"],"properties":{"name":{"type":["string","null"]}}}}}]},"Asset":{"type":"object","description":"Represents a digital asset with associated metadata.","properties":{"id":{"type":"string"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"name":{"type":["string","null"]},"libraryId":{"type":["string","null"]},"blurhash":{"type":["string","null"]},"url":{"type":["string","null"],"format":"uri","description":"Permanent, public CDN URL for the full-resolution asset. It needs no\nauthentication and never expires, so reference it directly as the source URL\nrather than downloading and re-hosting the file."},"thumbUrlBlurred":{"type":["string","null"],"format":"uri","deprecated":true},"sizes":{"type":["string","null"],"description":"JSON-encoded map of size (the image's longest edge, in pixels) to the CDN\nURL of the image at that size (parse before use). Each URL carries the same\npermanent, public, embed-directly semantics as `url`; pick the size closest to\nyour display target."},"appUrl":{"type":["string","null"],"format":"uri","description":"Absolute link that opens this asset in the Raster app, for a person to view,\nedit, or manage it. Give it to the user as a link; the source URL is `url`."},"variants":{"type":["array","null"],"items":{"anyOf":[{"$ref":"#/components/schemas/AssetVariant"},{"type":"null"}]},"description":"Non-destructive variants of the asset. Each entry carries its own\n`parentId` pointing back to this asset."},"parentId":{"type":["string","null"],"description":"`null` on a top-level original, the parent asset's id on a variant. A variant\nis a first-class Asset — tag-filtered and search queries return it as its own\nentry — and `parentId` relates it back to its original."},"description":{"type":["string","null"]},"tags":{"type":["array","null"],"items":{"type":["string","null"]}},"contentType":{"type":["string","null"],"description":"MIME content type of the asset (e.g. `image/jpeg`, `image/png`).","examples":["image/jpeg"]},"size":{"type":["number","null"],"description":"File size in bytes. May be `null` for older assets with no recorded size."},"duration":{"type":["number","null"],"description":"Length of the asset in seconds, for video. `null` on images and on\nvideos still being processed."},"metadata":{"anyOf":[{"$ref":"#/components/schemas/AssetMetadata"},{"type":"null"}],"description":"Details read from the asset's own bytes — how it was captured, and its\ntechnical format. `null` on assets uploaded before this data was captured."},"created":{"type":["number","null"],"description":"Creation time as unix milliseconds. May be `null` for some older assets."},"updated":{"type":["number","null"],"description":"Last-modified time as unix milliseconds. Bumped on any field change\n(rename, tag edit, variant-edit save). Useful for cache invalidation and\n\"sort by recently updated\" UX."},"contentVersion":{"type":["integer","null"],"description":"Counter bumped each time the asset's BYTES are replaced at an unchanged\nurl — a promoted variant, an edit re-save. Append it to any `url` or\n`sizes` entry as `?v=` to render the current pixels.\n\nNull on an asset whose bytes have never been replaced — render the bare\nurl there."},"uploadedBy":{"anyOf":[{"$ref":"#/components/schemas/Uploader"},{"type":"null"}],"description":"Attribution metadata for the user who uploaded the asset."},"approval":{"anyOf":[{"$ref":"#/components/schemas/Approval"},{"type":"null"}]}},"required":["id","width","height","name","libraryId","blurhash","url","thumbUrlBlurred","sizes","appUrl","variants","parentId","description","tags","contentType","size","duration","metadata","created","updated","contentVersion","uploadedBy","approval"]},"Tag":{"type":"object","description":"Represents a tag associated with assets or libraries.","properties":{"id":{"type":"string"},"count":{"type":["integer","null"]},"type":{"type":["string","null"]}},"required":["id","count","type"]},"Library":{"type":"object","description":"Represents a collection of assets.","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"assetsCount":{"type":["integer","null"]},"photosCount":{"type":["integer","null"],"deprecated":true},"trashCount":{"type":["integer","null"]},"tags":{"type":["array","null"],"items":{"anyOf":[{"$ref":"#/components/schemas/Tag"},{"type":"null"}]}}},"required":["id","name","assetsCount","photosCount","trashCount","tags"]},"SearchResult":{"type":"object","description":"Wire shape returned by the `searchAssets` query — `hits` plus the\ncorpus-wide `found` total and the resolved 1-based `page`.","properties":{"hits":{"type":"array","items":{"$ref":"#/components/schemas/SearchHit"}},"found":{"type":"integer"},"page":{"type":"integer"}},"required":["hits","found","page"]},"AssetVariant":{"type":"object","description":"Represents a variant of an asset.","properties":{"id":{"type":"string"},"parentId":{"type":["string","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"name":{"type":["string","null"]},"url":{"type":["string","null"],"format":"uri"},"sizes":{"type":["string","null"]},"appUrl":{"type":["string","null"],"format":"uri"}},"required":["id","parentId","width","height","name","url","sizes","appUrl"]},"AssetMetadata":{"type":"object","description":"Details read from the asset's own bytes — how it was captured, and its technical format.","properties":{"capture":{"anyOf":[{"$ref":"#/components/schemas/AssetCapture"},{"type":"null"}],"description":"How the asset was captured, when it carried any such details. `null` when\nit carried none, which is common for screen recordings and anything\nre-encoded."},"video":{"anyOf":[{"$ref":"#/components/schemas/VideoStream"},{"type":"null"}],"description":"The video's technical stream details. `null` on anything that is not a video."}},"required":["capture","video"]},"Uploader":{"type":"object","description":"Attribution metadata for the user or system that uploaded an asset.","properties":{"name":{"type":["string","null"],"description":"Display name of the uploader. May be `null`."},"avatarUrl":{"type":["string","null"],"format":"uri","description":"URL of the uploader's current avatar, read live from their account. May be `null`."}},"required":["name","avatarUrl"]},"Approval":{"type":"object","description":"An asset's approval (delivery) state — who set it, when, and why.","properties":{"state":{"type":"string"},"setBy":{"type":["string","null"],"description":"Email address of the person who set the approval. `null` when their account has no email address."},"setAt":{"type":["number","null"]},"note":{"type":"string"}},"required":["state","setBy","setAt"]},"SearchHit":{"type":"object","description":"A search hit — every public `Asset` field plus optional highlights and a\nrelevance score.","properties":{"id":{"type":"string"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"name":{"type":["string","null"]},"libraryId":{"type":["string","null"]},"blurhash":{"type":["string","null"]},"url":{"type":["string","null"],"format":"uri"},"thumbUrlBlurred":{"type":["string","null"],"format":"uri","deprecated":true},"sizes":{"type":["string","null"]},"appUrl":{"type":["string","null"],"format":"uri"},"variants":{"type":["array","null"],"items":{"anyOf":[{"$ref":"#/components/schemas/AssetVariant"},{"type":"null"}]}},"parentId":{"type":["string","null"]},"description":{"type":["string","null"]},"tags":{"type":["array","null"],"items":{"type":["string","null"]}},"contentType":{"type":["string","null"]},"size":{"type":["number","null"]},"duration":{"type":["number","null"]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/AssetMetadata"},{"type":"null"}]},"created":{"type":["number","null"]},"updated":{"type":["number","null"]},"contentVersion":{"type":["integer","null"]},"uploadedBy":{"anyOf":[{"$ref":"#/components/schemas/Uploader"},{"type":"null"}]},"approval":{"anyOf":[{"$ref":"#/components/schemas/Approval"},{"type":"null"}]},"highlights":{"$ref":"#/components/schemas/SearchHitHighlights","description":"Highlight snippets — present only when the search matched on the\ncorresponding field. Matched terms are wrapped in `<mark>` tags."},"score":{"type":"number","description":"Relevance score for the hit. Optional."}},"required":["id","width","height","name","libraryId","blurhash","url","thumbUrlBlurred","sizes","appUrl","variants","parentId","description","tags","contentType","size","duration","metadata","created","updated","contentVersion","uploadedBy","approval"]},"AssetCapture":{"type":"object","description":"How an asset was captured — when, on what device, and where.","properties":{"recordedAt":{"type":["string","null"],"description":"When the asset says it was recorded, as written by the device. On a\nre-encoded file this is the time of that encode rather than the original\nrecording, so treat it as a hint rather than a reliable capture date."},"make":{"type":["string","null"],"description":"Device maker, e.g. `Apple`."},"model":{"type":["string","null"],"description":"Device model, e.g. `iPhone 17 Pro`."},"software":{"type":["string","null"],"description":"Capture software or OS version reported by the device."},"latitude":{"type":["number","null"],"description":"Signed decimal degrees. Present only alongside `longitude`."},"longitude":{"type":["number","null"],"description":"Signed decimal degrees."},"altitude":{"type":["number","null"],"description":"Metres above sea level, when the container recorded one."}},"required":["recordedAt","make","model","software","latitude","longitude","altitude"]},"VideoStream":{"type":"object","description":"A video's technical stream details — codec, frame rate, bitrate, and audio.","properties":{"codec":{"type":"string","description":"Video codec, e.g. `h264`, `hevc`, `vp9`, `av1`."},"frameRate":{"type":["number","null"],"description":"Frames per second, e.g. `30` or `23.98`."},"bitrate":{"type":["number","null"],"description":"Overall bitrate in bits per second, across all streams."},"hasAudio":{"type":"boolean","description":"Whether the video carries an audio track."},"audioCodec":{"type":["string","null"],"description":"Audio codec, e.g. `aac`, `opus`. `null` when there is no audio track."},"audioLayout":{"type":["string","null"],"description":"Audio channel layout, e.g. `mono`, `stereo`, `5.1`."},"audioSampleRate":{"type":["integer","null"],"description":"Audio sample rate in hertz, e.g. `48000`."}},"required":["codec","frameRate","bitrate","hasAudio","audioCodec","audioLayout","audioSampleRate"]},"SearchHitHighlights":{"type":"object","description":"Highlight snippets returned alongside each `SearchHit`.","properties":{"name":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}}}},"paths":{"/me":{"get":{"operationId":"getMe","tags":["Account"],"summary":"Resolve the credential's organization and library scope.","description":"Returns the organization the presented credential belongs to and the libraries it can reach, each with a read or write access level. Call this first to discover ids — every other operation needs an organizationId and libraryId.","parameters":[{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Me"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/libraries":{"post":{"operationId":"createEphemeralLibrary","tags":["No account"],"summary":"Create an organization, library, and API key with no account.","description":"Anonymous — mints the key, so send no Authorization header. Give it an email address and it returns a ready-to-use organization, library, API key, and a claim URL to hand the user so they can attach the library to a real account later. Use this when an agent needs somewhere to put images before the user has signed up. https://raster.app/docs/guides/start-without-an-account","security":[],"parameters":[{"$ref":"#/components/parameters/ApiVersion"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"name":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["organizationId","libraryId","apiKey","claimUrl","expiresAt","emailSent"],"properties":{"organizationId":{"type":"string"},"libraryId":{"type":"string"},"apiKey":{"type":"string"},"claimUrl":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"},"emailSent":{"type":"boolean"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries":{"get":{"operationId":"listLibraries","tags":["Libraries"],"summary":"List libraries the credential can reach.","description":"Paginated list of the libraries inside one organization that the presented credential is allowed to see. Use it to resolve a human-supplied library name to the libraryId every asset operation needs.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"name":"page","in":"query","description":"Zero-based page index.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"pageSize","in":"query","schema":{"type":"integer","default":20,"maximum":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Library"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}},"post":{"operationId":"createLibrary","tags":["Libraries"],"summary":"Create a library in this organization.","description":"Creates an empty library. Requires a write-scoped credential; a read-only key returns API_KEY_READ_ONLY, and the free plan returns LIBRARY_LIMIT_EXCEEDED once its library allowance is used up.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"slug":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Library"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}":{"patch":{"operationId":"renameLibrary","tags":["Libraries"],"summary":"Rename a library.","description":"Changes a library's display name. The libraryId is unchanged, so existing integrations and asset URLs keep working.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Library"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets":{"get":{"operationId":"listAssets","tags":["Assets"],"summary":"List assets (paginated, optional tag filter).","description":"Pages through one library's assets, newest first, optionally narrowed to assets carrying every tag in the `tags` filter. Each asset carries a permanent public CDN `url` you can embed directly. To find an asset by what it depicts rather than by tag, use searchAssets instead.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","default":50,"maximum":500}},{"name":"tags","in":"query","description":"Comma-separated tag filter, up to 50.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}},"post":{"operationId":"uploadAssets","tags":["Assets"],"summary":"Upload up to 20 files.","description":"Uploads image or video files as multipart/form-data under the `files` field, up to 20 per call. Each returned asset carries a permanent public CDN `url` — embed that rather than re-hosting the bytes. Processing (thumbnails, AI tags, descriptions) continues after the response, so re-fetch an asset to see it fully populated.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["files"],"properties":{"files":{"type":"array","items":{"type":"string","format":"binary"}},"parentId":{"type":"string","description":"Attach the uploaded file as a variant of this asset. Send exactly one file with it. The asset must be an original in this library, of the same media kind as the file."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["responseText","assets"],"properties":{"responseText":{"type":"string"},"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}},"delete":{"operationId":"deleteAssets","tags":["Assets"],"summary":"Move up to 100 assets to trash (soft delete).","description":"Soft delete — assets stay recoverable from trash for 30 days, then are permanently removed. Trashing an asset also trashes its variants; a variant id on its own is rejected with 400.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","items":{"type":"string"},"maxItems":100}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["success","message","ids"],"properties":{"success":{"type":"boolean"},"message":{"type":"string"},"ids":{"type":"array","items":{"type":"string"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/{assetId}":{"get":{"operationId":"getAsset","tags":["Assets"],"summary":"Get one asset by id.","description":"Returns the full asset, including its non-destructive variants in the `variants` field; each variant carries its own tags/description — fetch a variant by its id for those.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"},{"$ref":"#/components/parameters/AssetId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Asset"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/{assetId}/description":{"patch":{"operationId":"updateAssetDescription","tags":["Assets"],"summary":"Replace one asset's description.","description":"Overwrites the asset's description with the supplied text — this is a replace, not an append. Descriptions are indexed, so a good one makes the asset findable through searchAssets.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"},{"$ref":"#/components/parameters/AssetId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","maxLength":2000}},"required":["description"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"assetId":{"type":"string","minLength":1},"description":{"type":"string"}},"required":["assetId","description"],"additionalProperties":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/{assetId}/promote":{"post":{"operationId":"promoteVariant","tags":["Assets"],"summary":"Make a variant the asset's default.","description":"The variant's pixels replace what the asset serves, at the SAME URL, so anything already embedding that URL shows the new file. The previous default is preserved as a new variant and keeps the name it had; the asset keeps its id and URL and takes the promoted variant's name. Its description and the tags generated from its image follow the picture — the promoted variant hands over any it carries, and the preserved variant keeps the ones that described it, while tags you added yourself stay on the asset. Rejected while the asset is a video that is still being processed. The promote is accepted and the swap completes a moment later; re-fetch the asset to see it reflected.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"},{"$ref":"#/components/parameters/AssetId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"variantId":{"type":"string","minLength":1}},"required":["variantId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"assetId":{"type":"string","minLength":1},"promotedVariantId":{"type":"string","minLength":1},"demotedVariantId":{"type":"string","minLength":1}},"required":["assetId","promotedVariantId","demotedVariantId"],"additionalProperties":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/{assetId}/approval":{"patch":{"operationId":"setAssetApproval","tags":["Assets"],"summary":"Set one asset's approval state.","description":"Sets the asset's approval to one of the organization's approval states, with an optional note; each call replaces the previous approval. Works on an original or a variant. Needs an OAuth access token — an API key can read approval but not set it (403 APPROVAL_REQUIRES_USER). Setting approval needs a paid plan (403 PAID_PLAN_REQUIRED).","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"},{"$ref":"#/components/parameters/AssetId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","minLength":1},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]}},"required":["state"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"assetId":{"type":"string","minLength":1},"approval":{"type":"object","properties":{"state":{"type":"string"},"setBy":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Email address of the person who set the approval. `null` when their account has no email address."},"setAt":{"type":"number"},"note":{"type":"string"}},"required":["state","setBy","setAt"],"additionalProperties":false}},"required":["assetId","approval"],"additionalProperties":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/tag":{"post":{"operationId":"tagAssets","tags":["Tags"],"summary":"Apply up to 20 tags to up to 100 assets.","description":"Adds tags to assets in bulk. Tags already present are left alone, so the call is safe to repeat. Tags are how listAssets filters, and they feed searchAssets ranking.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assetIds":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1}},"tags":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","minLength":1}}},"required":["assetIds","tags"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"taggedCount":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["taggedCount"],"additionalProperties":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/untag":{"post":{"operationId":"untagAssets","tags":["Tags"],"summary":"Remove up to 20 tags from up to 100 assets.","description":"Removes tags from assets in bulk. Tags that were not present are ignored, so the call is safe to repeat. Only the named tags are removed; every other tag on the asset stays.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assetIds":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1}},"tags":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","minLength":1}}},"required":["assetIds","tags"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"untaggedCount":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["untaggedCount"],"additionalProperties":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/assets/transfer":{"post":{"operationId":"transferAssets","tags":["Assets"],"summary":"Move up to 100 assets to another library.","description":"Moves assets from the library in the path to another library in the same organization. An asset's variants move with it; a variant cannot be transferred on its own — transfer its original and the variants follow. The credential needs write access to both libraries.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetLibraryId":{"type":"string","minLength":1},"assetIds":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1}}},"required":["targetLibraryId","assetIds"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"transferredCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"sourceLibraryId":{"type":"string","minLength":1},"targetLibraryId":{"type":"string","minLength":1}},"required":["transferredCount","sourceLibraryId","targetLibraryId"],"additionalProperties":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/libraries/{libraryId}/tags":{"get":{"operationId":"listTags","tags":["Tags"],"summary":"List a library's tags by usage count.","description":"The library's tag vocabulary, most-used first. Read it before tagging so you reuse the team's existing tags instead of inventing near-duplicates, and to discover what a `tags` filter on listAssets can select.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"$ref":"#/components/parameters/LibraryId"},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organizations/{organizationId}/search/assets":{"get":{"operationId":"searchAssets","tags":["Search"],"summary":"Search assets across libraries (ranked, highlighted).","description":"Full-text, relevance-ranked search over filenames, descriptions, and tags across every library the credential can reach, or the subset named in `libraries`. This is the right call for a natural-language request such as \"the photo of the blue packaging\" — use listAssets when you already know the exact tag to filter on.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/OrganizationId"},{"name":"q","in":"query","required":true,"schema":{"type":"string"}},{"name":"libraries","in":"query","description":"Comma-separated library ids.","schema":{"type":"string"}},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","default":10,"maximum":50}},{"name":"approval","in":"query","description":"Only assets in this approval state; `none` matches assets with no approval.","schema":{"type":"string","enum":["in_review","approved","needs_changes","none"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SearchResult"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/ServerError"}}}}}}