If you've ever integrated with more than one UK government API, you already know the pain: each one has its own auth flow, its own error format, its own rate limits, its own quirks in sandbox vs production. HMRC alone has a dozen+ API families under Making Tax Digital, each slightly different from the last.
I got tired of solving this from scratch on every project, so I built GovBridge UK — a single gateway that normalizes HMRC MTD, Companies House, DVLA, and Land Registry behind one consistent interface.
What it handles:
A single error envelope across all four government APIs, instead of learning four different failure formats OAuth token management and refresh handled once, not per-integration 40+ working HMRC endpoints already wired up and sandbox-tested (Income Tax MTD, VAT, CIS, PAYE, Property/Savings income, and more) A developer portal (React) so you can explore and test endpoints without reading four separate sets of gov.uk docs
Stack: .NET 8/10, ASP.NET Core, PostgreSQL, Redis for token/session caching, React/TypeScript for the portal.
It's still evolving - security hardening and the CT600/GovTalk integration are in progress. If you've built anything against HMRC's MTD APIs, Companies House, or DVLA, I'd genuinely like to hear how you handled the integration overhead - did you roll your own, or is there something out there I've missed?
Top comments (6)
The single error envelope is the right instinct I think and it's also the part I'd pressure-test first, because normalizing away four failure formats also normalizes away which upstream changed.
Government APIs don't version like normal vendors. Rules change on legislated dates, sometimes the sandbox lags production, and sometimes an authority republishes an artifact without moving a version label at all. When that happens the caller's question is never "what does error 4012 mean." It's "did this break because HMRC changed something last Tuesday, and when." A flattened envelope answers the first and destroys the second.
What worked for us in the EU e-invoicing equivalent, where the rule packs revise roughly twice a year across a dozen authorities: put provenance in the envelope itself. Every response names the ruleset version it was evaluated against and carries a SHA of the exact bytes we vendored, mirrored into response headers so it lands in the caller's logs without them parsing anything. A scheduled job re-fetches every upstream artifact and flags a digest mismatch, which is the only thing that catches a silent republish, since the version label doesn't move. And a request can pin a version or an as-of date and get the historical answer back.
On sandbox-tested specifically: worth checking whether each sandbox is actually on the same rule version as its production counterpart. In our domain they routinely aren't, and 40 green sandbox endpoints is a weaker signal than it feels like.
To answer your actual question, everyone I know rolled their own, which is why the layer is worth building. The moat isn't the normalization, it's being the one who noticed the upstream moved.
This is a sharp critique, and honestly exactly the kind of gap I hadn't fully reasoned through - I built a single error envelope for consistency, but you're right that it can bury why something changed, not just that it failed.
The provenance + digest idea is the right fix. A concrete example from tonight: I hit a case where an HMRC test-support endpoint needed a different API version than the "parent" endpoint's own docs implied nothing in the error told me that, I only found it by cross-referencing HMRC's own GitHub stub repo. A digest-mismatch flag would have caught that class of drift automatically instead of me hand-discovering it.
Your sandbox-vs-production point is fair too - everything I've verified so far is sandbox-only. Worth being explicit about that rather than letting "40 endpoints tested" imply more confidence than it should.
Going to sit with the provenance idea properly, thank you for this, Tobias.
This is the kind of engineering detail that usually matters more in production than the headline feature. Small boundaries compound when a workflow runs every day.
Agreed, and it's the kind of thing that's invisible until it isn't. A silent upstream drift on day one is a curiosity. The same drift six months into a production workflow is an incident. Appreciate you flagging it.
That split makes sense. Discovery scope and execution authorization are different controls, and collapsing them makes the manifest look more powerful than it should be. I like the expiry point too; permission without time bounds gets stale fast.
Exactly. Upstream drift is one of those risks that feels theoretical until a quiet field change breaks a production workflow. Making drift visible early is much cheaper than explaining it after the incident.