{
  "openapi": "3.1.0",
  "info": {
    "title": "myTDH API",
    "version": "2.0.0",
    "description": "Public artist-specific collector holding-time registers. The primary metric is an adaptation of abTDH, not oTDH."
  },
  "servers": [{ "url": "https://mytdh.xyz" }],
  "paths": {
    "/api/health": { "get": { "operationId": "getHealth", "summary": "Check service health", "responses": { "200": { "description": "Service is available" } } } },
    "/api/methodology": { "get": { "operationId": "getMethodology", "summary": "Read the collector-register methodology", "responses": { "200": { "description": "Methodology declaration" } } } },
    "/api/raster-collector-tdh": {
      "get": {
        "operationId": "listArtistCollectors",
        "summary": "List an artist's supply-weighted collector holding-time register",
        "description": "Returns every eligible collector record in the requested page. Scores are uninterrupted current-work days weighted inversely by edition size. This endpoint does not return oTDH.",
        "parameters": [
          { "name": "profile", "in": "query", "required": true, "schema": { "type": "string", "format": "uri" }, "example": "https://www.raster.art/artist/casey-reas" },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 5000, "default": 100 } },
          { "name": "query", "in": "query", "schema": { "type": "string" }, "description": "Optional collector name or wallet filter" }
        ],
        "responses": {
          "200": { "description": "Published collector register", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectorRegister" } } } },
          "400": { "description": "Invalid profile or data failure" },
          "404": { "description": "No published register for this Raster artist" }
        }
      }
    },
    "/api/raster-tdh": {
      "get": {
        "deprecated": true,
        "operationId": "listArtistCollectorsLegacyAlias",
        "summary": "Legacy alias for /api/raster-collector-tdh",
        "responses": { "200": { "description": "Published collector register" } }
      }
    },
    "/api/raster-collector-jobs": {
      "post": {
        "operationId": "startArtistCollectorRegister",
        "summary": "Start or reuse a background Raster collector-register job",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["profile"], "properties": { "profile": { "type": "string", "format": "uri" } } } } }
        },
        "responses": {
          "200": { "description": "A cached register is ready" },
          "202": { "description": "The register is queued or running" },
          "400": { "description": "Invalid Raster profile" }
        }
      }
    },
    "/api/raster-collector-jobs/{id}": {
      "get": {
        "operationId": "getArtistCollectorRegisterJob",
        "summary": "Read background register progress",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Current job state and progress" }, "404": { "description": "Job not found" } }
      }
    },
    "/api/calculate": {
      "post": {
        "operationId": "calculateArtistTdhFromDeclaredData",
        "summary": "Low-level calculation endpoint for a caller-supplied dataset",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } },
        "responses": { "200": { "description": "Calculation result" }, "400": { "description": "Invalid input" } }
      }
    }
  },
  "components": {
    "schemas": {
      "Collector": {
        "type": "object",
        "required": ["name", "address", "works_held", "tdh", "daily_rate", "rank"],
        "properties": {
          "name": { "type": "string" }, "address": { "type": "string" }, "works_held": { "type": "integer" },
          "raw_work_days": { "type": "integer" }, "first_acquired_at": { "type": ["string", "null"], "format": "date-time" },
          "last_acquired_at": { "type": ["string", "null"], "format": "date-time" }, "tdh": { "type": "integer" },
          "daily_rate": { "type": "integer" }, "rank": { "type": "integer" }
        }
      },
      "CollectorRegister": {
        "type": "object",
        "required": ["covered", "methodology", "profile", "artist", "corpus", "collectors", "pagination"],
        "properties": {
          "covered": { "type": "boolean", "const": true }, "methodology": { "type": "string", "const": "raster-artist-abtdh/1" },
          "profile": { "type": "string", "format": "uri" }, "snapshotAt": { "type": "string", "format": "date-time" },
          "metric": { "type": "object", "additionalProperties": true }, "artist": { "type": "object", "additionalProperties": true },
          "corpus": { "type": "object", "additionalProperties": true }, "method": { "type": "object", "additionalProperties": true },
          "collectors": { "type": "array", "items": { "$ref": "#/components/schemas/Collector" } },
          "pagination": { "type": "object", "additionalProperties": true }
        }
      }
    }
  }
}
