{
  "openapi": "3.1.0",
  "info": {
    "title": "entities.ca API",
    "version": "1.0.0-beta",
    "description": "Evidence-backed lookup and observed-change access for the Canadian Entity Graph. Public records retain source provenance, dataset generation and observation coverage."
  },
  "servers": [
    { "url": "https://entities.ca/api/v1" }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "Inspect API availability and the active dataset build",
        "responses": {
          "200": {
            "description": "The API contract and atomically active dataset generation.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiIndex" } } }
          },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/entities/search": {
      "get": {
        "summary": "Search current entity names",
        "description": "Searches the complete public entity file through an accent- and punctuation-normalized prefix index. It does not fuzzy-merge or infer identity.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "minLength": 2, "maxLength": 120 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded current-name matches.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntitySearch" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/entities/{ceid}": {
      "get": {
        "summary": "Retrieve one complete public entity record",
        "parameters": [
          { "$ref": "#/components/parameters/Ceid" }
        ],
        "responses": {
          "200": {
            "description": "The current privacy-gated entity record and serving metadata.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntityResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/entities/{ceid}/changes": {
      "get": {
        "summary": "List observed changes for one entity",
        "parameters": [
          { "$ref": "#/components/parameters/Ceid" },
          { "$ref": "#/components/parameters/After" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Source" },
          { "$ref": "#/components/parameters/Kind" }
        ],
        "responses": {
          "200": {
            "description": "A cursor-paged change feed.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChangeFeed" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/identifiers/{scheme}/{value}": {
      "get": {
        "summary": "Resolve an exact public identifier",
        "description": "Returns a record only when the identifier resolves unambiguously. The API refuses to guess when more than one entity holds the value.",
        "parameters": [
          {
            "name": "scheme",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$" }
          },
          {
            "name": "value",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "maxLength": 256 }
          }
        ],
        "responses": {
          "200": {
            "description": "The unambiguously resolved entity record.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EntityResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "The identifier is ambiguous.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/changes": {
      "get": {
        "summary": "List observed changes across the public graph",
        "description": "A later snapshot never claims to reconstruct changes during an unobserved gap.",
        "parameters": [
          { "$ref": "#/components/parameters/After" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Source" },
          { "$ref": "#/components/parameters/Kind" }
        ],
        "responses": {
          "200": {
            "description": "A cursor-paged change feed.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChangeFeed" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/sources/health": {
      "get": {
        "summary": "Inspect source freshness and observation coverage",
        "responses": {
          "200": {
            "description": "Current capture health for each enabled source.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SourceHealth" } } }
          },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Ceid": {
        "name": "ceid",
        "in": "path",
        "required": true,
        "description": "Permanent entities.ca Canadian Entity Identifier.",
        "schema": { "type": "string", "pattern": "^CA-[ELG]-[0-9A-Z]{4}-[0-9A-Z]{4}$" }
      },
      "After": {
        "name": "after",
        "in": "query",
        "schema": { "type": "integer", "minimum": 0, "default": 0 }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
      },
      "Source": {
        "name": "source",
        "in": "query",
        "schema": { "type": "string" }
      },
      "Kind": {
        "name": "kind",
        "in": "query",
        "schema": { "type": "string", "enum": ["appeared", "vanished", "changed"] }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The identifier or paging input is invalid.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "No current public record matches the request.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unavailable": {
        "description": "The indexed evidence layer is temporarily unavailable.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "ApiIndex": {
        "type": "object",
        "required": ["schema", "status", "stability", "activeBuild", "documentation", "openapi", "resources"],
        "properties": {
          "schema": { "const": "entities-ca.api.v1" },
          "status": { "const": "available" },
          "stability": { "const": "public-beta" },
          "activeBuild": {
            "type": "object",
            "required": ["id", "builtAt", "publishedEntities", "observations", "recordDepthVersion", "publicFootprintVersion"],
            "properties": {
              "id": { "type": "string" },
              "builtAt": { "type": "string", "format": "date-time" },
              "publishedEntities": { "type": "integer", "minimum": 0 },
              "observations": { "type": "integer", "minimum": 0 },
              "recordDepthVersion": { "type": "string" },
              "publicFootprintVersion": { "type": "string" }
            }
          },
          "documentation": { "type": "string", "format": "uri" },
          "openapi": { "type": "string", "format": "uri" },
          "resources": { "type": "array", "items": { "type": "string" } }
        }
      },
      "EntitySearch": {
        "type": "object",
        "required": ["schema", "query", "normalizedQuery", "data", "page"],
        "properties": {
          "schema": { "const": "entities-ca.entity-search.v1" },
          "query": { "type": "string" },
          "normalizedQuery": { "type": "string" },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["ceid", "name", "kind", "jurisdiction", "recordDepth", "links"],
              "properties": {
                "ceid": { "type": "string" },
                "name": { "type": "string" },
                "legalName": { "type": ["string", "null"] },
                "kind": { "type": "string" },
                "status": { "type": ["string", "null"] },
                "jurisdiction": { "type": "string" },
                "place": { "type": ["string", "null"] },
                "recordDepth": { "type": "integer", "minimum": 0, "maximum": 100 },
                "links": { "type": "object", "additionalProperties": { "type": "string" } }
              }
            }
          },
          "page": { "type": "object", "additionalProperties": true }
        }
      },
      "EntityResponse": {
        "type": "object",
        "required": ["schema", "asOf", "data", "meta", "links"],
        "properties": {
          "schema": { "const": "entities-ca.entity.v1" },
          "asOf": { "type": "string", "format": "date-time" },
          "data": { "$ref": "#/components/schemas/Entity" },
          "meta": { "$ref": "#/components/schemas/EntityMeta" },
          "links": { "type": "object", "additionalProperties": { "type": "string" } }
        }
      },
      "Entity": {
        "type": "object",
        "required": ["ceid", "name", "kind", "identifiers", "sources", "recordDepth", "publicFootprint"],
        "properties": {
          "ceid": { "type": "string" },
          "name": { "type": "string" },
          "legalName": { "type": ["string", "null"] },
          "alternateNames": { "type": "array", "items": { "type": "string" } },
          "kind": { "type": "string" },
          "status": { "type": ["string", "null"] },
          "primaryJurisdiction": { "type": "string" },
          "places": { "type": "array", "items": { "type": "string" } },
          "identifiers": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "registrations": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "licences": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "timeline": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "sources": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "recordDepth": { "type": "object", "additionalProperties": true },
          "publicFootprint": { "type": "object", "additionalProperties": true }
        },
        "additionalProperties": true
      },
      "EntityMeta": {
        "type": "object",
        "required": ["buildId", "recordDepthVersion", "publicFootprintVersion", "recordSha256", "sourceCount", "observationCoverage"],
        "properties": {
          "buildId": { "type": "string" },
          "recordDepthVersion": { "type": "string" },
          "publicFootprintVersion": { "type": "string" },
          "recordSha256": { "type": "string", "pattern": "^[a-f0-9]{64}$", "description": "SHA-256 of the exact canonical entity JSON bytes read from immutable storage." },
          "sourceCount": { "type": "integer", "minimum": 0 },
          "evidenceCount": { "type": "integer", "minimum": 0 },
          "observationCoverage": { "type": "object", "additionalProperties": true }
        }
      },
      "ChangeFeed": {
        "type": "object",
        "required": ["schema", "data", "page", "filters", "links"],
        "properties": {
          "schema": { "const": "entities-ca.change-feed.v1" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Change" } },
          "page": { "type": "object", "additionalProperties": true },
          "filters": { "type": "object", "additionalProperties": true },
          "links": { "type": "object", "additionalProperties": true }
        }
      },
      "Change": {
        "type": "object",
        "required": ["cursor", "ceid", "sourceId", "kind", "observedAt", "evidence"],
        "properties": {
          "cursor": { "type": "integer" },
          "ceid": { "type": "string" },
          "sourceId": { "type": "string" },
          "kind": { "type": "string", "enum": ["appeared", "vanished", "changed"] },
          "field": { "type": ["string", "null"] },
          "observedAt": { "type": "string", "format": "date-time" },
          "evidence": { "type": "object", "additionalProperties": true }
        }
      },
      "SourceHealth": {
        "type": "object",
        "required": ["schema", "data"],
        "properties": {
          "schema": { "const": "entities-ca.source-health.v1" },
          "data": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
        }
      },
      "Error": {
        "type": "object",
        "required": ["schema", "error"],
        "properties": {
          "schema": { "const": "entities-ca.error.v1" },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" }
            }
          }
        }
      }
    }
  }
}
