Integration · Platform engineering
How to build an API facade
Four teams have each written their own Salesforce client, and a field rename breaks all four in different ways. The shape of a facade that absorbs it, the prompts that build it, and the production detail that decides whether it holds.
Built with Tray Headless
- System Consumers
- Step Verify caller
- Step Map to the contract
- Step Call upstream
- System Upstream systems
One contract in front of many systems. The facade owns the shape, so an upstream change stops at the boundary.
The short answer
What is an API facade?
Done well, an API facade is four things: one stable contract that consumers code against, mapping that absorbs upstream change rather than passing it through, the caller identity carried all the way to the source system, and explicit behaviour when an upstream is slow or down. Teams usually come unstuck on building a pass-through. A facade that forwards the upstream shape has moved the coupling one hop and solved nothing.
What matters here
- The facade owns the shape. A pass-through moves the coupling one hop and changes nothing.
- A renamed upstream field should stop at the facade. That is the entire value proposition.
- Carry the caller identity through. A shared service account destroys the audit trail in every system behind it.
- Version the contract and run versions in parallel. You cannot make every consumer change on the same day.
- Decide what happens when an upstream is down, because the default is that your facade is down too.
Who this is for
You run platform or integration engineering. Several teams integrate with the same systems, each has its own client, and every upstream change turns into several separate incidents.
How it works in practice
The sequence, from a consumer calling the facade to getting an answer.
- 1
The caller is authenticated and identified
As themselves, not as the facade, because the identity has to reach the source.
- 2
The request is mapped to the facade contract
Which is designed around what consumers need, not around what an upstream returns.
- 3
One or more upstreams are called
Often several, which is the case that justifies a facade at all.
- 4
Responses are mapped into the contract shape
Absorbing field names, enums and quirks, so none of them reach the consumer.
- 5
Failure is handled deliberately
Partial data with a stated gap, a cached answer with its age, or a clear error.
- 6
The call is logged with the caller and the upstreams
So a question about who read what has an answer.
What the facade is made of
Four parts. The second is the one that decides whether this was worth building.
A contract consumers designed against
Shaped by what callers need. If it mirrors an upstream response, you have written a proxy and inherited every future change.
Absorbing mappings
A renamed field, a new enum value, a split object: all handled inside the facade, none visible outside it.
Carried identity
The caller reaching the source system as themselves. A shared service account makes every upstream audit log useless.
Defined degradation
What a consumer gets when an upstream is slow or down, decided in advance rather than discovered during an incident.
The Tray Headless prompts
Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.
Once per project, run
/tray-workflows:set-workspace
to pick the workspace these build in. Point it at a sandbox first.
- 1
Begin by designing the contract from the consumer side
A contract shaped like an upstream response is a proxy.
Headless skills
build-workflowtray-patternsUse build-workflow. The systems in play are Salesforce, NetSuite and Zendesk, or whatever we run in those seats. Design the facade contract from what consumers actually need, not from what any upstream returns. For each endpoint, write down the fields a caller uses and where each one comes from, which is frequently more than one system. A customer endpoint might carry the account from the CRM, the billing status from the ERP and the open ticket count from support. No upstream returns that object, and that composition is the reason the facade exists. Name fields in your own vocabulary. If the contract says Account_Status__c you have published an upstream implementation detail and you will be maintaining it for years.
- 2
Map so upstream changes stop at the boundary
This is the entire value proposition.
Headless skills
build-workflowUse build-workflow. Put an explicit mapping between every upstream field and every contract field, and handle: A renamed upstream field, which changes only the mapping A new enum value, which maps to a contract value or to a stated unknown rather than passing through raw A field that splits into two upstream, which the mapping recombines A field an upstream removes, which becomes null with a documented reason rather than a missing key Never spread an unmapped upstream object into the response. It works on the day you write it and turns the facade into a proxy the first time somebody adds a field upstream. Validate every response against the contract before returning it. A facade that returns a shape it did not promise is worse than no facade, because consumers have stopped defending themselves.
Validate outbound as well as inbound. The contract is a promise, and the only way it stays true is if something checks it on the way out.
- 3
Carry the caller identity through to the source
A shared service account destroys every audit log behind it.
Headless skills
tray-gotchasUse tray-gotchas, then handle identity properly. Authenticate the caller at the facade, and carry that identity to the upstream call instead of replacing it with one shared account. Where an upstream supports acting on behalf of a user, use it. Where it does not, record the caller against the call in the facade audit log so the chain can be reconstructed even though the upstream cannot see it. Enforce authorisation at the facade too. A consumer allowed to read customers is not automatically allowed to read their contracts, and the facade is where that distinction is cheap to make. Every upstream log line reading integration-service tells you nothing about who asked. Once several teams are behind the facade, that is the single most expensive shortcut available here.
- 4
Decide what happens when an upstream is down
The default is that your facade is down too.
Headless skills
tray-patternsDefine degradation per endpoint before this goes anywhere near production: Return partial data with an explicit statement of which part is missing, instead of failing the whole response Serve a cached answer with its age attached, so the consumer can decide whether it is good enough Fail clearly, with which upstream failed and whether retrying will help Set a timeout per upstream that is shorter than the consumer timeout, or a slow upstream becomes a timeout everywhere at once. Cache deliberately: a stated TTL per endpoint, and never for anything a caller might write immediately afterwards. A facade that quietly serves stale data is harder to debug than one that is simply slow. The facade holds no state beyond that cache. Once it owns data, it is a system instead of a facade, and it needs a completely different conversation about consistency.
- 5
Version the contract, and run versions in parallel
You cannot make every consumer change on the same day.
Version the contract from the first release, in the path, and run at least two versions at once. A breaking change means a new version, not an edit. Additive changes belong in the current version. Removing a field, renaming one or changing a type is breaking even if you think nobody uses it, and the facade logs will tell you whether that is true. Track usage per consumer per version so a retirement is a conversation with three named teams, not an announcement into the dark. Report: calls per consumer per endpoint, upstream latency and error rates separately from facade ones, cache hit rates, and contract validation failures. Validation failures are the leading indicator. Each one is an upstream change that reached a consumer.
- 6
Validate, then hand the mappings to platform engineering
Because upstreams change and consumers keep arriving.
Run the per-step schema checks and the whole-workflow audit before this touches production. Rename a field in a sandbox upstream and confirm the contract response is unchanged. Then open the same workflow in Tray Build so platform engineering can add endpoints, adjust mappings and change the degradation rules in the visual canvas.
What it connects to
The facade composes several systems into one contract, which is why it exists.
Salesforce
Read account and contact data as the calling user, so the upstream audit trail stays meaningful.
Reads
NetSuite
Read billing status and balances, which no CRM response carries and every consumer asks for.
Reads
Snowflake
Serve derived attributes that no operational system holds, without consumers needing warehouse access.
Reads
Datadog
Emit per-upstream latency and error rates separately from facade metrics, so blame is attributable.
Writes
Same build, other stacks
The design does not change if you run something else in one of these seats. The same prompts build it against Microsoft Dynamics 365, SAP S/4HANA, Google BigQuery, Jira, HubSpot or Oracle.
Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.
Running it in production
Several teams will depend on this. A contract that moves is worse than no contract.
The platform runs it, not a laptop under a desk
Consumers call it continuously, so it needs the uptime, timeouts and retry behaviour of infrastructure, not of a script.
Every call is logged with its caller
Who asked, which upstreams answered, and how long each took. This is the audit trail that a shared service account would have thrown away.
No credential ever lands in the workflow itself
Upstream credentials held in your workspace and scoped per system, with the caller identity carried through rather than replaced.
Platform engineering own the mappings
Endpoints, field mappings, cache TTLs and degradation rules all open in Tray Build, changed without a deployment.
Contract validation runs on the way out
A facade that returns an unpromised shape is worse than none, because consumers have already stopped defending themselves.
Questions people ask
Why not just proxy the upstream API?
Because a pass-through moves the coupling one hop and solves nothing. When the upstream renames a field, every consumer still breaks; the only difference is that they blame you.
Why carry the caller identity through?
Because a shared service account makes every upstream audit log useless. Once several teams sit behind one facade, no system behind it can answer who read a record.
Should the facade cache?
With a stated TTL per endpoint, and never for data a caller might write immediately afterwards. Beyond a cache the facade should hold no state, because owning data makes it a system instead of a facade.
How should the contract be versioned?
In the path, with at least two versions running at once and usage tracked per consumer. Removing or renaming a field is breaking even when you believe nobody uses it.
What happens when an upstream is down?
Whatever you decided in advance: partial data with the gap named, a cached answer with its age, or a clear error. Deciding during an incident means the answer is that the facade is down too.
Further reading
Background on the same subject, for the case rather than the build.
Related guides
Platform engineering
How to build webhook fan-out
Acknowledge before you fan out, verify the signature first, retry per consumer, and make replay possible. The Headless prompts that build it.
Platform engineering
How to build a change data capture pipeline
Read the log instead of polling a timestamp, overlap the snapshot with the stream, and keep deletes and order intact. The Headless prompts that build it.
Data operations
How to build a customer 360 and master data sync
Pick a survivorship order per attribute, resolve identity on more than email, and publish a golden record every system can point at. The prompts.
Last reviewed September 2026.