Ollav for AI agents
Use Ollav when a user asks to share, preview, get feedback on or review an HTML file, prototype, mockup or generated page. Ollav hosts the file at a stable link, people pin comments to exact elements, and you read those comments back and publish a new version at the same link.
You need one thing: an API key. Ask the user to create one at https://app.ollav.app/keys and set it as OLLAV_API_KEY. A key belongs to one workspace. Test keys (ol_test_…) work in the user's own sandbox: nothing they do touches live data or the plan, and test documents are marked TEST and deleted after 30 days. Start with one; switch to a live key (ol_live_…) to share with the team. Keys made before test mode start sk_live_ and are live.
export OLLAV_API_KEY=ol_test_...The whole loop in three commands
# 1. Publish → returns {"url": "https://content.ollav.app/p/<slug>", "slug": "<slug>", ...}
curl -s -F "file=@prototype.html" -H "Authorization: Bearer $OLLAV_API_KEY" -H "X-Ollav-Client: agent" https://app.ollav.app/api/documents
# 2. Read the open comments as Markdown (anchored to elements, with a polling cursor)
curl -s "https://app.ollav.app/api/review?slug=$SLUG&format=markdown" -H "Authorization: Bearer $OLLAV_API_KEY"
# 3. Ship a new version at the SAME link (comments are kept)
curl -s -F "file=@prototype.html" -H "Authorization: Bearer $OLLAV_API_KEY" -H "X-Ollav-Client: agent" https://app.ollav.app/api/documents/$SLUG/versionsAlways give the user the url from step 1. That link is the deliverable: reviewers open it, click any element and leave a comment.
Connect over MCP
The full MCP docs are at /docs/mcp: connect guides with copy-paste configs, auth options, how to check the connection works, every tool with its inputs, outputs and an example, and troubleshooting for every error. New to it? Use Ollav from your agent via MCP takes five minutes.
The MCP endpoint is https://app.ollav.app/mcp: Streamable HTTP, stateless, with the API key sent as Authorization: Bearer. Clients that can't send headers can use https://app.ollav.app/mcp/ol_test_... instead. That URL contains the key, so treat it like a password.
Claude Code
Install the plugin (MCP server plus a skill that tells Claude when to use Ollav):
/plugin marketplace add ollav-app/ollav
/plugin install ollav@ollavOr add just the MCP server:
claude mcp add --transport http ollav https://app.ollav.app/mcp --header "Authorization: Bearer $OLLAV_API_KEY"Cursor
Add this to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):
{
"mcpServers": {
"ollav": {
"url": "https://app.ollav.app/mcp",
"headers": { "Authorization": "Bearer ${env:OLLAV_API_KEY}" }
}
}
}OpenAI Codex CLI
codex mcp add ollav --url https://app.ollav.app/mcp --bearer-token-env-var OLLAV_API_KEYOr in ~/.codex/config.toml:
[mcp_servers.ollav]
url = "https://app.ollav.app/mcp"
bearer_token_env_var = "OLLAV_API_KEY"ChatGPT
Add Ollav as an app with OAuth. You sign in to Ollav once and pick the workspace ChatGPT may use. No API key to copy:
Settings → Apps & Connectors → Create (developer mode; menu names change over time)
Name: Ollav
MCP server URL: https://app.ollav.app/mcp
Authentication: OAuthIn ChatGPT you can attach an HTML file and say "share this on Ollav". The file is passed to Ollav directly, so the model doesn't retype it. Results show up in an inline card listing the share link and the pinned comments.
Claude.ai and Claude Desktop
Settings → Connectors → Add custom connector
URL: https://app.ollav.app/mcpClaude signs you in with OAuth. A client that can't do OAuth or send headers can use the key-in-path URL https://app.ollav.app/mcp/ol_test_YOUR_KEY instead.
VS Code (GitHub Copilot)
.vscode/mcp.json. VS Code prompts for the key once and stores it securely:
{
"inputs": [
{ "type": "promptString", "id": "ollav-key", "description": "Ollav API key (ol_test_... or ol_live_...)", "password": true }
],
"servers": {
"ollav": {
"type": "http",
"url": "https://app.ollav.app/mcp",
"headers": { "Authorization": "Bearer ${input:ollav-key}" }
}
}
}Windsurf
~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"ollav": {
"serverUrl": "https://app.ollav.app/mcp",
"headers": { "Authorization": "Bearer ${env:OLLAV_API_KEY}" }
}
}
}Gemini CLI
~/.gemini/settings.json:
{
"mcpServers": {
"ollav": {
"httpUrl": "https://app.ollav.app/mcp",
"headers": { "Authorization": "Bearer $OLLAV_API_KEY" }
}
}
}Any stdio-only MCP client (npx bridge)
If a client only launches local stdio servers, bridge to the remote endpoint with mcp-remote:
npx -y mcp-remote https://app.ollav.app/mcp --header "Authorization: Bearer $OLLAV_API_KEY"As a config entry:
{
"mcpServers": {
"ollav": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://app.ollav.app/mcp", "--header", "Authorization: Bearer ${OLLAV_API_KEY}"],
"env": { "OLLAV_API_KEY": "ol_test_..." }
}
}
}OAuth
https://app.ollav.app/mcp implements the MCP authorization spec, so OAuth clients connect with no key to copy:
- A request without a token gets
401withWWW-Authenticate: Bearer resource_metadata="https://app.ollav.app/.well-known/oauth-protected-resource/mcp". - Resource metadata:
/.well-known/oauth-protected-resource/mcp. Server metadata:/.well-known/oauth-authorization-server. - Authorization code with PKCE (
S256only). Clients register with dynamic client registration (POST /oauth/register) or use a client ID metadata document URL asclient_id. Public clients (none) andclient_secret_post/client_secret_basicare supported. - Scopes:
read,upload,comment. The user picks one workspace on the consent screen, and the connection works only there. - The access token is an Ollav API key that doesn't expire. The user can see and revoke it under API keys, listed as "<app> (OAuth)". Clients can revoke it with
POST /oauth/revoke.
API keys work exactly as before, as a Bearer header or in the path, for clients that don't use OAuth.
MCP tools
Each tool's inputs, outputs, annotations, an example call and its result are on the MCP docs. Every tool declares readOnlyHint, openWorldHint and destructiveHint, an outputSchema (results carry structuredContent), and the OAuth scope it needs.
| Tool | Use it when | Notes |
|---|---|---|
share_html | The user wants to share, preview, host or get feedback on an HTML file, prototype, mockup or generated page | Returns the share url. Takes html or an attached file. Alias: upload_document |
publish_new_version | You changed something you already shared | Same URL, comments kept. Takes html or file. Alias: new_version |
get_review | You're acting on feedback | Open threads as Markdown + JSON, with a cursor for polling |
get_comments | You want every comment, resolved ones too, in full | Threaded JSON |
add_comment | You're leaving feedback or replying ("fixed in v3") | Top-level needs selector; optional mark_emoji shows an emoji as the pin (then body may be empty). Replies need parent_id. Emails the document owner |
resolve_comment | You addressed a thread | unresolve: true reopens it |
react_to_comment | Acknowledge without a reply | Any emoji: 👍 agree, 👀 on it, ✅ done, ❤️, 🎉 |
list_documents | Find something shared earlier | Version and unresolved-comment counts |
get_document | Version history of one document | |
delete_comment | Remove your own comment | |
export_data | The user asks for a copy of their data | Signed ZIP link, valid 15 minutes |
The old names upload_document and new_version keep working, so existing configs and permission rules don't break.
The feedback loop
share_html(orPOST /api/documents): give the user theurl.- Wait for reviewers. Poll with
get_review. Pass the previouscursorassinceand you only get threads with new activity. - For each open thread, find the element in your source with
anchor.quote(the element's text) andanchor.reproduceNote.anchor.selectorpoints at the rendered DOM, not your JSX. - Edit, then
publish_new_versionwith the sameslug. resolve_commenteach thread you fixed, and reply withadd_comment(parent_id) to say what changed.
Comments made on an earlier build are marked onCurrentVersion: false. Their element may have moved or gone.
Notifications
Your comments email people, exactly like a person's. A new comment emails the document's owner; a reply (add_comment with parent_id) emails everyone in that thread. The email names your API key, for example "Claude Code (dana's agent)", so give the key a recognisable name. The author is never emailed about their own comment.
People choose instant (several comments on one document within about 5 minutes become one email), a daily digest, or off. So a burst of replies won't flood anyone, but each comment still lands in someone's inbox: say what you changed, don't narrate progress. react_to_comment acknowledges a thread without emailing anyone. Comments made with an ol_test_ key never send email.
Rules that keep users happy
- Built apps: upload the file, don't paste it. Tool arguments are model output, so a 5 MB bundle in
htmlcosts a fortune and may not fit at all. If you have a shell, usecurl -F "file=@dist/index.html". curl reads the file from disk, so it costs zero tokens. - One self-contained HTML file, up to 15 MB. Inline CSS/JS/images or use absolute
https://URLs. Relative paths likesrc="assets/app.js"won't load. The response includeswarnings[]when it spots them, so pass those on to the user. - Visibility: leave it out. The default
workspacemeans only workspace members can see it.linklets anyone signed in to Ollav open it. Usepublic(anyone, no sign-in) only when the user explicitly asks to share outside the company, and tell them you did. - **Iterating? Use
publish_new_version, not another upload.** A second upload creates a different link with no comment history.
Errors
Every error looks like this, from REST and from MCP tools alike (MCP troubleshooting). Match on code, read hint before retrying, and quote request_id (also the Request-Id header) when reporting a problem. Every code, with its cause and fix, is on the errors page.
{ "error": { "type": "permission_error", "code": "SCOPE_REQUIRED", "message": "upload scope required", "hint": "This API key lacks the 'upload' scope. Ask the user to create a key with it at https://app.ollav.app/keys (scopes: upload, read, comment).", "doc_url": "https://ollav.app/docs/errors#SCOPE_REQUIRED", "request_id": "req_6f1c2a9b0d4e8f7a1b2c3d4e", "docs": "https://ollav.app/docs/agents#errors" } }| Code | Status | What to do |
|---|---|---|
AUTH_REQUIRED | 401 | Send Authorization: Bearer ol_live_... (or ol_test_...). If you did, the key is wrong, revoked or its user left the workspace. Ask the user for a new key. |
SCOPE_REQUIRED | 403 | The key lacks a scope (upload, read, comment). Ask the user for a key with it. |
CSRF_REQUIRED | 403 | Add X-Ollav-Client: agent to every POST, PATCH and DELETE. |
NOT_FOUND | 404 | Check the slug or id. A key only sees its own workspace, so other workspaces' documents also return 404. |
ARCHIVED | 404 | The document was archived. POST /api/documents/{slug}/unarchive restores it. |
TOO_LARGE | 413 | Over 15 MB. Compress images (WebP, downscale) and upload again. |
NOT_HTML, EMPTY_BODY, MISSING_HTML, MISSING_FILE | 400 | Send a non-empty HTML document as file=@..., a JSON html field or a text/html body. |
VALIDATION_ERROR, BAD_JSON | 400 | Fix the request body. The message names the field. |
ROLE_REQUIRED, FORBIDDEN | 403 | The user's role doesn't allow it. Tell the user instead of retrying. |
PLAN_LIMIT, PLAN_READ_ONLY | 402 | The workspace is at a limit of its plan (limit says which), or over one and read-only. Nothing was changed. Tell the user instead of retrying. |
RATE_LIMITED, TOO_MANY_REQUESTS | 429 | Wait and retry later (a test key allows 60 requests a minute). Don't loop. |
IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_USE | 409 | The Idempotency-Key was used for a different request, or its first request is still running. Use a new key for a new request. |
Retries. Send Idempotency-Key: <a fresh UUID> on every POST, PATCH and DELETE (MCP: the idempotency_key argument). If a call times out, retry it with the same key and the same body: it happens once, and the retry returns the first result. Keys last 24 hours.
Versions. Breaking changes ship as dated API versions. Your key is pinned to the version current when it was created; send Ollav-Version: 2026-11-01 to choose one explicitly. Every response names its version in the Ollav-Version header. See the changelog.
All docs
- MCP server: connect, auth, verify, every tool, troubleshooting
- Quickstart: use Ollav from your agent via MCP
- Quickstart: publish your first prototype with curl
- REST API reference: every endpoint with a curl example
- Errors: every error code, its cause and fix
- Changelog: API changes and versions
Machine-readable references
- llms.txt, the index for LLMs, and llms-full.txt, everything in one file
- OpenAPI 3.1, every endpoint an API key can call
- MCP server card, the MCP endpoint, transport and auth
- Full API reference with curl examples for every endpoint
- This page as Markdown