{
  "openapi": "3.0.3",
  "info": {
    "title": "Global Balance FX API",
    "version": "1.3.0",
    "description": "Live exchange rates and money-transfer provider comparison. No API key required.",
    "contact": {
      "url": "https://globalbalance.dev"
    },
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    { "url": "https://globalbalance.dev", "description": "Production" },
    { "url": "http://localhost:3000",       "description": "Local dev" }
  ],
  "paths": {
    "/api/rates": {
      "get": {
        "summary": "Live mid-market rate",
        "description": "Returns the current mid-market exchange rate for a currency pair. Rates are cached for 15 minutes.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "example": "USD" },
            "description": "ISO 4217 source currency code"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "example": "INR" },
            "description": "ISO 4217 target currency code"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful rate lookup",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RateResponse" },
                "example": {
                  "ok": true,
                  "from": "USD",
                  "to": "INR",
                  "rate": 83.42,
                  "source": "open.er-api.com",
                  "timestamp": "2026-04-07T00:00:00Z",
                  "checkedAt": "Mon, 07 Apr 2026 14:23:11 GMT"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "500": { "$ref": "#/components/responses/UpstreamError" }
        }
      }
    },
    "/api/compare": {
      "get": {
        "summary": "Provider comparison",
        "description": "Compares send costs across all supported providers for a given currency pair and amount.",
        "parameters": [
          { "name": "from",   "in": "query", "required": true,  "schema": { "type": "string", "example": "USD" } },
          { "name": "to",     "in": "query", "required": true,  "schema": { "type": "string", "example": "MXN" } },
          { "name": "amount", "in": "query", "required": false, "schema": { "type": "number", "example": 1000, "default": 1000 } },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["rate", "fee", "spread"], "default": "rate" }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful comparison",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CompareResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "500": { "$ref": "#/components/responses/UpstreamError" }
        }
      }
    },
    "/api/matrix": {
      "get": {
        "summary": "Cross-rate matrix",
        "description": "Returns a nested matrix of exchange rates between a set of currencies.",
        "parameters": [
          {
            "name": "currencies",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "example": "USD,EUR,GBP,INR,MXN" },
            "description": "Comma-separated ISO 4217 codes. Max 10. Defaults to USD,EUR,GBP,JPY,CAD,MXN,INR,BRL"
          }
        ],
        "responses": {
          "200": {
            "description": "Matrix of rates",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MatrixResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "500": { "$ref": "#/components/responses/UpstreamError" }
        }
      }
    },
    "/api/providers": {
      "get": {
        "summary": "Provider metadata",
        "description": "Returns all supported providers with fee structures, network info, and data source metadata.",
        "responses": {
          "200": {
            "description": "Provider list",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ProvidersResponse" }
              }
            }
          }
        }
      }
    },
    "/api/history": {
      "get": {
        "summary": "Historical rates",
        "description": "Returns stored mid-market rate snapshots for a currency pair. Sampled every 15 min via cron; up to 30 days of data.",
        "parameters": [
          { "name": "from", "in": "query", "required": true,  "schema": { "type": "string", "example": "USD" } },
          { "name": "to",   "in": "query", "required": true,  "schema": { "type": "string", "example": "EUR" } },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 30, "default": 7 }
          }
        ],
        "responses": {
          "200": {
            "description": "Historical rate series",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HistoryResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "RateResponse": {
        "type": "object",
        "properties": {
          "ok":        { "type": "boolean" },
          "from":      { "type": "string" },
          "to":        { "type": "string" },
          "rate":      { "type": "number" },
          "source":    { "type": "string" },
          "timestamp": { "type": "string", "format": "date-time" },
          "checkedAt": { "type": "string" }
        }
      },
      "Provider": {
        "type": "object",
        "properties": {
          "rank":          { "type": "integer" },
          "id":            { "type": "string" },
          "name":          { "type": "string" },
          "type":          { "type": "string" },
          "receiveAmount": { "type": "number" },
          "effectiveRate": { "type": "number" },
          "spreadPct":     { "type": "number" },
          "feeFixed":      { "type": "number" },
          "feePct":        { "type": "number" },
          "totalFee":      { "type": "number" },
          "network":       { "type": "string" },
          "source":        { "type": "string" },
          "dataNote":      { "type": "string" }
        }
      },
      "CompareResponse": {
        "type": "object",
        "properties": {
          "ok":           { "type": "boolean" },
          "from":         { "type": "string" },
          "to":           { "type": "string" },
          "amount":       { "type": "number" },
          "midRate":      { "type": "number" },
          "providers":    { "type": "array", "items": { "$ref": "#/components/schemas/Provider" } },
          "bestProvider": { "type": "string" },
          "maxSavings":   { "type": "number" },
          "checkedAt":    { "type": "string" }
        }
      },
      "MatrixResponse": {
        "type": "object",
        "properties": {
          "ok":        { "type": "boolean" },
          "base":      { "type": "string" },
          "rates":     { "type": "object", "additionalProperties": { "type": "object", "additionalProperties": { "type": "number" } } },
          "source":    { "type": "string" },
          "timestamp": { "type": "string" }
        }
      },
      "ProviderMeta": {
        "type": "object",
        "properties": {
          "id":            { "type": "string" },
          "name":          { "type": "string" },
          "type":          { "type": "string" },
          "network":       { "type": "string" },
          "spreadPct":     { "type": "number" },
          "feeFixed":      { "type": "number" },
          "feePct":        { "type": "number" },
          "feeUrl":        { "type": "string", "format": "uri" },
          "dataNote":      { "type": "string" },
          "supportedFrom": { "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] },
          "supportedTo":   { "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }
        }
      },
      "ProvidersResponse": {
        "type": "object",
        "properties": {
          "ok":        { "type": "boolean" },
          "providers": { "type": "array", "items": { "$ref": "#/components/schemas/ProviderMeta" } }
        }
      },
      "HistoryPoint": {
        "type": "object",
        "properties": {
          "ts":   { "type": "string", "format": "date-time" },
          "rate": { "type": "number" }
        }
      },
      "HistoryResponse": {
        "type": "object",
        "properties": {
          "ok":   { "type": "boolean" },
          "from": { "type": "string" },
          "to":   { "type": "string" },
          "days": { "type": "integer" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryPoint" } }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Missing or invalid parameter",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Missing required param: from" }
          }
        }
      },
      "UpstreamError": {
        "description": "All upstream rate providers failed",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Failed to fetch rates" }
          }
        }
      }
    }
  }
}
