AnyRouter

Artifacts API

Publish a page or a small folder, get a stable URL, and ship new versions to it.

An artifact is a page you publish: one .html or .md file, or a small folder with its styles, scripts and images. It gets a stable URL at https://anyrouter.dev/a/{id}. Publishing again makes a new numbered version at the same URL.

Artifacts are private by default. Only you can open a private artifact. Set visibility to link to share it with anyone who has the URL.

Authenticate with an inference key (sk-ar-v1-…), a management key (ak_…), or a signed-in session. A management key needs read:artifacts to read and write:artifacts to publish, change or delete. A key restricted to specific endpoints must allow the Artifacts endpoint.

Publish one file

curl -sS https://anyrouter.dev/api/v1/artifacts \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Hello","files":{"index.html":{"content":"<h1>Hi</h1>"}}}'
{
  "id": "k3Vx9TqLm2Ab7Zcd",
  "version": 1,
  "url": "https://anyrouter.dev/a/k3Vx9TqLm2Ab7Zcd",
  "visibility": "private"
}
FieldTypeNotes
filesobjectPath to { "content": "...", "encoding": "utf8" | "base64" }. Use base64 for images and fonts. Required unless you send manifest.
titlestringUp to 120 characters. Defaults to the page's <title> or first heading.
descriptionstringUp to 300 characters.
visibilitystringprivate (default), link or public.
entry_pathstringThe page to open. Defaults to index.html, then the only .html or .md file.
labelstringA short note for this version, up to 60 characters.

An inline publish carries up to 5 MB in total. For more, or for a folder, use the manifest flow.

Publish a folder

List every file with its size and SHA-256. The response names the files the server still needs. Upload each one, then commit.

# 1. Open the upload (202)
curl -sS https://anyrouter.dev/api/v1/artifacts \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Report",
    "manifest": [
      {"path": "index.html", "size": 1204, "sha256": "<64 hex chars>"},
      {"path": "app.js", "size": 5310, "sha256": "<64 hex chars>"}
    ]
  }'
{
  "upload_id": "up_8Hq2...",
  "expires_at": "2026-10-23T12:00:00Z",
  "missing": [
    {"path": "index.html", "sha256": "…", "size": 1204,
     "put_url": "https://anyrouter.dev/api/v1/artifacts/uploads/up_8Hq2.../files/…"}
  ]
}
# 2. Upload each missing file to its put_url
curl -sS -X PUT "$PUT_URL" \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  --data-binary @index.html

# 3. Commit (201), same response as an inline publish
curl -sS -X POST https://anyrouter.dev/api/v1/artifacts/uploads/$UPLOAD_ID/commit \
  -H "Authorization: Bearer $ANYROUTER_API_KEY"

The upload is open for one hour. Committing twice is safe: the second call returns the same version. The server checks each file's SHA-256 and rejects a mismatch.

Publish a new version

Send the version you built on as base_version.

curl -sS https://anyrouter.dev/api/v1/artifacts/$ID/versions \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"base_version":1,"label":"fix chart","files":{"index.html":{"content":"<h1>Hi again</h1>"}}}'

Files you list are added or replaced. Files you leave out are kept. Set a path to null to remove it. The manifest flow accepts the same request plus delete (a list of paths to remove) and keep_unlisted: false (the manifest is the whole new file set).

If someone published a newer version first, you get 409 version_conflict with the version to merge against:

{
  "error": {
    "code": "version_conflict",
    "message": "Someone published version 2 first. Read it, merge, and retry with base_version 2.",
    "current_version": 2
  }
}

Set "force": true instead of base_version to publish on top of whatever is current.

Read, list and version history

RequestReturns
GET /api/v1/artifactsYour live artifacts, newest first. Query limit (1 to 100, default 25) and cursor; the response carries next_cursor.
GET /api/v1/artifacts/{id}Metadata, the current version and its file list. ?version=n reads an older version (owner only).
GET /api/v1/artifacts/{id}/versionsThe version history.
GET /api/v1/artifacts/{id}/files/{path}The source of one file (owner only), as a download.

Change visibility or details

curl -sS -X PATCH https://anyrouter.dev/api/v1/artifacts/$ID \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"visibility":"link"}'

You can change title, description, icon and visibility. public is currently treated the same as link.

Delete

curl -sS -X DELETE https://anyrouter.dev/api/v1/artifacts/$ID \
  -H "Authorization: Bearer $ANYROUTER_API_KEY"
{ "id": "k3Vx9TqLm2Ab7Zcd", "deleted": true }

The URL stops working for everyone else at once.

Limits

LimitValue
One file5 MB
One inline publish5 MB
One publish (manifest)50 files, 20 MB
One version100 files, 50 MB
Artifacts per account100
Stored size per account (current versions)200 MB
Publishes per hour20
Path length200 characters

Allowed file types: html, md, css, js, mjs, json, txt, csv, png, jpg, jpeg, gif, webp, avif, svg, woff2, ico. The server decides the content type from the extension and ignores the one you send.

Errors

Errors look like { "error": { "code": "...", "message": "..." } }.

StatusCodeMeaning
400invalid_requestA field is missing or has the wrong shape.
400base_version_requiredA new version needs base_version (or force).
400invalid_path, duplicate_path, invalid_entry, no_entry, no_filesThe file set is not valid.
400hash_mismatch, missing_blobs, unknown_blobThe upload does not match the manifest.
400invalid_encodingA base64 file is not valid base64.
401No valid credential.
403The key lacks the scope or the endpoint.
403quota_exceededYou reached the artifact limit.
404not_foundNo such artifact, or you cannot see it.
409version_conflictbase_version is behind. Merge and retry.
409already_committedThe upload was already used.
410upload_expiredThe upload was open longer than an hour.
413file_too_large, inline_too_large, publish_too_large, version_too_large, too_many_filesA size limit was exceeded.
415unsupported_media_typeThe file type is not allowed.
429rate_limitedMore than 20 publishes in an hour.

From the CLI

anyr artifact publish ./report            # a file or a folder
anyr artifact publish ./report --id k3Vx9TqLm2Ab7Zcd --label "fix chart"
anyr artifact publish page.html --title "Weekly" --visibility link --json

Flags: --title, --visibility private|link|public, --id (publish a new version), --label, --yes (skip the confirmation for public) and --json. The URL prints on stdout. Exit code 2 means a version conflict.

From an AI agent

The AnyRouter MCP server exposes five tools: publish_artifact, list_artifacts, get_artifact, delete_artifact and set_artifact_visibility. publish_artifact takes text files only, up to 1 MB, and publishes privately unless you say otherwise. Listing and reading need read:artifacts; the rest need write:artifacts.