{
  "openapi": "3.1.0",
  "info": {
    "title": "LMGNT Exchange",
    "version": "1.0.5",
    "description": "Read-only controlled distribution. Target URLs are configuration targets; this release is NOT verified on staging or production."
  },
  "servers": [
    {
      "url": "https://ex.lmgnt.com",
      "description": "Production target, deployment blocked"
    },
    {
      "url": "https://ex-staging.lmgnt.com",
      "description": "Isolated staging target, not deployed"
    }
  ],
  "security": [
    {
      "ExBearer": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "security": [],
        "summary": "Liveness/version only; does not prove data readiness or deployment acceptance",
        "responses": {
          "200": {
            "description": "Worker identity"
          }
        }
      }
    },
    "/api/v1/prices/latest": {
      "get": {
        "operationId": "latestPrices",
        "summary": "Export the authorized seven consecutive calendar dates; one new successful export consumes one shared client slot",
        "description": "No source collection or canonical writes. JSON and CSV share two slots/day/client. Missing/withheld values remain null. Same client+key+format+approval policy replays the same snapshot for24h without another charge, including after midnight. Prepared response+atomic quota commit is not proof of client network delivery. A retry after503/timeout MUST use the same key.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "const": 7,
              "default": 7
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{16,128}$"
            },
            "description": "Strongly recommended UUID generated before request. If omitted, server creates a key and returns it; loss of response can prevent safe retry."
          }
        ],
        "responses": {
          "200": {
            "description": "Seven calendar days: exactly six fields per row; operational metadata is in response headers.",
            "headers": {
              "Idempotency-Key": {
                "schema": {
                  "type": "string"
                },
                "description": "Reuse this key for a retry; create your own key before sending the initial request."
              },
              "X-Idempotent-Replay": {
                "schema": {
                  "type": "string"
                },
                "description": "true if this is the previously charged export."
              },
              "X-Quota-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "0..2 for the current Bangkok day at commit."
              },
              "X-Quota-Reset-At": {
                "schema": {
                  "type": "string"
                },
                "description": "UTC RFC3339 time of next Bangkok midnight."
              },
              "X-Dataset-Revision": {
                "schema": {
                  "type": "string"
                },
                "description": "Canonical revision hash."
              },
              "X-Dataset-Start": {
                "schema": {
                  "type": "string"
                },
                "description": "Start date inclusive."
              },
              "X-Dataset-End": {
                "schema": {
                  "type": "string"
                },
                "description": "Latest approved end date inclusive."
              },
              "X-LMGNT-Metadata": {
                "schema": {
                  "type": "string"
                },
                "description": "JSON and CSV: Base64 of UTF-8 operational metadata. Not part of the six-field data body."
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "Download filename."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceExport"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                },
                "example": "price_date,country_code,country_name,product_code,thb_per_liter,fx_date"
              }
            }
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/quota": {
      "get": {
        "summary": "Free authenticated client identity, current daily quota and window availability; contains no protected prices",
        "responses": {
          "200": {
            "description": "Quota and availability",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaStatus"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/requests/recent": {
      "get": {
        "summary": "Free recent request log for the authenticated client only; max20rows",
        "responses": {
          "200": {
            "description": "Recent operational results"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/clients": {
      "get": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "clients GET",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "clients POST",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "201": {
            "description": "Created; issued credential plaintext appears once only"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "X-EX-Admin",
            "in": "header",
            "required": true,
            "schema": {
              "const": "1"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must exactly equal the request canonical HTTPS origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "organization_key"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 160
                  },
                  "organization_key": {
                    "type": "string",
                    "pattern": "^[A-Z0-9][A-Z0-9_-]{2,63}$"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/clients/{id}": {
      "get": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "clients/{id} GET",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/admin/api/clients/{id}/credentials": {
      "post": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "clients/{id}/credentials POST",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "201": {
            "description": "Created; issued credential plaintext appears once only"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-EX-Admin",
            "in": "header",
            "required": true,
            "schema": {
              "const": "1"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must exactly equal the request canonical HTTPS origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expires_at"
                ],
                "properties": {
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Future expiry, max366days from request time"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/clients/{id}/status": {
      "post": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "clients/{id}/status POST",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "201": {
            "description": "Created; issued credential plaintext appears once only"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-EX-Admin",
            "in": "header",
            "required": true,
            "schema": {
              "const": "1"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must exactly equal the request canonical HTTPS origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "enum": [
                      "ACTIVE",
                      "SUSPENDED"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/credentials/{id}/revoke": {
      "post": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "credentials/{id}/revoke POST",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "201": {
            "description": "Created; issued credential plaintext appears once only"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-EX-Admin",
            "in": "header",
            "required": true,
            "schema": {
              "const": "1"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must exactly equal the request canonical HTTPS origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/admin/api/credentials/{id}/rotate": {
      "post": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "credentials/{id}/rotate POST",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "201": {
            "description": "Created; issued credential plaintext appears once only"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-EX-Admin",
            "in": "header",
            "required": true,
            "schema": {
              "const": "1"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must exactly equal the request canonical HTTPS origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expires_at"
                ],
                "properties": {
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Future expiry, max366days from request time"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/credentials/{id}/expiry": {
      "post": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "credentials/{id}/expiry POST",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "201": {
            "description": "Created; issued credential plaintext appears once only"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-EX-Admin",
            "in": "header",
            "required": true,
            "schema": {
              "const": "1"
            }
          },
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must exactly equal the request canonical HTTPS origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expires_at"
                ],
                "properties": {
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Future expiry, max366days from request time"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/usage": {
      "get": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "usage GET",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/audit": {
      "get": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "audit GET",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/api/availability": {
      "get": {
        "tags": [
          "Admin"
        ],
        "security": [
          {
            "CloudflareAccess": []
          }
        ],
        "summary": "availability GET",
        "description": "Access JWT must verify signature/issuer/audience/expiry AND server email allowlist. Consumer bearer tokens cannot authorize admin. Server canonical hostname guard always applies.",
        "responses": {
          "200": {
            "description": "Administrative result (usage max1000rows, audit max100rows)"
          },
          "400": {
            "description": "Unsupported request or invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or revoked credential / invalid Access JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Suspended client / denied admin or origin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No approved data / route not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "406": {
            "description": "Unsupported format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict or expired replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota or edge abuse limit exceeded. Retry-After header is seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Release closed or temporary failure. If commit result is unknown, retry the SAME Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ExBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "LMGNT EX credential"
      },
      "CloudflareAccess": {
        "type": "apiKey",
        "in": "header",
        "name": "Cf-Access-Jwt-Assertion",
        "description": "Edge-issued RS256 JWT verified in Worker; server email allowlist additionally required."
      }
    },
    "schemas": {
      "Quota": {
        "type": "object",
        "required": [
          "daily_limit",
          "quota_remaining",
          "quota_reset_at",
          "quota_timezone",
          "quota_day"
        ],
        "properties": {
          "daily_limit": {
            "const": 2
          },
          "quota_remaining": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2
          },
          "quota_reset_at": {
            "type": "string",
            "format": "date-time"
          },
          "quota_timezone": {
            "const": "Asia/Bangkok"
          },
          "quota_day": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "PriceRow": {
        "type": "object",
        "required": [
          "price_date",
          "country_code",
          "country_name",
          "product_code",
          "thb_per_liter",
          "fx_date"
        ],
        "properties": {
          "price_date": {
            "type": "string",
            "format": "date"
          },
          "country_code": {
            "type": "string",
            "enum": [
              "TH",
              "VN",
              "KH",
              "LA",
              "MM",
              "PH",
              "MY",
              "SG",
              "ID",
              "BN"
            ]
          },
          "country_name": {
            "type": "string"
          },
          "product_code": {
            "type": "string"
          },
          "thb_per_liter": {
            "type": [
              "number",
              "null"
            ],
            "exclusiveMinimum": 0
          },
          "fx_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          }
        },
        "additionalProperties": false
      },
      "ExportMetadata": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Quota"
          },
          {
            "type": "object",
            "required": [
              "dataset_start",
              "dataset_end",
              "latest_available_date",
              "generated_at",
              "data_revision",
              "snapshot_id",
              "coverage",
              "calendar_days",
              "country_count",
              "reader_policy_revision",
              "read_mode"
            ],
            "properties": {
              "dataset_start": {
                "type": "string",
                "format": "date"
              },
              "dataset_end": {
                "type": "string",
                "format": "date"
              },
              "latest_available_date": {
                "type": "string",
                "format": "date"
              },
              "generated_at": {
                "type": "string",
                "format": "date-time"
              },
              "data_revision": {
                "type": "string",
                "pattern": "^[a-f0-9]{64}$"
              },
              "snapshot_id": {
                "type": "string"
              },
              "calendar_days": {
                "const": 7
              },
              "country_count": {
                "const": 10
              },
              "charged_quota_day": {
                "type": "string",
                "format": "date"
              },
              "coverage": {
                "type": "array",
                "minItems": 7,
                "maxItems": 7,
                "items": {
                  "type": "object",
                  "required": [
                    "price_date",
                    "expected",
                    "available",
                    "missing",
                    "not_applicable",
                    "complete"
                  ],
                  "properties": {
                    "price_date": {
                      "type": "string",
                      "format": "date"
                    },
                    "expected": {
                      "type": "integer"
                    },
                    "available": {
                      "type": "integer"
                    },
                    "missing": {
                      "type": "integer"
                    },
                    "not_applicable": {
                      "type": "integer"
                    },
                    "withheld": {
                      "type": "integer"
                    },
                    "complete": {
                      "type": "boolean"
                    }
                  }
                }
              },
              "reader_policy_revision": {
                "type": "string",
                "enum": [
                  "EX-CANONICAL-READONLY-2026-10-29-V2"
                ]
              },
              "read_mode": {
                "type": "string",
                "enum": [
                  "CANONICAL_READ_ONLY_MATCHED_FX"
                ]
              }
            }
          }
        ]
      },
      "PriceExport": {
        "type": "array",
        "minItems": 154,
        "maxItems": 154,
        "items": {
          "$ref": "#/components/schemas/PriceRow"
        },
        "description": "Seven calendar days as a bare array. Every row contains exactly the six requested fields. Unavailable or withheld price/FX-date values are null."
      },
      "Error": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              },
              "retry_after": {
                "type": "integer"
              }
            }
          }
        }
      },
      "QuotaStatus": {
        "type": "object",
        "required": [
          "success",
          "client",
          "quota",
          "availability",
          "release_enabled"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "client": {
            "type": "object"
          },
          "quota": {
            "$ref": "#/components/schemas/Quota"
          },
          "availability": {
            "type": "object"
          },
          "release_enabled": {
            "type": "boolean"
          }
        }
      }
    }
  }
}
