{
  "openapi": "3.0.3",
  "info": {
    "title": "OpenArtifacts",
    "version": "0.1.0",
    "description": "Publish static HTML pages and sites, get instant URLs. Errors are always {code, message, hint}.\n\nMoney: a provisional account gets 5 free publishes for its whole (30-minute) life; a claimed one gets 100 per UTC calendar month, resetting on the 1st. Past that, $0.005 per publish, $0.50 per private/unlisted artifact per month, $0.05 per GB-month of storage, and $1.00 per million page views beyond 1,000,000 views/month. The three recurring charges are applied by a monthly metering pass, so a balance can go negative; while it is, publishes answer 402. Top-ups are $1-100 (a card rail starts at $5).\n\nProvisional accounts expire 30 minutes after signup: their pages stop serving with a 410 and claiming is refused with a 410 after that point."
  },
  "servers": [
    {
      "url": "https://openartifacts.sh"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "oa_sk_<32>"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message",
          "hint"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          }
        }
      },
      "Challenge": {
        "type": "object",
        "required": [
          "code",
          "reason",
          "amount_usd",
          "accepts",
          "hint"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "payment_required"
            ]
          },
          "reason": {
            "type": "string"
          },
          "amount_usd": {
            "type": "number",
            "description": "What settles this exact request now: the price, or the price plus the account's arrears when the monthly metering pass has left the balance negative. An inline X-PAYMENT must match it exactly."
          },
          "accepts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scheme": {
                  "type": "string"
                },
                "instructions": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            }
          },
          "hint": {
            "type": "string"
          }
        }
      },
      "Artifact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "host": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "unlisted",
              "private"
            ]
          },
          "moderation_status": {
            "type": "string",
            "enum": [
              "pending",
              "review",
              "pass",
              "blocked"
            ]
          },
          "version": {
            "type": "integer"
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "month",
          "publishes",
          "storage",
          "traffic",
          "private_artifacts",
          "credits_usd",
          "status"
        ],
        "properties": {
          "month": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}$",
            "description": "UTC calendar month these counters cover, e.g. 2026-08."
          },
          "publishes": {
            "type": "object",
            "required": [
              "used",
              "free_limit",
              "free_left"
            ],
            "properties": {
              "used": {
                "type": "integer"
              },
              "free_limit": {
                "type": "integer",
                "description": "Provisional: 5 for the account's whole life. Claimed: 100 per month."
              },
              "free_left": {
                "type": "integer"
              }
            }
          },
          "storage": {
            "type": "object",
            "required": [
              "bytes",
              "gb",
              "est_monthly_cost_usd"
            ],
            "properties": {
              "bytes": {
                "type": "integer"
              },
              "gb": {
                "type": "number"
              },
              "est_monthly_cost_usd": {
                "type": "number",
                "description": "At $0.05 per GB-month, charged once a month by the metering pass."
              }
            }
          },
          "traffic": {
            "type": "object",
            "required": [
              "views",
              "free_limit",
              "overage_views"
            ],
            "properties": {
              "views": {
                "type": "integer",
                "description": "Page views this month (HTML documents, not assets)."
              },
              "free_limit": {
                "type": "integer"
              },
              "overage_views": {
                "type": "integer",
                "description": "Views past free_limit, billed at $1.00 per million."
              }
            }
          },
          "private_artifacts": {
            "type": "object",
            "required": [
              "count",
              "monthly_cost_usd"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "description": "Artifacts currently private or unlisted."
              },
              "monthly_cost_usd": {
                "type": "number",
                "description": "count x $0.50, recurring every month."
              }
            }
          },
          "credits_usd": {
            "type": "number",
            "description": "Balance. Can be negative once the metering pass has run."
          },
          "status": {
            "type": "string",
            "enum": [
              "provisional",
              "claimed"
            ]
          }
        }
      },
      "PublishResult": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "host": {
            "type": "string"
          },
          "artifact_id": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "account": {
            "type": "object",
            "properties": {
              "free_publishes_left": {
                "type": "integer"
              },
              "credits_usd": {
                "type": "number"
              },
              "expires_at": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "401. No token? POST /v1/signup to get one - no email or account needed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "402. Free quota and credit are exhausted. Retry the identical request with the X-PAYMENT header an accepts entry describes - that settles the amount and completes the request - or buy credit with POST /v1/credits and retry with no header. An accepts entry carries \"instructions\" only when that retry can settle on this deployment.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Challenge"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/v1/status": {
      "get": {
        "summary": "Liveness.",
        "responses": {
          "200": {
            "description": "Service is up."
          }
        }
      }
    },
    "/v1/signup": {
      "post": {
        "summary": "Create a provisional account and its first token. No auth, no email.",
        "responses": {
          "201": {
            "description": "Account created. Save token (shown once) and show claim_url to the human straight away: the account and its pages expire 30 minutes from now unless someone opens that URL, and claiming is refused once they have."
          },
          "429": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/publish": {
      "post": {
        "summary": "Publish a page or site. First use of a slug creates the artifact.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "X-PAYMENT",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. Pays for this one request when the account is out of quota and credit; the amount must equal the 402 amount_usd exactly (local dev: dev:<amount_usd>). The receipt comes back on X-PAYMENT-RESPONSE."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]{3,40}$"
                  },
                  "html": {
                    "type": "string",
                    "description": "Shorthand for a single index.html. Use this or files, not both."
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    },
                    "description": "Each part's filename is its path inside the site, e.g. css/app.css."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Published.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/artifacts": {
      "get": {
        "summary": "List the artifacts this account owns.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Artifact list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Artifact"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/artifacts/{slug}": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9-]{3,40}$"
          }
        }
      ],
      "get": {
        "summary": "Artifact detail, including every version.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Artifact detail with versions[]."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Change visibility. Off public is $0.50 per artifact per month: this call charges the first month, the metering pass charges every month after.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "X-PAYMENT",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. Pays for this one request when the account is out of quota and credit; the amount must equal the 402 amount_usd exactly (local dev: dev:<amount_usd>). The receipt comes back on X-PAYMENT-RESPONSE."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "visibility"
                ],
                "properties": {
                  "visibility": {
                    "type": "string",
                    "enum": [
                      "public",
                      "unlisted",
                      "private"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated. For private, signed_url carries the ?sig= a viewer needs."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      },
      "delete": {
        "summary": "Soft-delete: stops serving immediately.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/artifacts/{slug}/versions": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9-]{3,40}$"
          }
        }
      ],
      "post": {
        "summary": "Add a version. ?activate=false stages it without swapping the pointer.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "X-PAYMENT",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. Pays for this one request when the account is out of quota and credit; the amount must equal the 402 amount_usd exactly (local dev: dev:<amount_usd>). The receipt comes back on X-PAYMENT-RESPONSE."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]{3,40}$"
                  },
                  "html": {
                    "type": "string",
                    "description": "Shorthand for a single index.html. Use this or files, not both."
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    },
                    "description": "Each part's filename is its path inside the site, e.g. css/app.css."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Version created."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/v1/artifacts/{slug}/activate": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9-]{3,40}$"
          }
        }
      ],
      "post": {
        "summary": "Point the live URL at a version. Rollback = activate an older seq.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "version"
                ],
                "properties": {
                  "version": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Activated."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "summary": "Quota, credit balance, a usage summary for the month, allow_agent_join, and claim_url while provisional.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account state, with a usage summary. GET /v1/usage has the full breakdown."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "patch": {
        "summary": "Owner only. allow_agent_join decides whether POST /v1/account/join may mint a token from an invite code; humans can always join an invite link in a browser.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "allow_agent_join"
                ],
                "properties": {
                  "allow_agent_join": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "summary": "This UTC calendar month: publishes against the free allowance, stored bytes, page views, private artifacts, and the balance paying for them.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage for the current month.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/account/claim-link": {
      "post": {
        "summary": "Rotate the claim code and return a fresh claim_url.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "New claim_url."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/account/invites": {
      "post": {
        "summary": "Create an invite for this account (admin or owner, claimed accounts only). One code serves both audiences: a human opens invite_url in a browser, an agent posts the code to /v1/account/join.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "member",
                      "admin"
                    ],
                    "default": "member",
                    "description": "member: publish and mint own tokens. admin: also manage invites, members and tokens."
                  },
                  "max_uses": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100,
                    "default": 1
                  },
                  "ttl_hours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720,
                    "default": 72
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created: {code, invite_url, role, max_uses, expires_at}."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List this account’s invites, with uses and revocation state (admin or owner).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Invite list."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account/invites/{code}": {
      "parameters": [
        {
          "name": "code",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "summary": "Revoke an invite (admin or owner). Every unused copy of the link stops working.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account/join": {
      "post": {
        "summary": "Redeem an invite code as an agent. No auth: the code is the credential. Returns a token on THAT account - publishes on it draw the shared allowance and bill the shared balance, so tell the human which account (by handle) you are publishing to.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Joined: {token, account:{handle}, scopes, hint}. The token is shown once and carries artifacts:read and artifacts:write."
          },
          "403": {
            "description": "The owner turned agent joining off (allow_agent_join = false). A human can still open invite_url in a browser; an agent needs a token minted for it instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account/members": {
      "get": {
        "summary": "Who publishes here: every member with role and join date, plus the account’s active tokens (id, name, scopes, created_by, last_used_at).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Members and active tokens."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/account/members/{user_id}": {
      "parameters": [
        {
          "name": "user_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "summary": "Remove a member (admin or owner) and revoke every token they created. The owner cannot be removed; a non-owner may remove themselves.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Removed, with the number of tokens revoked."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/invite/{code}": {
      "parameters": [
        {
          "name": "code",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "The page a human opens to join an account: which account, which role, and a Join button. Signed out, it links to sign-in and comes back here.",
        "responses": {
          "200": {
            "description": "text/html"
          }
        }
      },
      "post": {
        "summary": "Join the account behind this invite, using the dashboard session cookie. Idempotent for an existing member; redirects to /dashboard.",
        "responses": {
          "302": {
            "description": "Joined; redirected to the dashboard."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error. Always {code, message, hint}; the hint names the next call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/tokens": {
      "get": {
        "summary": "List tokens (never their secrets).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Token list."
          }
        }
      },
      "post": {
        "summary": "Mint a token. The secret is shown exactly once.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "scopes": {
                    "type": "string"
                  },
                  "artifact_id": {
                    "type": "string"
                  },
                  "expires_at": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Token minted."
          }
        }
      }
    },
    "/v1/tokens/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "summary": "Revoke a token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked."
          }
        }
      }
    },
    "/v1/credits": {
      "post": {
        "summary": "Buy credit ($1-100; a real card rail starts at $5). Without X-PAYMENT you get the 402 challenge; with it, the rail settles and the balance moves.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "X-PAYMENT",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Local dev: dev:<amount_usd>, matching the body exactly."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_usd"
                ],
                "properties": {
                  "amount_usd": {
                    "type": "number",
                    "minimum": 1,
                    "maximum": 100
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Settled: {credits_usd, receipt}."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/v1/reports": {
      "post": {
        "summary": "Report abuse on a host. No auth.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "host"
                ],
                "properties": {
                  "host": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Report recorded."
          }
        }
      }
    },
    "/v1/moderation/{slug}": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9-]{3,40}$"
          }
        }
      ],
      "post": {
        "summary": "DEV_MODE only. Dashboard moderator override; requires the oa_sess session cookie.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "pass",
                      "block"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status updated in D1 and at the edge."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "summary": "MCP streamable HTTP endpoint (stateless). Tools: publish_artifact, list_artifacts, get_artifact, rollback_artifact, delete_artifact, get_account, create_claim_link, create_invite, add_credits.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "JSON-RPC response."
          }
        }
      }
    },
    "/skill": {
      "get": {
        "summary": "The Claude skill package (SKILL.md).",
        "responses": {
          "200": {
            "description": "text/markdown"
          }
        }
      }
    },
    "/skill/reference.md": {
      "get": {
        "summary": "Full endpoint reference for agents.",
        "responses": {
          "200": {
            "description": "text/markdown"
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "summary": "Short agent guide.",
        "responses": {
          "200": {
            "description": "text/plain"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "summary": "This document.",
        "responses": {
          "200": {
            "description": "application/json"
          }
        }
      }
    }
  }
}