# artidrop — Full API Reference > The publishing layer for AI agents. Publish HTML, Markdown, or multi-file sites and get back a shareable URL. ## Base URL https://artidrop.ai ## Authentication Authentication is **optional** for the core endpoints (publish, get, versions). Three options: - **API key**: Pass `Authorization: Bearer sk-xxx` header. Create keys at https://artidrop.ai after logging in. - **Anonymous**: No auth needed. Anonymous publishes are rate-limited to 5/hour. - **AFAuth (signed requests)**: Agents sign requests with an Ed25519 keypair per [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421). Signup requires a §10 attestation token proving a human (the `AFAuth-Attestation` header); the first attested request provisions an artidrop account owned by the agent's `did:key`. No portal account, no API key. See the **Agents** section below for the full flow. ## Rate Limits | Operation | Authenticated | Anonymous | |-----------|--------------|-----------| | Publish (POST) | 60/hour | 5/hour per IP | | Read (GET) | 300/hour | 300/hour per IP | Response headers on every request: - `X-RateLimit-Limit`: Max requests in the window - `X-RateLimit-Remaining`: Requests remaining - `X-RateLimit-Reset`: Unix timestamp when the window resets - `Retry-After`: Seconds to wait (only on 429 responses) ## Error Format All errors return JSON: ```json { "error": { "code": "ERROR_CODE", "message": "Human-readable description", "details": {} } } ``` Error codes: - `VALIDATION_ERROR` (400) — Invalid input (empty content, oversized fields, bad format) - `NOT_FOUND` (404) — Artifact does not exist - `FORBIDDEN` (403) — Private artifact, not the owner - `RATE_LIMITED` (429) — Too many requests (details.retry_after has seconds to wait) - `INTERNAL_ERROR` (500) — Server error --- ## Endpoints ### POST /v1/artifacts — Publish content Create a new artifact and get back a shareable URL. No authentication required. **Request body** (JSON): | Field | Type | Required | Description | |-------|------|----------|-------------| | content | string | yes | HTML or Markdown content (min 1 char) | | format | string | yes | `"html"` or `"markdown"` | | title | string | no | Title, max 200 chars (auto-extracted from content if omitted) | | description | string | no | Description, max 500 chars (auto-extracted if omitted) | | visibility | string | no | `"public"`, `"unlisted"` (default), or `"private"` | | images | array | no | Images for markdown content (see below) | When `format` is `"markdown"`, you can include an `images` array to publish markdown with embedded images. Each entry has: - `path` (string): Relative path as referenced in the markdown (e.g., `"images/demo.png"`) - `data` (string): Base64-encoded image file data The images will be stored alongside the markdown and rendered inline. Image paths in the markdown (``) will resolve automatically. **Response** (201 Created): ```json { "id": "art_a1b2c3d4", "url": "https://artidrop.ai/a/wV8i4fgdwP", "content_url": "https://content.artidrop.ai/wV8i4fgdwP", "title": "My Report", "description": "A summary of findings...", "format": "html", "visibility": "unlisted", "version": 1, "size_bytes": 4523, "file_count": 1, "created_at": "2026-01-15T10:30:00.000Z", "updated_at": "2026-01-15T10:30:00.000Z", "owner": { "id": "usr_anonymous" } } ``` **Example**: ```bash curl -X POST https://artidrop.ai/v1/artifacts \ -H "Content-Type: application/json" \ -d '{"content": "
World
", "format": "html"}' ``` --- ### POST /v1/artifacts/upload — Upload a multi-file site Upload a ZIP archive containing HTML, CSS, JS, images, and other assets as a complete site. The ZIP must include an `index.html` at its root. No authentication required. **Request body** (multipart/form-data): | Field | Type | Required | Description | |-------|------|----------|-------------| | file | file | yes | ZIP file containing the site | | title | string | no | Site title | | description | string | no | Short description | | visibility | string | no | `"public"`, `"unlisted"` (default), or `"private"` | | format | string | no | Set to `"markdown"` to upload a markdown file with images as a ZIP | **Limits**: Max 100MB uncompressed, max 2000 files, max 50MB per file. **Response** (201 Created): Same as above, plus an `entry_file` field. Format will be `"site"` (or `"markdown"` when `format=markdown`). The `file_count` field reflects the number of files in the ZIP. **Example**: ```bash curl -X POST https://artidrop.ai/v1/artifacts/upload \ -F "file=@./my-site.zip" \ -F "title=My Portfolio" ``` Or via CLI: ```bash artidrop publish ./my-site/ # Publish a directory artidrop publish ./my-site.zip # Publish a ZIP file ``` --- ### GET /v1/artifacts/:id — Get artifact metadata Returns metadata including the shareable URL. Works with either the full ID (`art_xxx`) or the short ID. **Response** (200 OK): Same shape as the publish response above. **Example**: ```bash curl https://artidrop.ai/v1/artifacts/wV8i4fgdwP ``` --- ### GET /v1/artifacts/:id/versions — List version history Returns all versions of an artifact. **Response** (200 OK): ```json { "items": [ { "version": 2, "size_bytes": 5100, "created_at": "2026-01-16T08:00:00.000Z" }, { "version": 1, "size_bytes": 4523, "created_at": "2026-01-15T10:30:00.000Z" } ], "total": 2 } ``` **Example**: ```bash curl https://artidrop.ai/v1/artifacts/wV8i4fgdwP/versions ``` --- ## Content Negotiation Artifact page URLs (`https://artidrop.ai/a/