Raster
Guides

Using Raster from an AI agent

A runbook for LLMs and AI agents — discover Raster, authenticate, and drive a library end to end over MCP, REST, or GraphQL, with or without a Raster account.

Raster is built so an AI agent can store, organize, search, and serve images on a person's behalf. This page is the runbook: how an agent finds Raster, gets a key, and runs the full asset loop — upload, tag, search, hand off.

There are two starting points:

  • You have an API key already — the user pasted one, or your client is configured with it. Jump to Connect.
  • You have no account and no key — start the user from zero with a single call. See Start without an account.

Discover Raster

Raster publishes machine-readable discovery documents so a capable client can configure itself without hard-coded knowledge:

DocumentURLWhat it tells you
MCP server cardhttps://raster.app/.well-known/mcp/server-card.jsonThe MCP endpoint, transport, and auth scheme.
Agent skills indexhttps://raster.app/.well-known/agent-skills/index.jsonSkills an agent can load, such as Markdown content negotiation.
API cataloghttps://raster.app/.well-known/api-catalogLinks to the API service and its documentation.
LLM page indexhttps://raster.app/llms.txtA Markdown index of every public page, plus pointers to these API docs.

Any page under raster.app also serves a token-efficient Markdown representation when you send Accept: text/markdown, or by appending .md to the path. The Markdown negotiation skill describes it.

Connect

Pick the transport that matches your client.

Most agent platforms — Claude, Cursor, VS Code, the OpenAI Responses API — speak the Model Context Protocol. Point the client at the remote endpoint with a Bearer key and Raster's tools appear in the palette:

https://mcp.raster.app/

The full per-client setup, the tool list, and runnable examples are on the MCP reference. Authentication and key scoping are covered in MCP authentication.

Call the REST API directly when you control the HTTP layer. Every request carries two headers:

Authorization: Bearer <API_KEY>
Api-Version: 2026-05-20

Responses use a { data } / { error } envelope. The full endpoint table and examples are on the REST reference. The same keys work over GraphQL.

An API key is scoped to one organization and an allowlist of libraries, each at a read or write access level. Treat it like a password. Create and scope keys in organization settings — see Authentication.

Run the loop

Once connected, the working loop is the same across transports. Each step names its MCP tool and the matching REST endpoint; each transport names the operation in its own idiom, and the GraphQL reference lists the equivalent fields.

Resolve your scope

Call whoami (REST: GET /me) to get the organizationId and the library ids your key can reach. Every other call needs an organizationId + libraryId pair.

Read what is there

list_assets pages through a library; search_assets ranks across libraries with highlights; list_tags returns a library's tags by usage. Use these to ground later actions in what already exists.

Add images

upload_asset takes a public http(s) URL the server fetches, or an inline base64 payload; upload_assets takes a batch in one call. Processing is asynchronous — an uploaded asset's CDN url starts serving, and the asset appears in lists and search, a few seconds after upload.

Organize

tag_assets and update_asset_description make assets findable. transfer_assets moves them between libraries. delete_assets moves assets to trash (a recoverable soft delete).

Hand the result back

Each asset carries a permanent CDN url — the canonical link to the image. Return those URLs, and the library URL, to the user. If you started the user without an account, also give them the claim link so the work persists — see Start without an account.

Reusable prompts by use case

Each prompt below is a focused, drop-in system-prompt block for one job an agent actually does — connecting, adding images, organizing, finding, or restructuring. Paste the one that matches the task in front of you; together they decompose the full runbook above, and parameters and limits stay on the linked tool reference.

Clients that load agent skills can fetch Raster's published raster-api skill from https://raster.app/.well-known/agent-skills/index.json to wire these tools up automatically.

Each prompt is available per transport — pick your tab for MCP, REST, or GraphQL, and the choice carries across every example. See Run the loop for the references behind each transport's names.

Connect with a key and resolve scope

Use when you already hold an API key — the user pasted one, or your client is configured with it — and need to discover the organization and libraries it can act on before any read or write.

When you hold a Raster API key and need to act on the user's images:

- Call `whoami` first. It returns the `organizationId` and the libraries your key can reach. Pair that `organizationId` with a `libraryId` on every asset-level call.
- When the user names a library or you need to choose one, call `list_libraries` for the org and match by name.
- Before acting on a library, call `list_assets` and `list_tags` to ground your work in what already exists.
- A library your key cannot reach returns 404. Treat that as out of scope and pick a library `whoami` listed.
When you hold a Raster API key and need to act on the user's images over REST:

- Send `Authorization: Bearer <API_KEY>` and `Api-Version: 2026-05-20` on every request, and read the `{ data }` envelope on success or `{ error }` (with an `error.code`) on failure.
- Call `GET /me` first. It returns `data.organizationId` and `data.libraries`, the library ids your key can reach. Pair that `organizationId` with a `libraryId` on every asset-level path, such as `GET /organizations/:organizationId/libraries/:libraryId/assets`.
- When the user names a library or you need to choose one, call `GET /organizations/:organizationId/libraries` and match by `name`.
- Before acting on a library, call `GET /organizations/:organizationId/libraries/:libraryId/assets` and `GET /organizations/:organizationId/libraries/:libraryId/tags` to ground your work in what already exists.
- A library your key cannot reach returns `404` (`API_KEY_NOT_AUTHORIZED_FOR_LIBRARY`). Treat it as out of scope and pick a library that `GET /me` listed. Full headers, params, and codes: [REST reference](/api/rest/endpoints).
When you hold a Raster API key and need to act on the user's images over GraphQL, POST to `https://api.raster.app/` with `Authorization: Bearer <API_KEY>`:

- Run the `viewer` query first. It returns `organizationId` and `libraries`, the library ids your key can reach. Pass that `organizationId` and a `libraryId` to every asset-level operation.
- When the user names a library or you need to choose one, run the `libraries` query for the org and match by `name`.
- Before acting on a library, run the `assets` and `tags` queries for it to ground your work in what already exists.
- A library your key cannot reach fails the whole request with `API_KEY_NOT_AUTHORIZED_FOR_LIBRARY`. Treat it as out of scope and pick a library that `viewer` listed. Full schema and args: [GraphQL reference](/api/graphql).

Use when you have images to bring into a library — public URLs, local files, or a mix — and want them hosted on the CDN with shareable links back, without creating duplicates.

When you add images to a Raster library:

- Call `whoami` for the `organizationId` and a writable `libraryId`, then confirm the target library.
- Before uploading, run `search_assets` or `list_assets` and skip images that are already there.
- Upload with `upload_asset` for one image or `upload_assets` for a batch. Mix public http(s) URLs and base64 freely — pass a URL when you have one, base64 for local files. A batch validates every source before storing any byte, so a single invalid source fails the whole batch; fix it and resend.
- Each upload returns the asset's permanent CDN `url` and `id` right away, and the image appears in lists and search a few seconds later. Re-list or re-search before concluding an upload failed.
- Hand the user each asset's permanent `url`, plus the library URL.
When you add images to a Raster library over REST (`Authorization: Bearer <API_KEY>` and `Api-Version: 2026-05-20` on every call; read the `{ data }` / `{ error }` envelope):

- Call `GET /me` for the `organizationId` and a writable `libraryId` from `data.libraries`, then confirm the target library.
- Before uploading, run `GET /organizations/:organizationId/search/assets` or `GET /organizations/:organizationId/libraries/:libraryId/assets` and skip images already present.
- Upload with `POST /organizations/:organizationId/libraries/:libraryId/assets` as `multipart/form-data`, sending each file under the repeatable `files` field and letting the client set the multipart `Content-Type`. The batch validates every file before storing any byte, so one bad part fails the whole request (see the endpoint reference for the per-request and per-file limits) — fix it and resend.
- Each upload returns `data.assets[]` with the permanent CDN `url` and `id` right away; the async fields fill in a few seconds later, so re-read with `GET /organizations/:organizationId/libraries/:libraryId/assets/:assetId` before concluding an upload failed.
- Hand the user each asset's permanent `url`, plus the library URL.
When you add images to a Raster library over GraphQL (POST to `https://api.raster.app/` with `Authorization: Bearer <API_KEY>`):

- Run the `viewer` query for `organizationId` and a writable id from `libraries`, then confirm the target library.
- Before uploading, run `searchAssets` or `assets` and skip images already present.
- Upload with the `uploadAssets` mutation, passing `organizationId`, `libraryId`, the `files` (`Upload[]`) variables, and the uploader `email`. Each file's type and size are validated before storing (see the mutation reference for the limits), so one bad file fails the whole request — fix it and resend. Select `assets { id url }` to read the permanent CDN `url` back.
- The permanent `url` and `id` return right away; async fields fill in a few seconds later, so re-run the `asset` query by `id` before concluding an upload failed.
- Hand the user each asset's permanent `url`, plus the library URL.

Organize and curate — tag, describe, clean up

Use when images already live in a library and you need to make them findable or tidy them: apply or remove tags, write descriptions, or retire unwanted and duplicate assets.

When you organize assets in a Raster library:

- Call `list_tags` to reuse the library's existing vocabulary, then apply tags with `tag_assets`. Tagging is idempotent, and the reserved tags `index` and `trash` are rejected, so choose other words.
- Remove a label that no longer fits with `untag_assets`.
- Write a description with `update_asset_description`, which stores your text verbatim — run your own model to generate it, then post the result, and pass an empty string to clear it.
- Use `get_asset` to read one asset's current `url`, `tags`, and `description` before changing it.
- To curate duplicates, find matches with `search_assets`, keep the canonical asset, and move the rest to trash with `delete_assets`. This soft delete stays recoverable from trash until it is permanently removed.
- Every asset you tag, untag, describe, or delete must belong to the named library, or the call fails as a whole.
When you organize assets in a Raster library over REST (send `Authorization: Bearer <API_KEY>` and `Api-Version: 2026-05-20` on every call; read the `{ data }` / `{ error }` envelope):

- `GET /organizations/:organizationId/libraries/:libraryId/tags` to reuse the library's existing vocabulary, then apply labels with `POST /organizations/:organizationId/libraries/:libraryId/assets/tag` (`assetIds`, `tags`). Tagging is idempotent — compare the returned `taggedCount` against `assetIds.length × tags.length` to confirm every pair landed — and the reserved tags `index` and `trash` come back as `400 BAD_USER_INPUT`, so choose other words.
- Remove a label that no longer fits with `POST /organizations/:organizationId/libraries/:libraryId/assets/untag` (same `assetIds` and `tags` body), which echoes `untaggedCount`.
- Write a description with `PATCH /organizations/:organizationId/libraries/:libraryId/assets/:assetId/description`, which stores `description` verbatim — run your own model to generate it, then post the result, and send an empty string to clear it.
- `GET /organizations/:organizationId/libraries/:libraryId/assets/:assetId` to read one asset's current `url`, `tags`, and `description` before changing it.
- To curate duplicates, find matches with `GET /organizations/:organizationId/search/assets?q=...`, keep the canonical asset, and move the rest to trash with `DELETE /organizations/:organizationId/libraries/:libraryId/assets` (`ids`). This soft delete stays recoverable from trash until it is permanently removed; the returned `ids` list those actually moved, so a retry is safe.
- Every asset you tag, untag, describe, or delete must belong to the named library, or the call fails as a whole.
When you organize assets in a Raster library over GraphQL (POST to `https://api.raster.app/` with `Authorization: Bearer <API_KEY>`; pass the `organizationId` and `libraryId` slugs as arguments):

- Run the `tags` query to reuse the library's existing vocabulary, then apply labels with the `tagAssets` mutation (`assetIds`, `tags`). Tagging is idempotent — check the returned `taggedCount` to confirm every pair landed — and the reserved tags `index` and `trash` come back as `BAD_USER_INPUT`, so choose other words.
- Remove a label that no longer fits with the `untagAssets` mutation (same `assetIds` and `tags`), which returns `untaggedCount`.
- Write a description with the `updateAssetDescription` mutation, which stores `description` verbatim — run your own model to generate it, then post the result, and pass an empty string to clear it. It echoes back `{ assetId, description }`.
- Run the `asset` query (`assetId`) to read one asset's current `url`, `tags`, and `description` before changing it.
- To curate duplicates, find matches with the `searchAssets` query (`q`), keep the canonical asset, and move the rest to trash with the `deleteAssets` mutation (`assets`). This soft delete stays recoverable from trash until it is permanently removed; the mutation is idempotent, so a retry is safe.
- Every asset you tag, untag, describe, or delete must belong to the named library, or the call fails as a whole.

Use when the user wants you to find specific images and hand back usable links — for a page, an email, a doc, or to fill a content slot — across one library or many. Read-only.

When you find and return images from Raster:

- Call `whoami` for the `organizationId` and the libraries your key can reach.
- Use `search_assets` for free-text queries across every authorized library; it matches name, description, and tags and returns ranked hits with highlights. Pass `libraries[]` to narrow to a named subset, and use an empty query to get recent assets.
- Use `list_assets` to page through one library, filtering by a set of tags; call `list_tags` first to learn which tags exist.
- Use `get_asset` to fetch one asset's full detail by `id` without paging.
- Return each match's permanent `url`, with its tags and description as context, plus the library URL.
- A freshly uploaded image becomes searchable a few seconds after upload, so re-search if a recent one is missing.
When you find and return images from Raster over REST:

- Send `Authorization: Bearer <API_KEY>` and `Api-Version: 2026-05-20` on every call, and read each response from the `{ data }` / `{ error }` envelope.
- Call `GET /me` first for the `organizationId` and the library ids your key can reach.
- For free-text queries across authorized libraries, call `GET /organizations/:organizationId/search/assets` with `q`; it matches name, description, and tags and returns ranked hits with highlights. Repeat `libraries` to narrow to a named subset, and send an empty `q` for recent assets.
- To page one library by tag, call `GET /organizations/:organizationId/libraries/:libraryId/assets` with `tags`; call `GET /organizations/:organizationId/libraries/:libraryId/tags` first to learn which tags exist.
- Call `GET /organizations/:organizationId/libraries/:libraryId/assets/:assetId` to fetch one asset's full detail by id.
- Return each match's permanent `url`, with its `tags` and `description` as context, plus the library URL.
- A freshly uploaded image becomes searchable a few seconds after upload, so re-search if a recent one is missing.
When you find and return images from Raster over GraphQL, POST to `https://api.raster.app/` with `Authorization: Bearer <API_KEY>`:

- Run the `viewer` query first for `organizationId` and the `libraries` your key can reach.
- For free-text queries across authorized libraries, run `searchAssets` with `q`; it matches name, description, and tags and returns ranked `hits`. Pass `libraries` to narrow to a named subset, and send an empty `q` for recent assets.
- To page one library by tag, run `assets` with `tags`; run the `tags` query first to learn which tags exist.
- Run the `asset` query with `assetId` to fetch one asset's full detail by id.
- Return each match's permanent `url`, with its `tags` and `description` as context, plus the library URL.
- A freshly uploaded image becomes searchable a few seconds after upload, so re-search if a recent one is missing.

Restructure libraries and move assets

Use when you need to change library structure: create a new library for a project or campaign, rename one, or move assets between libraries in the same organization.

When you restructure a user's Raster libraries:

- Call `whoami` for the `organizationId`, then `list_libraries` to see what already exists before creating anything.
- Create a library with `create_library` (name required, optional slug). It joins your key's allowlist, so you can upload to it right away.
- Rename a library with `rename_library`; the name updates everywhere it is shown and the URL slug stays the same, so existing links keep working.
- Move assets within the same org with `transfer_assets` — name the `sourceLibraryId`, the `targetLibraryId`, and the asset `id`s. Every `id` must already live in the source library, or the call fails as a whole.
- After creating, renaming, or moving, re-run `list_libraries` (and `list_assets` where relevant) to confirm the new state, and report the library names and URLs back to the user.
When you restructure a user's Raster libraries over REST (send `Authorization: Bearer <API_KEY>` and `Api-Version: 2026-05-20` on every call; read the `{ data }` / `{ error }` envelope):

- `GET /me` for the `organizationId`, then `GET /organizations/:organizationId/libraries` to see what already exists before creating anything.
- Create a library with `POST /organizations/:organizationId/libraries` (`name` required, optional `slug`). It joins your key's allowlist, so you can upload to it right away.
- Rename a library with `PATCH /organizations/:organizationId/libraries/:libraryId` (`name` in the body); the name updates everywhere it is shown and the URL slug stays the same, so existing links keep working.
- Move assets within the same org with `POST /organizations/:organizationId/libraries/:libraryId/assets/transfer` — the URL's `:libraryId` is the source, the body carries `targetLibraryId` and the asset `assetIds`. Every id must already live in the source library, or the call fails as a whole.
- After creating, renaming, or moving, re-run `GET /organizations/:organizationId/libraries` (and `GET .../assets` where relevant) to confirm the new state from `data`, and report the library names and URLs back to the user.
When you restructure a user's Raster libraries over GraphQL (POST to `https://api.raster.app/` with `Authorization: Bearer <API_KEY>`):

- Run the `viewer` query for `organizationId`, then the `libraries` query to see what already exists before creating anything.
- Create a library with the `createLibrary` mutation (`name` required, optional `slug`). It joins your key's allowlist, so you can upload to it right away.
- Rename a library with the `renameLibrary` mutation (`organizationId`, `libraryId`, `name`); the returned `Library` reflects the new name and the URL slug stays the same, so existing links keep working.
- Move assets within the same org with the `transferAssets` mutation — pass `sourceLibraryId`, `targetLibraryId`, and the `assetIds`. Every id must already live in the source library, or the mutation fails as a whole; read `transferredCount` to confirm.
- After creating, renaming, or moving, re-run the `libraries` query (and `assets` where relevant) to confirm the new state, and report the library names and URLs back to the user.

Start from zero — no account, no key

Use when the user has no Raster account and you hold no API key. The dedicated flow mints an organization, a starter library, and a key, then hands off a claim link — its prompt lives at Start without an account.

Where to go next

On this page