{
  "openapi": "3.1.0",
  "info": {
    "title": "confBuild ERP Handover API",
    "version": "1.0.0",
    "description": "Pilot API for immutable, owner-released BOM snapshots. Read access works without an editor session. No native ERP connector, engineering certification, prices or automatic configuration generation. Owner publication uses the canonical displayed BOM; attached CAD/PDF revision matching is publisher-attested."
  },
  "servers": [
    {
      "url": "https://app.confbuild.com/api/v1",
      "description": "Production route after backend and Hosting deployment"
    }
  ],
  "paths": {
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "security": [],
        "responses": {
          "200": {
            "description": "This OpenAPI document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/releases": {
      "get": {
        "operationId": "listReleases",
        "summary": "List project releases in ascending sequence order",
        "security": [
          {
            "ProjectKey": []
          },
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,12}$",
              "default": "0"
            },
            "description": "Last processed sequence. Save it for polling, including when nextCursor is null."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "nextCursor"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Release"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "publishRelease",
        "summary": "Owner publishes an immutable handover snapshot",
        "security": [
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReleaseInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishResult"
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishResult"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases/{id}": {
      "get": {
        "operationId": "getRelease",
        "summary": "Read release metadata",
        "security": [
          {
            "ProjectKey": []
          },
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Release"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases/{id}/bom": {
      "get": {
        "operationId": "getBom",
        "summary": "Read the frozen BOM JSON document",
        "security": [
          {
            "ProjectKey": []
          },
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bom"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases/{id}/documents": {
      "get": {
        "operationId": "listDocuments",
        "summary": "List immutable documents",
        "security": [
          {
            "ProjectKey": []
          },
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "releaseId": {
                      "type": "string"
                    },
                    "contentHash": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Artifact"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases/{id}/documents/{documentId}": {
      "get": {
        "operationId": "downloadDocument",
        "summary": "Download bytes after authorization and SHA-256 verification",
        "security": [
          {
            "ProjectKey": []
          },
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "documentId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Attachment bytes. Content-Disposition supplies a safe filename; ETag is the quoted SHA-256.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bom"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/keys": {
      "get": {
        "operationId": "listKeys",
        "summary": "Owner lists recent project credentials (never secrets)",
        "security": [
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Key"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createKey",
        "summary": "Owner creates a read-only credential; token returned once",
        "security": [
          {
            "OwnerToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "projectId",
                  "name"
                ],
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "expiresInDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365,
                    "default": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Key"
                    },
                    {
                      "type": "object",
                      "required": [
                        "token"
                      ],
                      "properties": {
                        "token": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/keys/{id}": {
      "delete": {
        "operationId": "revokeKey",
        "summary": "Owner revokes a credential immediately",
        "security": [
          {
            "OwnerToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required with an owner Firebase ID token; inferred from a project-scoped API key."
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked (also succeeds if already revoked)"
          },
          "400": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected; see error.code. 429 includes Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ProjectKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "cb_read_ credential bound to one owner/project; read-only, revocable, expires after at most 365 days."
      },
      "OwnerToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Firebase ID token",
        "description": "Current Firebase ID token from the owning confBuild account. Revocation checked. Required for publishing and key management."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "requestId": {
                "type": "string"
              }
            }
          }
        }
      },
      "Item": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "lineId",
          "position",
          "designation",
          "articleNumber",
          "articleRevision",
          "manufacturer",
          "manufacturerPartNumber",
          "quantity",
          "unit",
          "sourcing",
          "material",
          "mechanicalMaterialId",
          "materialStatus",
          "size",
          "assemblies",
          "sourceIds"
        ],
        "properties": {
          "lineId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Identity of this BOM grouping, not an ERP article identity."
          },
          "position": {
            "type": "integer",
            "minimum": 1
          },
          "designation": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "articleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "articleRevision": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturer": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerPartNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "unit": {
            "enum": [
              "piece",
              "meter",
              "sqm",
              "kg",
              "liter"
            ]
          },
          "sourcing": {
            "enum": [
              "make",
              "buy",
              "unknown"
            ]
          },
          "material": {
            "type": [
              "string",
              "null"
            ]
          },
          "mechanicalMaterialId": {
            "type": [
              "string",
              "null"
            ]
          },
          "materialStatus": {
            "enum": [
              "declared",
              "unknown",
              "assumed",
              "purchased-assembly"
            ]
          },
          "size": {
            "type": [
              "string",
              "null"
            ]
          },
          "assemblies": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string",
              "maxLength": 500
            }
          },
          "sourceIds": {
            "type": "array",
            "maxItems": 1000,
            "items": {
              "type": "string",
              "maxLength": 250
            }
          }
        }
      },
      "Artifact": {
        "type": "object",
        "required": [
          "id",
          "name",
          "mediaType",
          "bytes",
          "sha256",
          "provenance"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "mediaType": {
            "type": "string"
          },
          "bytes": {
            "type": "integer"
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "provenance": {
            "enum": [
              "generated-from-bom",
              "publisher-attested"
            ]
          }
        }
      },
      "ReleaseInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "projectId",
          "projectName",
          "configurationId",
          "configurationName",
          "sourceRevision",
          "externalReference",
          "scope",
          "confirmed",
          "items"
        ],
        "properties": {
          "projectId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,128}$"
          },
          "projectName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "configurationId": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z0-9_-]{1,128}$"
          },
          "configurationName": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceRevision": {
            "type": [
              "string",
              "null"
            ]
          },
          "externalReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "scope": {
            "const": "displayed-bom",
            "description": "Exactly the BOM positions selected by the current displayed list/filters; not automatically the whole assembly."
          },
          "confirmed": {
            "const": true
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2000,
            "items": {
              "$ref": "#/components/schemas/Item"
            }
          },
          "attachments": {
            "type": "array",
            "maxItems": 8,
            "description": "Optional PDF/STEP/DXF files, at most 3 MiB each and 6 MiB total. Publisher confirms their relation to this snapshot. 9 MiB request and 600 KB metadata/BOM limits.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "name",
                "contentBase64"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "maxLength": 120,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._ -]*\\.(pdf|PDF|step|STEP|stp|STP|dxf|DXF)$"
                },
                "contentBase64": {
                  "type": "string",
                  "contentEncoding": "base64",
                  "maxLength": 4194304
                }
              }
            }
          }
        }
      },
      "Release": {
        "type": "object",
        "required": [
          "id",
          "sequence",
          "schemaVersion",
          "status",
          "releasedAt",
          "contentHash",
          "projectId",
          "warnings",
          "documents",
          "itemCount"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "sequence": {
            "type": "integer",
            "minimum": 1
          },
          "schemaVersion": {
            "const": "1.0"
          },
          "status": {
            "const": "released"
          },
          "releasedAt": {
            "type": "string",
            "format": "date-time"
          },
          "contentHash": {
            "type": "string",
            "description": "SHA-256 of the normalized handover payload and document manifest, not a CAD revision hash."
          },
          "projectId": {
            "type": "string"
          },
          "projectName": {
            "type": "string"
          },
          "configurationId": {
            "type": [
              "string",
              "null"
            ]
          },
          "configurationName": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceRevision": {
            "type": [
              "string",
              "null"
            ]
          },
          "externalReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "scope": {
            "const": "displayed-bom"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            }
          },
          "itemCount": {
            "type": "integer"
          }
        }
      },
      "Bom": {
        "type": "object",
        "properties": {
          "schemaVersion": {
            "const": "1.0"
          },
          "projectId": {
            "type": "string"
          },
          "projectName": {
            "type": "string"
          },
          "configurationId": {
            "type": [
              "string",
              "null"
            ]
          },
          "configurationName": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceRevision": {
            "type": [
              "string",
              "null"
            ]
          },
          "externalReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "scope": {
            "const": "displayed-bom"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Item"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PublishResult": {
        "type": "object",
        "required": [
          "release",
          "replayed"
        ],
        "properties": {
          "release": {
            "$ref": "#/components/schemas/Release"
          },
          "replayed": {
            "type": "boolean"
          }
        }
      },
      "Key": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "scope": {
            "const": "releases:read"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "revoked": {
            "type": "boolean"
          }
        }
      }
    }
  }
}
