{
  "openapi": "3.1.0",
  "info": {
    "title": "Elucora API",
    "version": "1.2.0",
    "description": "Capture a public web source and create a portable, cryptographically signed evidence receipt. A valid receipt proves artifact integrity and issuer; it does not prove that the source is factually true."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current Elucora host"
    }
  ],
  "tags": [
    {
      "name": "Receipts",
      "description": "Capture and retrieve evidence receipts."
    },
    {
      "name": "Verification",
      "description": "Verify portable receipts and discover public keys."
    },
    {
      "name": "Transparency",
      "description": "Inspect the public hash-linked receipt log and independent timestamp status."
    },
    {
      "name": "Operations",
      "description": "Service readiness."
    }
  ],
  "paths": {
    "/v1/receipts": {
      "post": {
        "operationId": "createReceipt",
        "summary": "Create a signed receipt",
        "description": "Fetches a public HTTP(S) source, optionally selects exact evidence, preserves the original bytes, and returns a JCS-canonicalized ES256-signed receipt. Use a unique Idempotency-Key for each logical capture.",
        "tags": [
          "Receipts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A unique 1–128 character key. Reusing it with the same body replays the original response; using it with a different body returns 422.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReceiptRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Receipt created",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptResponse"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay of the original receipt",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "413": {
            "$ref": "#/components/responses/Problem"
          },
          "415": {
            "$ref": "#/components/responses/Problem"
          },
          "422": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "504": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/receipts/{id}": {
      "get": {
        "operationId": "getReceipt",
        "summary": "Retrieve and verify a receipt",
        "tags": [
          "Receipts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ReceiptId"
          }
        ],
        "responses": {
          "200": {
            "description": "Receipt with current verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/receipts/{id}/artifact": {
      "get": {
        "operationId": "downloadArtifact",
        "summary": "Download the preserved source bytes",
        "description": "Returns the immutable bytes captured for a receipt. Only the owning account may download them.",
        "tags": [
          "Receipts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ReceiptId"
          }
        ],
        "responses": {
          "200": {
            "description": "Original captured bytes",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "contentEncoding": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "410": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/transparency": {
      "get": {
        "operationId": "getTransparencyLog",
        "summary": "Get the public transparency-log head and recent entries",
        "tags": [
          "Transparency"
        ],
        "responses": {
          "200": {
            "description": "Hash-linked log head and recent entries"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/transparency/checkpoint": {
      "get": {
        "operationId": "getTransparencyCheckpoint",
        "summary": "Get a signed checkpoint of the transparency-log head",
        "description": "Returns the current head sequence, head entry hash, and entry count, signed with the current receipt key over the RFC 8785 canonical form of those fields. Publish checkpoints outside Elucora so a later rewrite of the log becomes detectable.",
        "tags": [
          "Transparency"
        ],
        "responses": {
          "200": {
            "description": "Signed transparency-log checkpoint"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/transparency/{id}": {
      "get": {
        "operationId": "getTransparencyProof",
        "summary": "Get a receipt transparency proof and RFC 3161 token when available",
        "tags": [
          "Transparency"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ReceiptId"
          }
        ],
        "responses": {
          "200": {
            "description": "Transparency chain entry and timestamp proof"
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/transparency/{id}/ots": {
      "get": {
        "operationId": "getTransparencyOtsProof",
        "summary": "Download the OpenTimestamps proof for a transparency entry",
        "description": "Returns a detached OpenTimestamps proof file attesting the entry hash. The proof carries a calendar attestation until the commitment reaches a Bitcoin block, after which it carries the block attestation. Verify with `ots verify`.",
        "tags": [
          "Transparency"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ReceiptId"
          }
        ],
        "responses": {
          "200": {
            "description": "Detached OpenTimestamps proof",
            "content": {
              "application/vnd.opentimestamps.ots": {
                "schema": {
                  "type": "string",
                  "contentEncoding": "binary"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/verify": {
      "post": {
        "operationId": "verifyReceipt",
        "summary": "Verify a stored or portable receipt",
        "tags": [
          "Verification"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "id"
                    ],
                    "properties": {
                      "id": {
                        "$ref": "#/components/schemas/ReceiptId"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "payload",
                      "signature"
                    ],
                    "properties": {
                      "payload": {
                        "$ref": "#/components/schemas/ReceiptPayload"
                      },
                      "signature": {
                        "type": "string",
                        "description": "Base64url ES256 signature."
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/public-key": {
      "get": {
        "operationId": "getCurrentPublicKey",
        "summary": "Get the current verification key",
        "tags": [
          "Verification"
        ],
        "responses": {
          "200": {
            "description": "Current ES256 public JWK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/.well-known/jwks.json": {
      "get": {
        "operationId": "getJwks",
        "summary": "Get all active and retained verification keys",
        "tags": [
          "Verification"
        ],
        "responses": {
          "200": {
            "description": "JSON Web Key Set",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "keys"
                  ],
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "/v1/health": {
        "get": {
          "operationId": "getHealth",
          "summary": "Check service readiness",
          "tags": [
            "Operations"
          ],
          "responses": {
            "200": {
              "description": "All core services are ready"
            },
            "503": {
              "description": "One or more core services are unavailable"
            }
          }
        }
      }
    },
    "components": {
      "securitySchemes": {
        "bearerAuth": {
          "type": "http",
          "scheme": "bearer",
          "bearerFormat": "Elucora API key",
          "description": "A server-side API key beginning with elu_live_."
        }
      },
      "parameters": {
        "ReceiptId": {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "$ref": "#/components/schemas/ReceiptId"
          }
        }
      },
      "headers": {
        "RequestId": {
          "description": "Trace identifier for support and logs.",
          "schema": {
            "type": "string"
          }
        },
        "RateLimitLimit": {
          "description": "Per-minute capture limit.",
          "schema": {
            "type": "integer"
          }
        },
        "RateLimitRemaining": {
          "description": "Remaining captures in the current minute.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "responses": {
        "Problem": {
          "description": "Machine-readable API error",
          "headers": {
            "X-Request-Id": {
              "$ref": "#/components/headers/RequestId"
            }
          },
          "content": {
            "application/problem+json": {
              "schema": {
                "$ref": "#/components/schemas/Problem"
              }
            }
          }
        }
      },
      "schemas": {
        "ReceiptId": {
          "type": "string",
          "pattern": "^rcpt_[a-f0-9]{12,32}$",
          "examples": [
            "rcpt_4af921be38e1412396cc3887fa558119"
          ]
        },
        "TextQuoteSelector": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "type",
            "exact"
          ],
          "properties": {
            "type": {
              "const": "text-quote"
            },
            "exact": {
              "type": "string",
              "minLength": 1,
              "maxLength": 65536
            },
            "prefix": {
              "type": "string",
              "maxLength": 512,
              "description": "Optional text immediately before exact, used to disambiguate repeated quotes."
            },
            "suffix": {
              "type": "string",
              "maxLength": 512,
              "description": "Optional text immediately after exact, used to disambiguate repeated quotes."
            }
          }
        },
        "JsonPointerSelector": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "type",
            "value"
          ],
          "properties": {
            "type": {
              "const": "json-pointer"
            },
            "value": {
              "type": "string",
              "maxLength": 2048,
              "description": "RFC 6901 JSON Pointer."
            }
          }
        },
        "Metadata": {
          "type": "object",
          "maxProperties": 10,
          "propertyNames": {
            "pattern": "^[A-Za-z][A-Za-z0-9_.-]{0,63}$"
          },
          "additionalProperties": {
            "type": [
              "string",
              "number",
              "boolean",
              "null"
            ]
          },
          "description": "Optional signed customer context, capped at 2 KB canonical JSON. Never put secrets here."
        },
        "CreateReceiptRequest": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "url"
          ],
          "properties": {
            "url": {
              "type": "string",
              "format": "uri",
              "maxLength": 2048,
              "description": "A public HTTP(S) URL on a standard port."
            },
            "selector": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/TextQuoteSelector"
                },
                {
                  "$ref": "#/components/schemas/JsonPointerSelector"
                }
              ]
            },
            "metadata": {
              "$ref": "#/components/schemas/Metadata"
            }
          }
        },
        "ReceiptPayload": {
          "type": "object",
          "required": [
            "version",
            "id",
            "url",
            "final_url",
            "retrieved_at",
            "status",
            "content_type",
            "size_bytes",
            "artifact_hash",
            "selector",
            "selected_content",
            "selected_content_hash",
            "capture_method"
          ],
          "properties": {
            "version": {
              "type": "string",
              "enum": [
                "1.0",
                "1.1"
              ]
            },
            "id": {
              "$ref": "#/components/schemas/ReceiptId"
            },
            "url": {
              "type": "string",
              "format": "uri"
            },
            "final_url": {
              "type": "string",
              "format": "uri"
            },
            "retrieved_at": {
              "type": "string",
              "format": "date-time"
            },
            "status": {
              "type": "integer",
              "minimum": 200,
              "maximum": 299
            },
            "content_type": {
              "type": "string"
            },
            "size_bytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 5000000
            },
            "artifact_hash": {
              "type": "string",
              "pattern": "^sha256:[a-f0-9]{64}$"
            },
            "selector": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/TextQuoteSelector"
                },
                {
                  "$ref": "#/components/schemas/JsonPointerSelector"
                },
                {
                  "type": "null"
                }
              ]
            },
            "selected_content": {
              "type": [
                "string",
                "null"
              ]
            },
            "selected_content_hash": {
              "type": [
                "string",
                "null"
              ]
            },
            "capture_method": {
              "const": "http"
            },
            "metadata": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Metadata"
                },
                {
                  "type": "null"
                }
              ]
            },
            "hash_algorithm": {
              "const": "SHA-256"
            },
            "signature_algorithm": {
              "const": "ES256"
            },
            "canonicalization": {
              "const": "JCS-RFC8785"
            },
            "key_id": {
              "type": "string"
            }
          }
        },
        "ReceiptResponse": {
          "allOf": [
            {
              "$ref": "#/components/schemas/ReceiptPayload"
            },
            {
              "type": "object",
              "required": [
                "signature",
                "verify_url",
                "jwks_url",
                "transparency_url"
              ],
              "properties": {
                "signature": {
                  "type": "string",
                  "description": "Base64url ES256 signature over the canonical receipt payload."
                },
                "verify_url": {
                  "type": "string",
                  "format": "uri"
                },
                "receipt_url": {
                  "type": "string",
                  "format": "uri"
                },
                "artifact_url": {
                  "type": "string",
                  "format": "uri"
                },
                "public_key_url": {
                  "type": "string",
                  "format": "uri"
                },
                "jwks_url": {
                  "type": "string",
                  "format": "uri"
                },
                "transparency_url": {
                  "type": "string",
                  "format": "uri"
                },
                "artifact_expires_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "transparency": {
                  "type": "object",
                  "description": "Public hash-chain coordinates and independent timestamp status. status is 'pending' with null hashes until the entry is appended after the request, then 'recorded'."
                },
                "signature_valid": {
                  "type": "boolean"
                },
                "request_id": {
                  "type": "string"
                },
                "idempotent_replay": {
                  "type": "boolean"
                }
              }
            }
          ]
        },
        "VerificationResult": {
          "type": "object",
          "required": [
            "valid",
            "id",
            "checked_at",
            "request_id"
          ],
          "properties": {
            "valid": {
              "type": "boolean"
            },
            "id": {
              "$ref": "#/components/schemas/ReceiptId"
            },
            "version": {
              "type": "string"
            },
            "signature_algorithm": {
              "type": "string"
            },
            "canonicalization": {
              "type": "string"
            },
            "key_id": {
              "type": [
                "string",
                "null"
              ]
            },
            "checked_at": {
              "type": "string",
              "format": "date-time"
            },
            "request_id": {
              "type": "string"
            }
          }
        },
        "Problem": {
          "type": "object",
          "required": [
            "type",
            "title",
            "status",
            "detail",
            "code",
            "instance",
            "request_id"
          ],
          "properties": {
            "type": {
              "type": "string",
              "format": "uri-reference"
            },
            "title": {
              "type": "string"
            },
            "status": {
              "type": "integer"
            },
            "detail": {
              "type": "string"
            },
            "code": {
              "type": "string"
            },
            "instance": {
              "type": "string",
              "format": "uri"
            },
            "request_id": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
