{
  "openapi": "3.1.0",
  "info": {
    "title": "epcis.dev",
    "version": "0.1.0",
    "description": "An EPCIS 2.0 capture gateway: validated against GS1's pinned official schema, gateway-stamped (who = attested observer; capturedBy = warrantor account — never collapsed), hashed by CBV 2.0 §8.9, append-only. One door for people and agents."
  },
  "servers": [
    {
      "url": "https://epcis.dev"
    }
  ],
  "paths": {
    "/events": {
      "get": {
        "operationId": "listCollection",
        "summary": "The demo events collection — clearly-labeled seed records; typed OK | EMPTY | BLOCKED on one pathname, branching on its query",
        "description": "The keyless, branching collection (AXP Clauses 4 + 7): plain GET answers 200 OK with substantive typed content to an anonymous caller; a non-matching filter answers 200 EMPTY; a reserved scope answers 403 BLOCKED. Collection member name: \"events\".",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "filter the collection by kind; a non-matching value answers a typed 200 EMPTY, never a fake success"
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "filter the collection by tag; a non-matching value answers a typed 200 EMPTY, never a fake success"
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "reserved scopes (admin, internal) answer a typed 403 BLOCKED"
          }
        ],
        "responses": {
          "200": {
            "description": "OK or EMPTY envelope",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/OkEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/EmptyEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "BLOCKED envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlockedEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/pricing": {
      "get": {
        "operationId": "getPricing",
        "summary": "The Pricing Document (AXP Appendix A.2)",
        "description": "This API is free: {\"model\":\"free\"} — the declaration itself is the obligation (the no-ask-zone law). This price is NOT bound by published terms: it is a stated intent, and the document says so in its `binding` and `statement` members. Budget against it; do not contract on it.",
        "responses": {
          "200": {
            "description": "the Pricing Document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PricingDocument"
                }
              }
            }
          }
        }
      }
    },
    "/family.json": {
      "get": {
        "operationId": "getFamilyRegistry",
        "summary": "The family registry — sibling properties and their seams as typed edges",
        "description": "Lists the sibling doors of this property's family so an agent at this door discovers the others as contracts, not links.",
        "responses": {
          "200": {
            "description": "the family registry",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/translate": {
      "post": {
        "summary": "EPCIS 1.1/1.2/2.0 XML in, EPCIS 2.0 JSON-LD out, with a per-job round-trip fidelity report.",
        "description": "EPCIS 1.1/1.2/2.0 XML in, EPCIS 2.0 JSON-LD out, with a per-job round-trip fidelity report. No key, no account: this door answers on https://epcis.dev today. Typed refusals only: epcisException:ValidationException, epcisException:UnsupportedMediaTypeException, epcisException:CaptureLimitExceededException.",
        "requestBody": {
          "required": true,
          "description": "An EPCIS 1.1, 1.2 or 2.0 XML document.",
          "content": {
            "application/xml": {
              "schema": {
                "type": "string"
              }
            },
            "text/xml": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ document, fidelityReport, meteredEvents, sourceSchemaVersion }",
            "headers": {
              "GS1-EPCIS-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "GS1-CBV-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "GS1-Extensions": {
                "schema": {
                  "type": "string"
                }
              },
              "Spine-Translation-Fidelity": {
                "schema": {
                  "type": "string"
                }
              },
              "Spine-Translation-Lossy-Paths": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "type",
                    "document",
                    "fidelityReport",
                    "meteredEvents",
                    "sourceSchemaVersion"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "const": "OK",
                      "description": "AXP typed-envelope marker — a substantive 200 answers type: \"OK\"."
                    },
                    "document": {
                      "type": "object",
                      "description": "The EPCIS 2.0 JSON-LD document."
                    },
                    "fidelityReport": {
                      "type": "object",
                      "description": "The per-job round-trip fidelity report (roundTripClean, lossyPaths)."
                    },
                    "meteredEvents": {
                      "type": "integer"
                    },
                    "sourceSchemaVersion": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "epcisException:ValidationException — translation failed; the fidelity report and per-path schema errors ride the problem body",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "epcisException:CaptureLimitExceededException — payload exceeds the file-size limit",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "epcisException:UnsupportedMediaTypeException — this door accepts application/xml or text/xml; POST JSON to /validate or /hash",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/validate": {
      "post": {
        "summary": "An EPCIS 2.0 document or bare event in; the verdict and per-path errors against the pinned official GS1 EPCIS 2.0.1 schema out.",
        "description": "An EPCIS 2.0 document or bare event in; the verdict and per-path errors against the pinned official GS1 EPCIS 2.0.1 schema out. No key, no account: this door answers on https://epcis.dev today. Typed refusals only: epcisException:ValidationException, epcisException:UnsupportedMediaTypeException, epcisException:CaptureLimitExceededException.",
        "requestBody": {
          "required": true,
          "description": "An EPCISDocument or a bare EPCIS event object.",
          "content": {
            "application/json": {
              "schema": {}
            },
            "application/ld+json": {
              "schema": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ valid, schema: { artefact, version, sha256 }, errors: [{ path, keyword, message }] }",
            "headers": {
              "GS1-EPCIS-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "GS1-CBV-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "GS1-Extensions": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "type",
                    "valid",
                    "schema",
                    "errors"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "const": "OK",
                      "description": "AXP typed-envelope marker — a substantive 200 answers type: \"OK\"."
                    },
                    "valid": {
                      "type": "boolean"
                    },
                    "schema": {
                      "type": "object",
                      "description": "The pinned official schema the verdict is against.",
                      "properties": {
                        "artefact": {
                          "type": "string",
                          "const": "epcis-json-schema.json"
                        },
                        "version": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string",
                          "description": "The sha256 pin of the official schema (vendor/gs1/PINS.json).",
                          "const": "0f46ff694efffd8d8ce840a33dfde84228add11b516b8b258f3200740ae210af"
                        }
                      }
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "keyword": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "epcisException:ValidationException — malformed JSON",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "epcisException:CaptureLimitExceededException — payload exceeds the file-size limit",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "epcisException:UnsupportedMediaTypeException — this door accepts application/json or application/ld+json; POST XML to /translate",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/hash": {
      "post": {
        "summary": "An event, an array of events, or an EPCISDocument in; CBV 2.0 §8.9 event hashes out, as ni:///sha-256 URIs.",
        "description": "An event, an array of events, or an EPCISDocument in; CBV 2.0 §8.9 event hashes out, as ni:///sha-256 URIs. No key, no account: this door answers on https://epcis.dev today. Typed refusals only: epcisException:ValidationException, epcisException:UnsupportedMediaTypeException, epcisException:CaptureLimitExceededException.",
        "requestBody": {
          "required": true,
          "description": "A single EPCIS event, an array of events, or an EPCISDocument with epcisBody.eventList.",
          "content": {
            "application/json": {
              "schema": {}
            },
            "application/ld+json": {
              "schema": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ algorithm, count, hashes: [ni:///sha-256;…?ver=CBV2.0] }",
            "headers": {
              "GS1-EPCIS-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "GS1-CBV-Version": {
                "schema": {
                  "type": "string"
                }
              },
              "GS1-Extensions": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "type",
                    "algorithm",
                    "count",
                    "hashes"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "const": "OK",
                      "description": "AXP typed-envelope marker — a substantive 200 answers type: \"OK\"."
                    },
                    "algorithm": {
                      "type": "string",
                      "const": "CBV 2.0 §8.9 event hash"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hashes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "ni:///sha-256;…?ver=CBV2.0"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "epcisException:ValidationException — malformed JSON, or no EPCIS events found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "epcisException:CaptureLimitExceededException — payload exceeds the file-size limit",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "epcisException:UnsupportedMediaTypeException — this door accepts application/json or application/ld+json; POST XML to /translate",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "description": "RFC 7807 application/problem+json carrying the standard's own epcisException:* types. Match on type, never on prose.",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "The epcisException:* problem type."
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "summary": "The MCP agent door: JSON-RPC 2.0 on this origin. initialize, tools/list and ping answer with no key; the translate tool answers with no key; the keyed tools (capture, query, get_event, trace) answer with a capture key and, without one, answer the typed epcisException:SecurityException 401 as a tool-level error (isError: true).",
        "description": "The MCP agent door: JSON-RPC 2.0 on this origin. initialize, tools/list and ping answer with no key; the translate tool answers with no key; the keyed tools (capture, query, get_event, trace) answer with a capture key and, without one, answer the typed epcisException:SecurityException 401 as a tool-level error (isError: true). No key, no account: this door answers on https://epcis.dev today. Typed refusals only: epcisException:SecurityException.",
        "requestBody": {
          "required": true,
          "description": "A JSON-RPC 2.0 request envelope: { jsonrpc: \"2.0\", id, method, params }.",
          "content": {
            "application/json": {
              "schema": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC 2.0 response envelope; a door refusal is a tool result with isError: true mirroring the gateway's problem document, not a JSON-RPC error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A JSON-RPC 2.0 response envelope. A door refusal is a tool result with isError: true mirroring the gateway's problem document — it is not a JSON-RPC error."
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "OkEnvelope": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "const": "OK"
          }
        },
        "description": "200 — substantive content. The collection member name (results/items/events…) is this API's own choice, documented on the operation."
      },
      "EmptyEnvelope": {
        "type": "object",
        "required": [
          "type",
          "message"
        ],
        "properties": {
          "type": {
            "const": "EMPTY"
          },
          "message": {
            "type": "string"
          }
        },
        "description": "200 — a truthful empty collection, never a bare [] masquerading as data."
      },
      "BlockedEnvelope": {
        "type": "object",
        "required": [
          "type",
          "reason"
        ],
        "properties": {
          "type": {
            "const": "BLOCKED"
          },
          "reason": {
            "type": "string"
          }
        },
        "description": "401/403 — a permission boundary with a worded reason."
      },
      "OfferEnvelope": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "const": "OFFER"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "price": {},
          "checkoutUrl": {
            "type": "string"
          },
          "alternatives": {
            "type": "array"
          }
        },
        "description": "402 — a payment or ceiling re-authorization boundary; an offer to proceed, never a refusal."
      },
      "PricingDocument": {
        "type": "object",
        "required": [
          "model"
        ],
        "properties": {
          "model": {
            "enum": [
              "free",
              "metered"
            ]
          },
          "hardCeiling": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "unit": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "binding": {
            "type": "boolean",
            "description": "Whether published terms bind this price. `model` answers what it costs; `binding` answers whether you can hold us to it. Absent means not declared — never assume bound."
          },
          "statement": {
            "type": "string",
            "description": "Present when binding is false: the stated intent, in the same words the human pages use."
          },
          "termsUrl": {
            "type": "string",
            "description": "Present when binding is true: the terms document that binds this price."
          },
          "ledgerUrl": {
            "type": "string",
            "description": "Where the open item to bind this price is tracked."
          }
        },
        "description": "AXP Appendix A.2 — closed model \"free\" | \"metered\"; hardCeiling required and > 0 when metered. `binding` is a descriptive member on an axis orthogonal to `model`: binding: true carries termsUrl, binding: false carries statement."
      }
    }
  }
}