Server-side CRM proxy, scoped per account

All CRM traffic goes through a single authenticated server route that injects the caller's own location id and token, so credentials never reach the browser and calls can never be aimed at another account.

What it is

POST /api/ghl takes a path, method and body. It resolves the caller's credentials, injects the location id into the query string or body where the endpoint expects it, and forwards the call. An account with no CRM connected gets a structurally valid empty payload on reads and a 503 with code ghl_not_connected on writes.

Also called: API proxy · no keys in the browser · location injection

See it
Server-side CRM proxy, scoped per account
🔒ridgelinepostframe.com/project/ghl-ap856
Hobby Shop
Framing · on schedule
A request diagram: browser → /api/ghl (auth + credential injection) → vendor API, with the key drawn only on the server side of the boundary. Sample data — no customer information appears here.
How it works
  1. 1Client posts { path, method, body }.
  2. 2The route authenticates and resolves per-org credentials.
  3. 3Location id is injected into empty location_id / locationId params, or added when absent — except for endpoints that already carry it in the path, which reject the query param.
  4. 4The upstream call is made with the Bearer token and version header.
  5. 5Non-2xx bodies are returned to the caller rather than swallowed, with the status attached.
Why we built it

The header states the multi-tenant reason: 'Per-org scoped: each tenant uses their own GHL location id + API key.' The empty-payload behaviour is a UX decision recorded there too — an unconnected tenant gets '{ contacts: [], opportunities: [] } for read paths and a 503 for write paths — keeps the dashboard rendering cleanly without spamming the user with errors.' The path-vs-query nuance is a real vendor quirk: endpoints like /locations/{id}/customFields 'REJECT a locationId query param ("property locationId should not exist")'.

The problem
  • CRM credentials would otherwise have to be exposed to client code.
  • An unconnected tenant saw error noise instead of an empty dashboard.
Sound familiar?
What you get
One choke point for every CRM call, always scoped to the caller.
Unconnected accounts render cleanly.
Vendor quirks are handled once, centrally.

See it on your own jobs

Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.

or keep browsing features →