{"openapi":"3.1.0","info":{"title":"Savvy Rilla FX API","summary":"South Sudan foreign exchange market data and analytics API.","description":"Savvy Rilla FX API provides South Sudanese Pound exchange-rate data, market summaries, structured commentary, API-key protected access, rate limiting, caching and developer tooling.","version":"1.0.0","contact":{"name":"Savvy Rilla","url":"https://fx.savvyrilla.tech","email":"admin@savvygorilla.tech"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://fx.savvyrilla.tech","description":"Production"},{"url":"http://localhost:3000","description":"Local development"}],"tags":[{"name":"Health","description":"Operational health and service diagnostics."},{"name":"Rates","description":"Latest, recent and historical FX rates."},{"name":"Currencies","description":"Supported currency reference data."},{"name":"Market Summary","description":"Market analytics, conditions scoring, and structured commentary."},{"name":"Export","description":"Downloadable FX datasets."}],"security":[{"ApiKeyHeader":[]},{"BearerApiKey":[]}],"paths":{"/api/health":{"get":{"tags":["Health"],"summary":"Health check","description":"Returns service health, environment and database connectivity status.","security":[],"responses":{"200":{"description":"Service health response.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"db":{"type":"boolean"},"service":{"type":"string","example":"Savvy Rilla FX API"},"environment":{"type":"string","example":"production"},"uptimeSeconds":{"type":"integer"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/rates/latest":{"get":{"tags":["Rates"],"summary":"Latest FX rates","description":"Returns the latest SSP-per-unit rates for supported quote currencies.","parameters":[{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."}],"responses":{"200":{"description":"Latest exchange rates.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/LatestRates"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/rates/{quote}/latest":{"get":{"tags":["Rates"],"summary":"Latest FX rate by quote","description":"Returns the latest rate for SSP or supplied base currency against the path quote currency.","parameters":[{"name":"quote","in":"path","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"example":"USD"},"description":"Quote currency code."},{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."}],"responses":{"200":{"description":"Latest exchange rate.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/ExchangeRate"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/rates/recent":{"get":{"tags":["Rates"],"summary":"Recent FX rates","description":"Returns recent rates for a base currency, optionally restricted to the newest available date.","parameters":[{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."},{"name":"quote","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"description":"Quote currency code."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of records to return."},{"name":"latest_only","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"When true, returns every rate from the newest available date. The limit parameter is ignored."}],"responses":{"200":{"description":"Recent exchange rates.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/RateConvention"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecentRate"}},"meta":{"type":"object"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/rates/history":{"get":{"tags":["Rates"],"summary":"Historical FX rates","description":"Returns historical chart-ready FX data.","parameters":[{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."},{"name":"quote","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"description":"Quote currency code."},{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["all","range"]},"description":"Use all to return full available history. Otherwise the route defaults to a 365-day window."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1},"description":"Rolling window in days when mode is not all."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":2,"maximum":50000,"default":20000}}],"responses":{"200":{"description":"Historical exchange rate points.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/RateConvention"},{"type":"object","properties":{"points":{"type":"array","items":{"$ref":"#/components/schemas/ChartPoint"}},"pair":{"type":"string","example":"SSP/USD","description":"Legacy storage-order pair retained for v1 compatibility. Use display_pair for conventional market notation."},"base":{"type":"string","example":"SSP"},"quote":{"type":"string","example":"USD"},"meta":{"type":"object"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/currencies":{"get":{"tags":["Currencies"],"summary":"Supported currencies","description":"Returns supported currency reference data.","parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"active","in":"query","required":false,"schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Supported currencies.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Currency"}},"meta":{"type":"object"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/summary/market":{"get":{"tags":["Market Summary"],"summary":"Market summary","description":"Returns market summary, trend analysis and Market Health Score.","parameters":[{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."},{"name":"quote","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"description":"Quote currency code."}],"responses":{"200":{"description":"Market analysis summary.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/MarketSummary"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/summary/insights":{"get":{"tags":["Market Summary"],"summary":"Market insights","description":"Returns structured commentary and interpreted market insights.","parameters":[{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."},{"name":"quote","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"description":"Quote currency code."}],"responses":{"200":{"description":"Structured market insight response.","headers":{"ETag":{"schema":{"type":"string"},"description":"Entity tag for conditional requests."},"Last-Modified":{"schema":{"type":"string"},"description":"Last modification date for the response resource."},"X-Cache":{"schema":{"type":"string","enum":["HIT","MISS"]},"description":"Whether the response was served from the API cache."}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/RateConvention"},{"type":"object","properties":{"pair":{"type":"string","example":"USD/SSP"},"base":{"type":"string","example":"SSP"},"quote":{"type":"string","example":"USD"},"as_of_date":{"type":"string","format":"date"},"commentary":{"type":"object"},"insights":{"type":"array","items":{"type":"string"}},"marketHealth":{"$ref":"#/components/schemas/MarketHealth"},"meta":{"type":"object"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/v1/export/rates":{"get":{"tags":["Export"],"summary":"Export FX rates","description":"Exports FX rates as JSON or CSV depending on supported query parameters.","parameters":[{"name":"base","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"SSP"},"description":"Base currency code."},{"name":"quote","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"description":"Quote currency code."},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv"],"default":"csv"}}],"responses":{"200":{"description":"Exported rate data.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"$ref":"#/components/schemas/RateConvention"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecentRate"}},"meta":{"type":"object"}}}]}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}}},"components":{"securitySchemes":{"ApiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Savvy Rilla FX API key. Example: srfx_live_xxxxxxxxxxxxxxxxx."},"BearerApiKey":{"type":"http","scheme":"bearer","description":"Alternative API key transport using Authorization: Bearer <api-key>."}},"schemas":{"ApiEnvelope":{"type":"object","required":["success","requestId","timestamp","version","durationMs"],"properties":{"success":{"type":"boolean"},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string","example":"v1"},"durationMs":{"type":"number"}}},"RateConvention":{"type":"object","required":["rate_convention","unit"],"properties":{"rate_convention":{"type":"string","const":"base_units_per_quote_unit","description":"Each mid-rate value is the number of base-currency units equal to one quote-currency unit."},"display_pair":{"type":"string","example":"USD/SSP","description":"Conventional market display pair, when one quote currency is selected."},"unit":{"type":"string","example":"SSP per 1 USD"}}},"ErrorEnvelope":{"allOf":[{"$ref":"#/components/schemas/ApiEnvelope"},{"type":"object","required":["error"],"properties":{"success":{"type":"boolean","const":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"INVALID_CURRENCY"},"message":{"type":"string","example":"base and quote must be valid 3-letter currency codes."},"details":{}}}},"example":{"success":false,"requestId":"req_01JFXAMPLE","timestamp":"2026-06-29T12:00:00.000Z","version":"v1","durationMs":8,"error":{"code":"INVALID_CURRENCY","message":"base and quote must be valid 3-letter currency codes."}}}]},"ExchangeRate":{"allOf":[{"$ref":"#/components/schemas/RateConvention"},{"type":"object","required":["pair","base","quote","as_of_date","mid_rate"],"properties":{"pair":{"type":"string","example":"SSP/USD","description":"Legacy storage-order pair retained for v1 compatibility. Use display_pair for conventional market notation."},"base":{"type":"string","example":"SSP"},"quote":{"type":"string","example":"USD"},"as_of_date":{"type":"string","format":"date","example":"2026-06-29"},"mid_rate":{"type":"number","example":4824.005},"change_pct_vs_previous":{"type":["number","null"],"example":0.12},"is_official":{"type":"boolean"},"is_manual_override":{"type":"boolean"}},"example":{"rate_convention":"base_units_per_quote_unit","display_pair":"USD/SSP","unit":"SSP per 1 USD","pair":"SSP/USD","base":"SSP","quote":"USD","as_of_date":"2026-07-14","mid_rate":4824.005,"change_pct_vs_previous":0.3533,"is_official":true,"is_manual_override":false}}]},"LatestRates":{"allOf":[{"$ref":"#/components/schemas/RateConvention"},{"type":"object","required":["base","as_of_date","rates"],"properties":{"base":{"type":"string","example":"SSP"},"as_of_date":{"type":"string","format":"date","example":"2026-07-14"},"source":{"type":"string","example":"fx_daily_rates"},"rates":{"type":"object","additionalProperties":{"type":"number"},"example":{"KES":37.3235,"USD":4824.005}}}}]},"RecentRate":{"type":"object","required":["id","as_of_date","base_currency","quote_currency","rate_mid"],"properties":{"id":{"type":"integer"},"as_of_date":{"type":"string","format":"date"},"base_currency":{"type":"string","example":"SSP"},"quote_currency":{"type":"string","example":"USD"},"rate_mid":{"type":"number","example":4824.005},"is_official":{"type":["boolean","null"]},"is_manual_override":{"type":["boolean","null"]},"source_id":{"type":["integer","null"]}}},"Currency":{"type":"object","required":["code","name"],"properties":{"code":{"type":"string","example":"USD"},"name":{"type":"string","example":"United States Dollar"},"symbol":{"type":["string","null"],"example":"$"},"decimals":{"type":["integer","null"],"example":2},"created_at":{"type":["string","null"],"format":"date-time"}}},"ChartPoint":{"type":"object","required":["date","mid"],"properties":{"date":{"type":"string","format":"date"},"mid":{"type":"number"}}},"MarketHealth":{"type":"object","required":["score","status","color"],"properties":{"score":{"type":"integer","minimum":0,"maximum":100,"example":84},"status":{"type":"string","example":"Stable"},"color":{"type":"string","example":"green"}}},"MarketSummary":{"allOf":[{"$ref":"#/components/schemas/RateConvention"},{"type":"object","properties":{"pair":{"type":"string","example":"USD/SSP"},"base":{"type":"string","example":"SSP"},"quote":{"type":"string","example":"USD"},"as_of_date":{"type":"string","format":"date"},"mid_rate":{"type":"number","example":4824.005},"change_pct_vs_previous":{"type":["number","null"],"example":0.3533},"trend":{"type":"object"},"volatility":{"type":"object"},"changes":{"type":"object"},"ranges":{"type":"object"},"averages":{"type":"object"},"observations":{"type":"object"},"signals":{"type":"object"},"marketHealth":{"$ref":"#/components/schemas/MarketHealth"},"commentary":{"type":"object"}},"example":{"rate_convention":"base_units_per_quote_unit","display_pair":"USD/SSP","unit":"SSP per 1 USD","base":"SSP","quote":"USD","pair":"USD/SSP","as_of_date":"2026-07-14","mid_rate":4824.005,"change_pct_vs_previous":0.3533,"trend":{"window_days":7,"label":"Uptrend","change_pct":0.8983},"volatility":{"window_days":30,"avg_daily_move_pct":0.1734,"label":"low"},"marketHealth":{"score":78,"status":"Stable","color":"emerald"}}}]}},"responses":{"BadRequest":{"description":"Invalid request parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"Unauthorized":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"RateLimited":{"description":"Rate limit exceeded.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the client should retry."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"ServerError":{"description":"Server-side error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}