{
  "openapi": "3.1.0",
  "info": {
    "title": "Papayya API",
    "version": "1.0.0",
    "summary": "Run batch AI work as items you can grade, price and re-drive one at a time.",
    "description": "Papayya is ready-made infrastructure for batch and periodic AI pipelines — a task queue whose unit is the **item**: one ticket, one document page, one row.\n\nEvery item carries its own id, input, verdict and cost, and Papayya records two independent facts about it: whether it **ran** (`status`) and whether it **worked** (`worst_outcome_status`). Those two disagreeing is the thing the API exists to expose: an item that completed and still returned a wrong answer raises no exception anywhere else in your stack.\n\n## Availability\n\nEvery operation below is served by a control plane running in **your own infrastructure** — there is no shared Papayya endpoint, so the server for those operations is the `{papayyaBaseUrl}` variable below, set to whatever hostname your install uses. They are tagged `x-papayya-availability: self-hosted`. Do not fabricate a hostname or credentials: a caller without an install needs one, which starts at https://getpapayya.com/demo.\n\n## Authentication\n\nSend `Authorization: Bearer <token>`, where the token is either a project API key (prefix `pk_`) or a session JWT. An API key is scoped to the project it was minted for and is refused with `403` against any other project. A JWT acts across the whole account by design.\n\n## Errors\n\nErrors are always JSON, never HTML: `{\"error\": {\"code\", \"message\"}}`. `code` is stable and machine-readable; `message` is for a human. Some endpoints add `hint`, `status` and `docs`.\n\n## Idempotency\n\nWrite operations that start work accept an `idempotency_key`. Repeating a request with the same key returns the original result instead of starting the work twice.",
    "termsOfService": "https://getpapayya.com/privacy",
    "contact": {
      "name": "Papayya",
      "url": "https://getpapayya.com/contact",
      "email": "kingsley@getpapayya.com"
    },
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Papayya-Terms"
    },
    "x-logo": {
      "url": "https://getpapayya.com/logo.svg",
      "altText": "Papayya"
    }
  },
  "externalDocs": {
    "description": "Papayya documentation",
    "url": "https://docs.getpapayya.com/api"
  },
  "servers": [
    {
      "url": "{papayyaBaseUrl}",
      "description": "Your Papayya control plane, running in your own infrastructure. The SDK and CLI read it from PAPAYYA_BASE_URL.",
      "variables": {
        "papayyaBaseUrl": {
          "default": "http://localhost:8090",
          "description": "The origin your install serves on. http://localhost:8090 when the stack runs on one machine."
        }
      }
    },
    {
      "url": "https://getpapayya.com",
      "description": "getpapayya.com — serves this specification and the marketing site. It exposes no API operations."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Health",
      "description": "Liveness and readiness. No authentication."
    },
    {
      "name": "Projects",
      "description": "Projects and the API keys scoped to them."
    },
    {
      "name": "Agents",
      "description": "The deployable unit of code."
    },
    {
      "name": "Runs",
      "description": "One invocation of an agent over one or many items."
    },
    {
      "name": "Items",
      "description": "The unit of verdict and of recovery: one item's input, outcome, trace and cost."
    },
    {
      "name": "Recovery",
      "description": "Triage, replay, resume and cohort actions — how a broken item gets re-driven."
    },
    {
      "name": "Schedules",
      "description": "Recurring invocations."
    },
    {
      "name": "Account",
      "description": "Account-level administration, including deletion."
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "getLiveness",
        "tags": [
          "Health"
        ],
        "summary": "Liveness probe",
        "description": "Returns `{\"status\":\"ok\"}` when the API process is up. Does not touch the database, so it stays 200 during a dependency outage.",
        "security": [],
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The process is running.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/health/ready": {
      "get": {
        "operationId": "getReadiness",
        "tags": [
          "Health"
        ],
        "summary": "Readiness probe",
        "description": "Checks Postgres and Redis and reports each. Returns 503 with the failing dependency named when either is unreachable.",
        "security": [],
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "Every dependency answered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Readiness"
                }
              }
            }
          },
          "503": {
            "description": "A dependency is unreachable; the failing one is named.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Readiness"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Projects"
        ],
        "summary": "List projects",
        "description": "Every project in the authenticated account. A project is how you separate production from staging; API keys are minted per project and cannot address another.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The account's projects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Project"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{projectId}/api-keys": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProjectId"
        }
      ],
      "get": {
        "operationId": "listApiKeys",
        "tags": [
          "Projects"
        ],
        "summary": "List a project's API keys",
        "description": "Metadata for every key minted for this project. Key plaintext is never returned — only the prefix and the creation time.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "Key metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiKey"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createApiKey",
        "tags": [
          "Projects"
        ],
        "summary": "Mint an API key for a project",
        "description": "Creates a key scoped to this project. The plaintext key is returned once, in this response, and never again — store it immediately.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "A label for the key, shown in the dashboard.",
                    "maxLength": 128
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key, including its plaintext, shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyWithSecret"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents": {
      "get": {
        "operationId": "listAgents",
        "tags": [
          "Agents"
        ],
        "summary": "List agents",
        "description": "Every agent in the account, optionally narrowed to one project. An agent is the deployable unit of code; runs execute its most recent deployment.",
        "x-papayya-availability": "self-hosted",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": false,
            "description": "Return only agents belonging to this project.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The account's agents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Agent"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Create an agent",
        "description": "Registers a new agent in a project. Creating an agent does not deploy code — push a bundle with `papayya deploy` before triggering a run.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentCreate"
              },
              "example": {
                "project_id": "1f3a6b6c-0a2e-4a0e-9c5e-2b6b1b6a3f01",
                "name": "tag-tickets",
                "slug": "tag-tickets",
                "description": "Classifies inbound support tickets"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "An agent with this slug already exists in the project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{agentId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "get": {
        "operationId": "getAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Get one agent",
        "description": "Loads an agent's configuration and its current config version. The config version is not the deployed code version — runs execute the latest deployment.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateAgent",
        "tags": [
          "Agents"
        ],
        "summary": "Update an agent's configuration",
        "description": "Changes name, description or config. This bumps the agent's config version; it does not package or deploy code, so the next run still executes the current deployment.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{agentId}/runs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AgentId"
        }
      ],
      "post": {
        "operationId": "triggerRun",
        "tags": [
          "Runs"
        ],
        "summary": "Trigger a run over a single input",
        "description": "Starts one invocation of the agent against one input. For many inputs use `submitRun`, which produces one run over many items and lets you re-drive them individually. Set `callback_url` to be POSTed on every terminal transition; deliveries are signed with `X-Papayya-Signature`.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunTriggerRequest"
              },
              "example": {
                "agent_id": "a4f7c2d1-0b3e-4a5f-8c9d-1e2f3a4b5c6d",
                "input": {
                  "text": "My invoice is wrong"
                },
                "item_id": "ticket_8173",
                "budget_cents": 100,
                "timeout_seconds": 600
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The run was accepted and queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunTriggerResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan limit reached, or the account is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/batches": {
      "post": {
        "operationId": "submitRun",
        "tags": [
          "Runs"
        ],
        "summary": "Submit one run over many items",
        "description": "Submits a run over a list of items. Each item takes its own `input`, and optionally your own `item_id` (at most 256 bytes — your ticket or document id, never derived from the input) and a `partition_key` for per-tenant rollups. The response's `group_id` is the run id you pass to `getRun` and `listRunItems`. The wire path is a frozen name; the product noun is the run.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunSubmitRequest"
              },
              "example": {
                "agent_id": "a4f7c2d1-0b3e-4a5f-8c9d-1e2f3a4b5c6d",
                "name": "nightly-ticket-tagging",
                "items": [
                  {
                    "input": {
                      "text": "My invoice is wrong"
                    },
                    "item_id": "ticket_8173",
                    "partition_key": "acme"
                  },
                  {
                    "input": {
                      "text": "Cancel my plan"
                    },
                    "item_id": "ticket_8174",
                    "partition_key": "globex"
                  }
                ],
                "callback_url": "https://your-api.example.com/callbacks/papayya"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The run was accepted; every item is queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunSubmitResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan limit reached, or the account is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v2/runs": {
      "get": {
        "operationId": "listRuns",
        "tags": [
          "Runs"
        ],
        "summary": "List runs",
        "description": "Every run in the account, newest first. `?agent=` narrows to one agent's slug; `?item_id=` answers 'which runs touched my document', which is how you get from an id you know to the run ids you do not — a re-driven item is spread across the submission that failed and the replay that fixed it.",
        "x-papayya-availability": "self-hosted",
        "parameters": [
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "description": "Return only runs of this agent, by slug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "item_id",
            "in": "query",
            "required": false,
            "description": "Return only runs that touched this customer item id.",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return. Clamped to [1, 500]; defaults to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Runs, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Run"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v2/runs/{runId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RunId"
        }
      ],
      "get": {
        "operationId": "getRun",
        "tags": [
          "Runs"
        ],
        "summary": "Get one run's rollup",
        "description": "One run's aggregate row. Read `status` for whether it ran and `worst_outcome_status` for whether it worked — they are independent, and a run can be `completed` and `degraded` at the same time. `cost_usd` is a lower bound whenever `unpriced_item_count` is non-zero.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v2/runs/{runId}/items": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RunId"
        }
      ],
      "get": {
        "operationId": "listRunItems",
        "tags": [
          "Items"
        ],
        "summary": "List a run's items",
        "description": "Every item the run processed, with its own input identity, verdict, output, error and cost. This is the endpoint that answers 'which of the thousand did not work' — filter the result on `worst_outcome_status != \"ok\"`.",
        "x-papayya-availability": "self-hosted",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return. Clamped to [1, 500]; defaults to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The run's items.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Item"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/steps": {
      "get": {
        "operationId": "searchSteps",
        "tags": [
          "Items"
        ],
        "summary": "Search steps across runs",
        "description": "Keyset-paginated search over individual steps, for questions that cut across runs: every step of one document id, everything graded `degraded` in a window, or the most expensive steps in a period.",
        "x-papayya-availability": "self-hosted",
        "parameters": [
          {
            "name": "item_id",
            "in": "query",
            "required": false,
            "description": "Exact customer item id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "item_id_prefix",
            "in": "query",
            "required": false,
            "description": "Customer item id prefix, e.g. `DOC-2026-`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "outcome_status",
            "in": "query",
            "required": false,
            "description": "Filter on the worked axis.",
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "degraded",
                "failed"
              ]
            }
          },
          {
            "name": "outcome_reason",
            "in": "query",
            "required": false,
            "description": "Filter on the reason token recorded by the grader.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_cost_usd",
            "in": "query",
            "required": false,
            "description": "Only steps costing at least this much.",
            "schema": {
              "type": "number",
              "format": "double",
              "minimum": 0
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on step time (RFC 3339).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on step time (RFC 3339).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Ordering.",
            "schema": {
              "type": "string",
              "enum": [
                "recent",
                "cost"
              ],
              "default": "recent"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a previous response's `next_cursor`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return. Clamped to [1, 200]; defaults to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching steps and a cursor when more remain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StepSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/triage": {
      "get": {
        "operationId": "listTriage",
        "tags": [
          "Recovery"
        ],
        "summary": "List items that need a person",
        "description": "The unified needs-attention feed: items that did not work and items held in quarantine, newest first, keyset-paginated. Each row names the actions it will accept in `available_actions`, so a client never has to guess which recovery endpoint applies. A row leaves the feed once it carries a disposition.",
        "x-papayya-availability": "self-hosted",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Restrict to one lane.",
            "schema": {
              "type": "string",
              "enum": [
                "dlq",
                "quarantine"
              ]
            }
          },
          {
            "name": "partition_key",
            "in": "query",
            "required": false,
            "description": "Only rows for this correlation key (your tenant id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a previous response's `next_cursor`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return. Clamped to [1, 200]; defaults to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rows awaiting a decision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TriageResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/durable/runs/{itemId}/replay": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ItemId"
        }
      ],
      "post": {
        "operationId": "replayItem",
        "tags": [
          "Recovery"
        ],
        "summary": "Re-drive one item",
        "description": "Re-executes a single item from its recorded input. Steps that already succeeded are reused rather than recomputed, so fixing one page of a 150-page document costs one page execution. The replay is a new item beside the original — the original verdict is never overwritten — and its `replayed_from` names the item it came from.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplayRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The replay was accepted; `reused_steps` says how much work was skipped.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReplayResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The item is not in a state that can be replayed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/durable/runs/{itemId}/resume": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ItemId"
        }
      ],
      "post": {
        "operationId": "resumeItem",
        "tags": [
          "Recovery"
        ],
        "summary": "Resume a paused item",
        "description": "Restarts an item that parked — because a budget fence fired, or because a verification held it — from its last successful checkpoint. Use this rather than `replayItem` when the work stopped mid-flight and you want to continue it rather than re-drive it.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "202": {
            "description": "The item was resumed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReplayResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The item is not paused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/durable/runs/{itemId}/acknowledge": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ItemId"
        }
      ],
      "post": {
        "operationId": "acknowledgeTriageItem",
        "tags": [
          "Recovery"
        ],
        "summary": "Acknowledge a triage row",
        "description": "Records that a person has seen this row and is dealing with it. The row leaves the triage feed; the item's verdict is unchanged.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The row was acknowledged and has left the feed."
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/durable/runs/{itemId}/dismiss": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ItemId"
        }
      ],
      "post": {
        "operationId": "dismissTriageItem",
        "tags": [
          "Recovery"
        ],
        "summary": "Dismiss a triage row",
        "description": "Marks the row as not worth acting on and drains it from the triage feed. The item's recorded verdict, input and output are untouched — dismissing is a decision about your attention, not about the data.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The row was dismissed and has left the feed."
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/durable/cohorts": {
      "get": {
        "operationId": "listCohort",
        "tags": [
          "Recovery"
        ],
        "summary": "Select a cohort of items by predicate",
        "description": "Selects items across runs by a predicate — agent, correlation key, outcome, error category, customer item id prefix — and returns the members with a cost note. This is the read half of bulk recovery: choose the set here, act on the same predicate with `replayCohort`.",
        "x-papayya-availability": "self-hosted",
        "parameters": [
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "description": "Agent slug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "partition_key",
            "in": "query",
            "required": false,
            "description": "Your correlation key (tenant id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "run_id",
            "in": "query",
            "required": false,
            "description": "Restrict to one run.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "item_id",
            "in": "query",
            "required": false,
            "description": "Exact customer item id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "item_id_prefix",
            "in": "query",
            "required": false,
            "description": "Customer item id prefix.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "outcome",
            "in": "query",
            "required": false,
            "description": "Filter on the worked axis.",
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "degraded",
                "failed"
              ]
            }
          },
          {
            "name": "error_category",
            "in": "query",
            "required": false,
            "description": "Closed-set error category.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "outcome_reason",
            "in": "query",
            "required": false,
            "description": "Reason token recorded by the grader.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agent_version",
            "in": "query",
            "required": false,
            "description": "Restrict to items produced by one agent version.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_triaged",
            "in": "query",
            "required": false,
            "description": "Include rows that already carry a disposition.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows to return. Clamped to [1, 500]; defaults to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The cohort's members, its total, and what re-driving it would cost.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CohortResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/durable/cohorts/replay": {
      "post": {
        "operationId": "replayCohort",
        "tags": [
          "Recovery"
        ],
        "summary": "Re-drive every item in a cohort",
        "description": "Re-drives the whole set selected by the same predicate `listCohort` takes. Read the cohort first: the response there tells you how many items you are about to re-execute and what it is estimated to cost.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CohortReplayRequest"
              },
              "example": {
                "agent": "tag-tickets",
                "outcome": "degraded",
                "partition_key": "acme"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The cohort was accepted for replay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CohortReplayResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/schedules": {
      "get": {
        "operationId": "listSchedules",
        "tags": [
          "Schedules"
        ],
        "summary": "List every schedule in the account",
        "description": "Recurring invocations across all agents, with their cron expression and next fire time.",
        "x-papayya-availability": "self-hosted",
        "responses": {
          "200": {
            "description": "The account's schedules.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Schedule"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/accounts/{accountId}/data": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountId"
        }
      ],
      "delete": {
        "operationId": "deleteAccountData",
        "tags": [
          "Account"
        ],
        "summary": "Delete workload data by item id or prefix",
        "description": "Removes stored items — inputs, outputs and traces — selected by exact customer item id or by prefix. **Runs as a dry run unless `dry_run` is explicitly false**, so you can see precisely what would be removed before anything is. Every deletion is written to an audit trail.",
        "x-papayya-availability": "self-hosted",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PurgeRequest"
              },
              "example": {
                "item_id_prefix": "DOC-2025-",
                "dry_run": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What was deleted, or — on a dry run — what would have been.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PurgeResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or a parameter was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but is scoped to a different project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource in this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or concurrent run limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Papayya project API key (prefix pk_) or session JWT",
        "description": "Send `Authorization: Bearer <token>`. An API key is scoped to the project it was minted for and is refused with 403 against any other project."
      }
    },
    "parameters": {
      "ProjectId": {
        "name": "projectId",
        "in": "path",
        "required": true,
        "description": "The project's id.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "AgentId": {
        "name": "agentId",
        "in": "path",
        "required": true,
        "description": "The agent's id.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "AccountId": {
        "name": "accountId",
        "in": "path",
        "required": true,
        "description": "The account's id.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "RunId": {
        "name": "runId",
        "in": "path",
        "required": true,
        "description": "The run's id — the `group_id` returned by `submitRun`.",
        "schema": {
          "type": "string"
        }
      },
      "ItemId": {
        "name": "itemId",
        "in": "path",
        "required": true,
        "description": "Papayya's id for one item (the `id` field on an Item), not your own `item_id`.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Every error from every Papayya endpoint has this shape. Never HTML.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable, machine-readable identifier for the failure.",
                "examples": [
                  "invalid_email",
                  "Not Found",
                  "Forbidden"
                ]
              },
              "message": {
                "type": "string",
                "description": "One sentence for a human."
              },
              "hint": {
                "type": "string",
                "description": "What to do about it. Present where the endpoint can give useful advice."
              },
              "status": {
                "type": "integer",
                "description": "The HTTP status, repeated in the body for clients that lose it."
              },
              "docs": {
                "type": "string",
                "format": "uri",
                "description": "Documentation for this class of error."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Readiness": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ready",
              "unhealthy"
            ]
          },
          "postgres": {
            "type": "string",
            "description": "`ok`, or the connection error."
          },
          "redis": {
            "type": "string",
            "description": "`ok`, or the connection error."
          }
        },
        "required": [
          "status"
        ]
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "The first characters of the key, for identification."
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "prefix"
        ]
      },
      "ApiKeyWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "description": "The plaintext key. Returned once, here, and never again."
              }
            },
            "required": [
              "key"
            ]
          }
        ]
      },
      "Agent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "URL-safe identifier, unique within the project."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "config": {
            "type": "object",
            "additionalProperties": true,
            "description": "Free-form agent configuration."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "project_id",
          "name",
          "slug"
        ]
      },
      "AgentCreate": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid",
            "description": "Project to create the agent in."
          },
          "name": {
            "type": "string",
            "description": "Human-readable name."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe identifier, unique within the project."
          },
          "description": {
            "type": "string"
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "project_id",
          "name",
          "slug"
        ]
      },
      "AgentUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "image_ref": {
            "type": "string",
            "description": "A pre-built container image to execute instead of a deployed bundle."
          }
        },
        "additionalProperties": false
      },
      "RunTriggerRequest": {
        "type": "object",
        "properties": {
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "input": {
            "description": "Whatever your agent function is called with. Any JSON value."
          },
          "item_id": {
            "type": "string",
            "maxLength": 256,
            "description": "Your own id for this item. Never derived from the input. Over 256 bytes is a 400 naming the limit."
          },
          "max_steps": {
            "type": "integer",
            "minimum": 1,
            "default": 50
          },
          "budget_cents": {
            "type": "integer",
            "minimum": 0,
            "default": 500,
            "description": "Cost cap in cents."
          },
          "budget_exceeded_behavior": {
            "type": "string",
            "enum": [
              "pause",
              "fail"
            ],
            "description": "What to do when the budget fence fires. Pausing preserves in-flight work for an operator to resume."
          },
          "timeout_seconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 86400,
            "default": 1800,
            "description": "Wall-clock timeout. The clock starts when the run goes queued -> running, not at creation."
          },
          "callback_url": {
            "type": "string",
            "format": "uri",
            "description": "POSTed on every terminal transition, signed with `X-Papayya-Signature`."
          },
          "idempotency_key": {
            "type": "string",
            "description": "Repeating a request with this key returns the original result."
          }
        },
        "required": [
          "agent_id"
        ]
      },
      "RunTriggerResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed",
              "cancelled",
              "budget_exceeded"
            ]
          },
          "current_step": {
            "type": "integer"
          },
          "total_input_tokens": {
            "type": "integer"
          },
          "total_output_tokens": {
            "type": "integer"
          },
          "total_cost_cents": {
            "type": "integer"
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "run_timeout",
              "step_timeout",
              "tool_calls_per_step_exceeded",
              "max_steps_exceeded",
              "thrash_detected",
              "budget_exceeded",
              "heartbeat_timeout",
              "orphan_container_recovered",
              "container_start_failed",
              null
            ],
            "description": "Set when the run ends in `failed`. The closed set of terminal causes."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "agent_id",
          "status"
        ]
      },
      "RunSubmitItem": {
        "type": "object",
        "properties": {
          "input": {
            "description": "What your agent function is called with, and what a replay re-executes. Any JSON value."
          },
          "item_id": {
            "type": "string",
            "maxLength": 256,
            "description": "Your own id for this item — your ticket or document id. Optional."
          },
          "partition_key": {
            "type": "string",
            "description": "Your correlation key, usually your customer's tenant id. Verdicts and cost roll up by it."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "input"
        ]
      },
      "RunSubmitRequest": {
        "type": "object",
        "properties": {
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "A label for the run."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/RunSubmitItem"
            }
          },
          "budget_cents_cap": {
            "type": "integer",
            "minimum": 0,
            "description": "Cost cap for the whole run, in cents."
          },
          "concurrency_cap": {
            "type": "integer",
            "minimum": 1,
            "description": "Maximum items in flight at once."
          },
          "callback_url": {
            "type": "string",
            "format": "uri",
            "description": "Inherited by every item. A 1,000-item run with one callback_url produces up to 1,000 deliveries — correlate on `run_id`, tell them apart on `item_id`."
          },
          "idempotency_key": {
            "type": "string"
          }
        },
        "required": [
          "agent_id",
          "items"
        ]
      },
      "RunSubmitResponse": {
        "type": "object",
        "properties": {
          "group_id": {
            "type": "string",
            "description": "The run id. Pass it to `getRun` and `listRunItems`."
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string"
          },
          "total_items": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "group_id",
          "agent_id",
          "status",
          "total_items"
        ]
      },
      "Run": {
        "type": "object",
        "description": "A run's rollup. `status` is whether it ran; `worst_outcome_status` is whether it worked. They are independent.",
        "properties": {
          "id": {
            "type": "string"
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent": {
            "type": "string",
            "description": "The agent's slug."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed"
            ],
            "description": "Did it RUN. Rollup over the run's items."
          },
          "worst_outcome_status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "failed"
            ],
            "description": "Did it WORK. Severity order ok < degraded < failed. A run can be `completed` and `degraded` at the same time — that disagreement is the point."
          },
          "item_count": {
            "type": "integer"
          },
          "queued_count": {
            "type": "integer"
          },
          "running_count": {
            "type": "integer"
          },
          "completed_count": {
            "type": "integer"
          },
          "failed_count": {
            "type": "integer"
          },
          "quarantined_count": {
            "type": "integer"
          },
          "degraded_count": {
            "type": "integer"
          },
          "cost_usd": {
            "type": "number",
            "format": "double",
            "description": "Estimated from your rate card, not a bill. A LOWER BOUND whenever `unpriced_item_count` is non-zero."
          },
          "unpriced_item_count": {
            "type": "integer",
            "description": "Items that spent tokens no rate-card entry could price. Non-zero means `cost_usd` omits them — unpriced is not free."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "agent",
          "status",
          "worst_outcome_status",
          "item_count",
          "cost_usd"
        ]
      },
      "Item": {
        "type": "object",
        "description": "One thing a run processed: its input identity, outcome, output, trace and cost. The unit of verdict and of recovery.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Papayya's id for this item. Pass it to `replayItem`."
          },
          "run_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The run this item belongs to. Null for an item spawned inside another item."
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when this item was spawned from inside another item's agent body."
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent": {
            "type": "string"
          },
          "agent_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "Did it RUN.",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed",
              "quarantine",
              "paused"
            ]
          },
          "worst_outcome_status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "failed"
            ],
            "description": "Did it WORK. Filter on this, not on `status`, to find silent failures."
          },
          "degraded_count": {
            "type": "integer"
          },
          "item_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "YOUR id for this item, as submitted."
          },
          "partition_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your correlation key."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "output": {
            "description": "What the item produced. Any JSON value."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "One line saying why it failed. Null on anything that did not fail."
          },
          "error_category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Closed-set category. Group on this, never on `error`."
          },
          "cost_usd": {
            "type": "number",
            "format": "double"
          },
          "budget_limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "llm_tokens_total": {
            "type": "integer"
          },
          "quarantined_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "quarantine_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "quarantine_disposition": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "agent",
          "status",
          "worst_outcome_status",
          "cost_usd"
        ]
      },
      "StepSearchResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass back as `?cursor=` for the next page. Null on the last page."
          }
        },
        "required": [
          "items"
        ]
      },
      "TriageRow": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "dlq",
              "quarantine"
            ],
            "description": "Which lane the row is in. Determines which action endpoints apply."
          },
          "run_id": {
            "type": "string"
          },
          "group_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "item_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "YOUR identifier for the item — the ticket or document id you submitted."
          },
          "agent": {
            "type": "string"
          },
          "partition_key": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "available_actions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "replay",
                "resume",
                "release",
                "discard",
                "dismiss",
                "acknowledge"
              ]
            },
            "description": "The actions this row will accept, computed server-side. Use it rather than inferring the endpoint from `kind`."
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "kind",
          "run_id",
          "agent",
          "status",
          "available_actions",
          "occurred_at"
        ]
      },
      "TriageResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TriageRow"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "items"
        ]
      },
      "ReplayRequest": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Why you are re-driving. Recorded on the audit trail."
          },
          "idempotency_key": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "ReplayResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The NEW item created by the replay."
          },
          "run_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "agent": {
            "type": "string"
          },
          "item_id": {
            "type": "string",
            "description": "Your id, carried over from the original."
          },
          "replayed_from": {
            "type": "string",
            "description": "The item this replay came from. The original is never overwritten."
          },
          "reused_steps": {
            "type": "integer",
            "description": "How many already-successful steps were reused instead of recomputed."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status"
        ]
      },
      "CohortMember": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "item_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "agent": {
            "type": "string"
          },
          "agent_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "partition_key": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "worst_outcome_status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "failed"
            ]
          },
          "degraded_count": {
            "type": "integer"
          },
          "cost_usd": {
            "type": "number",
            "format": "double"
          },
          "output": {},
          "input": {},
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "error_category": {
            "type": [
              "string",
              "null"
            ]
          },
          "dlq_disposition": {
            "type": [
              "string",
              "null"
            ]
          },
          "replayed_from": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "agent",
          "status",
          "worst_outcome_status"
        ]
      },
      "CohortResponse": {
        "type": "object",
        "properties": {
          "members": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CohortMember"
            }
          },
          "total": {
            "type": "integer",
            "description": "How many items match the predicate, ignoring `limit`."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when `members` is shorter than `total`."
          },
          "cost_usd": {
            "type": "number",
            "format": "double",
            "description": "Cost already spent by the cohort."
          },
          "cost_note": {
            "type": "string",
            "description": "Says in words what the cost figure includes and what it omits."
          }
        },
        "required": [
          "members",
          "total"
        ]
      },
      "CohortReplayRequest": {
        "type": "object",
        "description": "The same predicate fields `listCohort` accepts as query parameters.",
        "properties": {
          "agent": {
            "type": "string"
          },
          "partition_key": {
            "type": "string"
          },
          "run_id": {
            "type": "string"
          },
          "item_id": {
            "type": "string"
          },
          "item_id_prefix": {
            "type": "string"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "failed"
            ]
          },
          "error_category": {
            "type": "string"
          },
          "outcome_reason": {
            "type": "string"
          },
          "agent_version": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "description": "Why you are re-driving. Recorded on the audit trail."
          },
          "idempotency_key": {
            "type": "string"
          }
        }
      },
      "CohortReplayResponse": {
        "type": "object",
        "properties": {
          "replayed": {
            "type": "integer",
            "description": "How many items were accepted for replay."
          },
          "run_id": {
            "type": "string",
            "description": "The run the replays were grouped into."
          }
        },
        "required": [
          "replayed"
        ]
      },
      "Schedule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "cron": {
            "type": "string",
            "description": "Cron expression, in the schedule's timezone."
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone name."
          },
          "enabled": {
            "type": "boolean"
          },
          "next_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "agent_id",
          "cron",
          "enabled"
        ]
      },
      "PurgeRequest": {
        "type": "object",
        "description": "Select what to delete by exact item id or by prefix. At least one selector is required — there is deliberately no 'delete everything' shape.",
        "properties": {
          "item_id": {
            "type": "string",
            "description": "Delete the item with exactly this customer id."
          },
          "item_id_prefix": {
            "type": "string",
            "description": "Delete every item whose customer id starts with this."
          },
          "dry_run": {
            "type": "boolean",
            "default": true,
            "description": "Defaults to TRUE. Nothing is removed unless this is explicitly false."
          },
          "reason": {
            "type": "string",
            "description": "Recorded on the audit trail."
          }
        }
      },
      "PurgeResponse": {
        "type": "object",
        "properties": {
          "dry_run": {
            "type": "boolean",
            "description": "Whether this was a preview."
          },
          "matched": {
            "type": "integer",
            "description": "How many items the selector matched."
          },
          "deleted": {
            "type": "integer",
            "description": "How many were removed. Zero on a dry run."
          },
          "audit_id": {
            "type": "string",
            "description": "The audit-trail entry for this deletion."
          }
        },
        "required": [
          "dry_run",
          "matched",
          "deleted"
        ]
      }
    }
  }
}
