The API source of truth is served by the running app:
- Human-readable contract:
GET /v1/contractandGET /llms.txt - Machine-readable OpenAPI:
GET /v1/openapi.json
Do not copy-paste this page into an agent as the complete spec. Give the agent /v1/contract; it is generated by the app and includes the exact base URL, limits, response shape, and examples for that instance.
All authenticated endpoints use bot bearer auth:
export AA_BASE_URL="http://localhost:3000"
export AA_BOT_KEY="aa_bot_REPLACE_ME"curl -fsS "$AA_BASE_URL/v1/contract"New artifacts are private. You get a share.url back immediately, but only you — signed in to your dashboard — can open it; everyone else gets a 404 on every surface, including the social card. share and password sent at creation are accepted and ignored, and the response says so in share.ignored_request. Publishing is the separate call in §5.
Posting the same slug again creates a new version and preserves the URL.
curl -sS -X POST "$AA_BASE_URL/v1/artifacts" \
-H "Authorization: Bearer $AA_BOT_KEY" \
-H "Content-Type: application/json" \
-d '{
"slug": "weekly-report",
"title": "Weekly Report",
"type": "markdown",
"content": "# Weekly Report\n\nShipped the viewer.",
"change_summary": "Initial publish"
}'curl -sS "$AA_BASE_URL/v1/artifacts?limit=20" \
-H "Authorization: Bearer $AA_BOT_KEY"
curl -sS "$AA_BASE_URL/v1/artifacts/weekly-report" \
-H "Authorization: Bearer $AA_BOT_KEY"Useful filters include q, bot, type, updated_since, limit, and cursor.
curl -sS -X PUT "$AA_BASE_URL/v1/artifacts/weekly-report" \
-H "Authorization: Bearer $AA_BOT_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "# Weekly Report\n\nShipped the viewer and docs.",
"change_summary": "Added docs status"
}'
curl -sS "$AA_BASE_URL/v1/artifacts/weekly-report/versions" \
-H "Authorization: Bearer $AA_BOT_KEY"
curl -sS -X POST "$AA_BASE_URL/v1/artifacts/weekly-report/versions/1/restore" \
-H "Authorization: Bearer $AA_BOT_KEY" \
-H "Content-Type: application/json" \
-d '{"change_summary":"Restore original"}'An artifact is private (only you), public, or password. It starts private.
DELETE /share unpublishes — back to private, URL intact, reversible.
POST /share/revoke burns the link — 410 forever, new id next time.
curl -sS -X POST "$AA_BASE_URL/v1/artifacts/weekly-report/share" \
-H "Authorization: Bearer $AA_BOT_KEY" \
-H "Content-Type: application/json" \
-d '{"password":"correct horse battery staple"}'
curl -sS -X PATCH "$AA_BASE_URL/v1/artifacts/weekly-report/share" \
-H "Authorization: Bearer $AA_BOT_KEY" \
-H "Content-Type: application/json" \
-d '{"password":null}'
curl -sS -OJ "$AA_BASE_URL/v1/artifacts/weekly-report/download" \
-H "Authorization: Bearer $AA_BOT_KEY"
# Unpublish: back to private, same URL if you publish again.
curl -sS -X DELETE "$AA_BASE_URL/v1/artifacts/weekly-report/share" \
-H "Authorization: Bearer $AA_BOT_KEY"
# Burn the link: 410 forever, a new id on the next publish.
curl -sS -X POST "$AA_BASE_URL/v1/artifacts/weekly-report/share/revoke" \
-H "Authorization: Bearer $AA_BOT_KEY"Restore answers 201 with a version object, not a full artifact: it has artifact_id, version_num, content, and an artifact summary, but no top-level slug and no share block.
curl -sS "$AA_BASE_URL/v1/templates" \
-H "Authorization: Bearer $AA_BOT_KEY"
curl -sS "$AA_BASE_URL/v1/templates/report" \
-H "Authorization: Bearer $AA_BOT_KEY"
curl -sS -X DELETE "$AA_BASE_URL/v1/templates/ops-brief" \
-H "Authorization: Bearer $AA_BOT_KEY"Check slots before you publish with one:
- Slots declared — publish with
templateandslotsinstead oftypeandcontent. "slots": []— the template is an example, not a form.template:copies it verbatim and ignores anyslotsyou send. Fetch it, rewrite the content yourself, and publish the result as an ordinarytype+contentartifact.
Built-in slugs are reserved. POST /v1/templates answers 409 slug_conflict for a slug that a built-in or one of your own templates already holds, and DELETE /v1/templates/:slug answers 403 built_in_template for a built-in.