Developer documentation
Diagnosing a request
Every response carries an id. What it is for, and how to get from a failure to the reason for it.
The request id
Every response this platform produces carries X-XeroProxy-Request-Id, and every refusal repeats it as error.requestId in the body. It is not a trace header borrowed from somewhere else: it is the primary key of that request's row in the account's log, so quoting it identifies exactly one request and nothing else.
curl -sD - -o /dev/null \
-H "Authorization: Bearer $XERO_PROXY_KEY" \
https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices | grep -i x-xeroproxy-request-id- Log it on every call your integration makes, successful or not. It costs one header and it is the difference between a support conversation and a guessing game.
- It is generated before anything is checked, so even
missing_credential- a refusal made before the proxy knows who you are - carries one. - Quote it to whoever administers the account. They can open it directly and see which gate answered, what it cost, and what caching, idempotency and the quota policy each decided.
Working out what refused you
The proxy answers a request in one of four ways, and the response tells you which without anybody having to look it up.
| What you see | What happened |
|---|---|
X-Proxy-Error: true | This platform refused it. error.code says which gate; the errors page lists all of them. It never reached Xero. |
X-XeroProxy-Cache: hit | Answered from a stored read. It never reached Xero and spent none of the organisation's Xero budget. |
X-XeroProxy-Idempotent-Replay: true | Answered from a recorded write. The write was not carried out again. |
| None of the above | Forwarded to Xero. The status, headers and body are exactly what Xero returned. |
A 429 without X-Proxy-Error is Xero saying the organisation is out of budget. A 429 with it is this platform saying the budget exists and this key is not the one entitled to spend it. They need different responses, which is why they are distinguishable.
When the metadata is not enough
Request and response bodies are financial data and are not recorded. Occasionally a failure cannot be explained without them, so an account owner can turn on a capture window for a single key from the API keys page.
- Per key, never account-wide, and lasting at most an hour.
- It requires a written reason, which is stored against the key and in the audit log along with who turned it on.
- Captured bodies are encrypted at rest, capped at 64 kB each, and expire on their own.
- Credentials and
Idempotency-Keyare replaced with[redacted]before anything is written. No diagnosis needs the bearer token back, and an idempotency key would let whoever read it replay the write. - Turning capture off discards everything it collected, rather than leaving it to expire.
If you are asked to reproduce a failure while capture is on, do it with test data where you can. The window is short and the storage is encrypted, but the shortest-lived copy of a customer's ledger is still one that was never made.