{
  "$schema": "https://modan.io/changelog.schema.json",
  "title": "Modan API Changelog",
  "description": "Notable changes to the Modan African FX API, terminal and MCP server. Newest first. Machine-readable — poll this file or read it at https://modan.io/changelog.",
  "entries": [
    {
      "date": "2026-09-03",
      "title": "Decimal slips are corrected in one pass",
      "tags": [
        "Improved"
      ],
      "body": [
        "A small number of archived observations were recorded a clean factor of ten from what every other provider quoted the same day — 1,371,027 where the market was near 1,364, which is the same digits with a comma read as a thousands separator rather than a decimal point. Admin can now restore the point across all of them in one action. It is deliberately narrow: only a clean power of ten qualifies, the digit sequence is never altered, an observation whose corrected value would need more precision than we store is left for a person, and each correction is still checked against what other providers quoted at that moment — anything that does not fit is skipped and reported rather than written. Corrections are appended at the original timestamp as before, and any of them can be reversed in one click, putting the original observation back."
      ]
    },
    {
      "date": "2026-09-03",
      "title": "Corrections are appended, never written over",
      "tags": [
        "Improved"
      ],
      "body": [
        "When a recorded observation turns out to be wrong and we can establish what the provider actually published, the correction is now appended at the same timestamp as the observation it replaces. The original moves to the same withdrawal record used elsewhere — keeping the value it held, who corrected it and why, plus a pointer to what stands in its place — so the series reads correctly from then on while the mistake stays on the record. The admin rate table's edit form, which used to overwrite the row, is gone: there is no longer any path that changes a published number in place. A correction that is itself implausible against what other providers quoted at that moment is refused outright, and the original observation stays exactly where it was."
      ]
    },
    {
      "date": "2026-09-03",
      "title": "Suspected bad rates can be withdrawn, and fewer get in",
      "tags": [
        "Improved"
      ],
      "body": [
        "A handful of observations in the archive were recorded at ten, a hundred or a thousand times their true value — parsing slips rather than prices, and they distorted any chart or time series that included them. Admin now has a review screen that ranks every observation against what other providers quoted on the same corridor that day, says why each one looks wrong, and lets the team withdraw the confirmed errors. Withdrawn observations are not deleted: the original value, its timestamp, who withdrew it and why are all kept, and it can be put back. Nothing is ever edited in place. Separately, the 30% plausibility check that already guarded the automated feed now runs in the database, so it covers the manual rate form and the bulk upload too; it stands aside when a corridor has too few quotes to judge against, so a genuine market-wide move is never blocked."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "Eight guides on African FX pricing, with the data as the worked example",
      "tags": [
        "New"
      ],
      "body": [
        "modan.io/guides is a new section of plain-language explainers, each answering one question people actually ask: what the official, interbank, retail and parallel naira rates each mean and which you can transact at; how to read spread_bps and vs_mid_bps in basis points; how remittance providers set their rates; how a treasurer can benchmark a bank's quote against the provider book and the independent mid; getting NGN rates by API in Python and JavaScript; giving an AI agent live rates through MCP; how USDT→NGN stablecoin corridors are priced and why they carry no mid; and how Modan collects and validates its data. Every guide links to the live corridor, currency and provider pages it discusses, carries a FAQ, and is real HTML."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "An MCP setup page, a registry manifest, and live facts in llms.txt",
      "tags": [
        "New",
        "Improved"
      ],
      "body": [
        "modan.io/docs/mcp is a new page with one job: connect Modan to Claude Code, Cursor, VS Code or any other MCP client in a minute. It lists the seven tools straight from the server's own definitions (so it can never describe a tool that does not exist), shows the exact config for each client, gives example prompts, explains how to read spread_bps, vs_mid_bps, rate_type and data_freshness in the answers, and says plainly which clients cannot send an API-key header today (ChatGPT connectors — use a Custom GPT Action with openapi.json instead). A server.json manifest for the official MCP Registry ships in the repository. llms.txt now opens with a facts block (providers, corridors, currencies, latest observation) that is regenerated on every deploy, plus an index of every kind of public page, and both llms files name the plans as Free / Individual / Team."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "Corridor, currency and provider hub pages",
      "tags": [
        "New"
      ],
      "body": [
        "Three new kinds of public page, all prerendered and all drawn from the same live rate book. /corridors lists every currency pair Modan tracks, grouped by the currency the money lands in, with the best executable quote, who quotes it, how many providers do, and when it was observed. /currency/ngn (and one page per currency, 19 in all) shows every corridor into and out of that currency plus the providers quoting it. /providers lists every provider with its institution type, the kind of price it publishes, how many corridors it quotes and on how many it has the best executable rate; /providers/lemfi (one per provider, 21 in all) ranks that provider on every corridor it quotes, with its distance from the best in basis points. Each carries a dated summary, a FAQ and an API snippet, and the header now has a Corridors link. The sitemap grew from 360 to 402 pages."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "Every public page is now real HTML, and every corridor has a page worth reading",
      "tags": [
        "New",
        "Improved"
      ],
      "body": [
        "Until now the site was a JavaScript application that handed crawlers and AI assistants an empty page. The build now renders every public page to static HTML — the home page, docs, pricing, help, changelog, all 80 corridor pages and every provider-on-a-corridor page (359 pages) — with the live rate book baked in and clearly dated, so search engines, ChatGPT, Claude and anyone fetching a URL see the same table you do. Each corridor page now reads as a report: a plain-language summary of the best executable quote, the spread across providers and the independent mid, a short FAQ, an API snippet, and links to related corridors; provider pages say where that provider ranks and link to its other corridors. Structured data (Organization, Dataset, FAQ, breadcrumbs, product offers, WebAPI) and a sitemap of every page ship with it, and corridor URLs are now lowercase (old uppercase links still work). Nothing changes in the browser except that pages paint before the JavaScript arrives."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "Home page coverage, live rates and counts no longer vanish",
      "tags": [
        "Fixed"
      ],
      "body": [
        "For anonymous visitors the home page could load without its corridor coverage globe, live-rates strip and provider/corridor counts. All three came from one read of the whole current rate book, which is a scan of the entire append-only rates table and exceeded the database's 3-second limit whenever it ran cold — the page then quietly rendered as if there were no data. The page now reads the same lightweight snapshot the API serves (about a hundredth of the work, well under a second), and if that read ever fails it says so in place of the coverage section instead of hiding it."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "A cleaner landing page and one navigation everywhere",
      "tags": [
        "Improved"
      ],
      "body": [
        "The landing page now follows one rhythm — a single column width, one section padding scale, one heading style — so the coverage globe, the pricing preview and the footer line up with everything else instead of each choosing their own spacing. Every public page (home, pricing, API docs, changelog, help, corridor pages) shares the same header: **API Docs · Pricing · Help · Changelog**, with the current page marked and a proper menu on phones, where the links used to disappear entirely. The footer is aligned to the page column and now links the API status endpoint, the MCP docs and the most-used corridor pages."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "New daily API quotas: Free 50, Individual 250, Team 1,000",
      "tags": [
        "Improved",
        "API"
      ],
      "body": [
        "Daily request allowances are now **Free 50 / Individual 250 / Team 1,000** per UTC day (previously 25 / 500 / 5,000). The free tier doubles so an evaluation can run a real corridor sweep; Individual and Team are sized to the request patterns we actually see on those plans. Everything else is unchanged: the allowance is shared across all of an account's keys, MCP tool calls count against it, every response carries `X-RateLimit-Limit`/`X-RateLimit-Remaining`/`X-RateLimit-Reset`, requests beyond the limit are refused rather than billed, and data freshness stays hourly on Free/Individual and real-time on Team. The new numbers apply to existing keys immediately — no new key needed."
      ]
    },
    {
      "date": "2026-09-02",
      "title": "Spread and “Best” now respect rate type",
      "tags": [
        "Fixed"
      ],
      "body": [
        "The terminal grid and the public corridor pages measured spread against the highest number on the board, so an official or parallel print could sit at 0.0 as the \"best\" rate — a price nobody can actually get. Spread is now measured within a rate type, exactly as the API's `spread_bps` is, and **Best** only ever names the best executable quote. Official and parallel prints are labelled `· ref` and are never ranked; a provider page for one says so instead of showing a rank."
      ]
    },
    {
      "date": "2026-08-21",
      "title": "Provider taxonomy + rate_type: spread now compares like with like",
      "tags": [
        "New",
        "API",
        "Breaking"
      ],
      "body": [
        "Every provider now carries two independent labels. provider_type says what kind of institution it is — central_bank, commercial_bank, non_bank_lp, imto, fintech_psp, crypto_venue, bureau_de_change or aggregator. rate_type says what kind of PRICE it publishes — official, interbank, retail, p2p or parallel. A commercial bank may post a retail board rate or an interbank one, so the two are orthogonal.",
        "spread_bps is now measured WITHIN a rate_type. A corridor carrying several kinds of price therefore carries several 0.0 spreads, one per kind. Previously a central bank's official reference was ranked against executable retail quotes, which reported dispersion nobody could ever have traded.",
        "official and parallel prices are not executable. They are still returned and labelled, but excluded from best on /convert and from best_rate and avg_spread_bps on /corridors — naming a published reference as the best available rate would be recommending a price nobody can get.",
        "- BREAKING: provider_type values changed. 'mto' is now 'imto' (the licence the CBN issues) and 'fintech' is now 'fintech_psp'. If you switch on those strings, update them.",
        "- /rates, /convert and /rates/history now return rate_type and provider_type on each entry; /providers returns rate_type alongside provider_type.",
        "- Applies to the REST API, the MCP tools and the terminal alike."
      ]
    },
    {
      "date": "2026-07-11",
      "title": "Tiered data freshness + new API quotas",
      "tags": [
        "New",
        "API"
      ],
      "body": [
        "Data freshness is now part of the plan ladder: Free and Individual API keys serve rates as of the top of the current UTC hour, while Team keys serve every observation in real time. Responses always state which you got via data_freshness (plus as_of when hourly) and an X-Data-Freshness header.",
        "- Daily API quotas are now Free 25 / Individual 500 / Team 5,000 requests.",
        "- Applies to the REST API and MCP tools; the terminal keeps live ticks on every plan."
      ]
    },
    {
      "date": "2026-07-10",
      "title": "Free tier raised to 25 requests/day",
      "tags": [
        "Improved",
        "API"
      ],
      "body": [
        "The free daily quota is now **25 requests/day**, up from 10. Pro (**10,000/day**) and Enterprise (**100,000/day**) are unchanged.",
        "The quota is shared across all of an account's keys and resets at 00:00 UTC. Every response still carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`."
      ]
    },
    {
      "date": "2026-07-10",
      "title": "Email alert when you hit your daily API limit",
      "tags": [
        "New"
      ],
      "body": [
        "When an account crosses its daily quota, the owner now gets a one-per-day email with a direct upgrade link — so a `429` never comes as a surprise.",
        "Track consumption any time from **Developers → My Keys** or via `GET /api/v1/admin/usage`, which reports used / remaining / reset plus a per-key breakdown and 7-day history and never counts against your quota."
      ]
    },
    {
      "date": "2026-07-10",
      "title": "Developer FX endpoint suite: fetch-*, time-series, historical, change",
      "tags": [
        "New",
        "API"
      ],
      "body": [
        "A fastforex-style convenience layer for building fintech products on Modan:",
        "- `GET /fetch-one`, `/fetch-multi`, `/fetch-matrix`, `/fetch-many-to-one` — mid-market rates for one or many pairs in a single call (one quota unit regardless of pair count), with the best tracked provider attached where the pair is a covered corridor.",
        "- `GET /time-series` — daily (`P1D`) or hourly (`PT1H`) buckets of mid + best provider.",
        "- `GET /historical` — a corridor snapshot as of a past date, and `GET /change` — absolute and % move of mid and best over a period.",
        "- `GET /rates/provider` — every corridor and current rate a single provider quotes.",
        "- `GET /admin/usage` and `GET /status` (no key required) for metering and platform health."
      ]
    },
    {
      "date": "2026-07-09",
      "title": "Sub-second API responses",
      "tags": [
        "Performance"
      ],
      "body": [
        "Reworked the request path — single round-trip authentication, reads issued in parallel with auth, background usage logging and a loose-index-scan snapshot — bringing every public endpoint under **1 second** warm (from ~2.4s).",
        "No changes required on your side."
      ]
    },
    {
      "date": "2026-07-08",
      "title": "MCP server — call Modan from AI agents",
      "tags": [
        "New"
      ],
      "body": [
        "Modan now runs a native **Model Context Protocol** server at `https://modan.io/api/mcp`. Claude, Cursor, Codex and any MCP client get live African FX as tools — `get_rates`, `convert`, `fetch_rates`, `get_history`, `list_corridors`, `list_providers`, `list_currencies` — authenticated with the same API key and daily quota.",
        "Errors come back as readable messages an agent can act on."
      ]
    },
    {
      "date": "2026-07-06",
      "title": "Independent mid-market reference",
      "tags": [
        "New"
      ],
      "body": [
        "Rate responses now include an independent mid-market benchmark when a fresh reference exists: top-level `mid_rate` / `mid_source` / `mid_fetched_at`, and per-provider `vs_mid_bps` (basis points versus that mid; negative means the provider pays out below mid).",
        "These fields are omitted when no recent reference is available — absence is explicit, never fabricated."
      ]
    },
    {
      "date": "2026-07-03",
      "title": "New endpoints: /convert and /currencies, paginated history, JSON errors",
      "tags": [
        "New",
        "Improved"
      ],
      "body": [
        "- `GET /convert` returns per-provider gross and net-of-fee delivered amounts and flags the provider with the best value for the recipient.",
        "- `GET /currencies` lists active currencies and the corridors currently served.",
        "- `GET /rates/history` is now paginated (`limit`, `offset`, `order`, `has_more`) with `period` = 1d / 7d / 30d / 90d.",
        "- Unknown paths now return a JSON `404` with an actionable message instead of HTML."
      ]
    },
    {
      "date": "2026-06-28",
      "title": "Rate ingestion API + CSV bulk upload",
      "tags": [
        "New"
      ],
      "body": [
        "Data-team accounts can push observations via `POST /api/v1/rates` (one object or an array of up to 100, atomic per batch) or the admin console's CSV/JSON bulk uploader. Ingestion does not consume the read quota, and every write is append-only and audited."
      ]
    },
    {
      "date": "2026-06-20",
      "title": "Rate and spread alerts",
      "tags": [
        "New"
      ],
      "body": [
        "Set rate-above / rate-below and spread-above / spread-below alerts per corridor in the terminal. The evaluator runs every 15 minutes and can notify you in-app, by email, or both."
      ]
    },
    {
      "date": "2026-06-10",
      "title": "Public REST API launch",
      "tags": [
        "New",
        "API"
      ],
      "body": [
        "Modan is now a developer platform: sign up, grab an API key, and pull live African FX in minutes. Launch endpoints include `GET /rates`, `/corridors` and `/providers`, authenticated with an `X-API-Key` header and metered by daily quota.",
        "Machine-readable references ship alongside: [openapi.json](/openapi.json) (OpenAPI 3.1), [llms.txt](/llms.txt) and [llms-full.txt](/llms-full.txt)."
      ]
    }
  ]
}
