Outcomes & troubleshooting
Every way a lookup can end, what each outcome tells you, the 60-second cache, and a symptom-to-cause list.
Every lookup ends in exactly one outcome. This page is the one to keep open while you're getting an integration working.
The outcomes
| Outcome | When | What it means |
|---|---|---|
ok | 2xx and at least one mapped field resolved | The agent sees the data. |
not_found | Upstream returned 404 | A genuine answer: no such record. Not a broken integration. |
unauthorized | Upstream returned 401 or 403 | Your check rejected us — usually a secret mismatch. |
rate_limited | Upstream returned 429 | Your system throttled us. |
unavailable | Upstream returned 5xx | Your system is temporarily down. |
unreachable | DNS, TLS, timeout, blocked address, non-HTTPS URL | We never got a response at all. |
invalid_response | 2xx, but the body isn't a JSON object — or no mapped field resolved | We got a reply we couldn't use. |
not_found is an answer, not a failure
If an agent asks for shipment SHP-0000 and it doesn't exist, 404 is the correct thing
for your endpoint to return. It isn't an error to be smoothed over.
So don't map "no such record" onto a 200 with an empty body — that would resolve no fields
and surface as invalid_response, which tells the agent your integration is broken when in
fact it worked perfectly and the answer was simply no.
Return 404. The agent sees "no such record," which is what they needed to know.
A failed lookup is still a successful request
The lookup call succeeds even when your endpoint didn't. The agent gets a normal 200
back, carrying an outcome that describes what happened.
Failure is data, not an exception. An agent whose lookup returns unavailable sees that
your system is down; they don't see a broken helpdesk.
The 60-second cache
An identical lookup — same source, same ticket, same parameters — within 60 seconds is served from the recent result instead of re-hitting your endpoint.
The rule for which results are cached is the useful part:
- Settled outcomes are cached:
okandnot_found. These are real answers, and asking again within a minute would give the same one. - Degraded outcomes always re-hit:
unauthorized,rate_limited,unavailable,unreachable,invalid_response. These are situations that might have just been fixed.
That asymmetry is what makes retrying work. When you deploy the corrected secret, or your API comes back up, the agent's next attempt genuinely reaches your server — it isn't served a minute-old failure.
Why your server didn't see that request
If you're watching your access logs while testing and a lookup doesn't appear, this is
usually why: an identical ok or not_found within the last 60 seconds was served from
cache.
Change a parameter, wait a minute, or use the dry run — which always calls your endpoint.
Symptom → cause
unauthorized
Your endpoint returned 401 or 403, so your verification rejected us.
- You rotated the secret without redeploying. Rotation is immediate and has no overlap window, so every lookup starts failing the instant you rotate. Check this first — it's the one that breaks a working integration.
- Your canonical string doesn't match ours. Run a dry run and diff your payload against the
returned
signed_payload. The usual culprits: the missing trailing dot on a GET, a dropped query string, or a re-serialized body. - A custom header carrying your own API credential is wrong or expired — if your endpoint sits behind a gateway that checks one.
unreachable
We never got a response.
- The URL isn't HTTPS. Plain
http://fails immediately. - The address is private.
localhost, a VPN, a VPC address, or a public hostname that resolves to a private IP. We block these deliberately — you cannot test against your laptop. - It timed out. Default 5s, hard cap 10s. If your lookup is genuinely slow, raise the source's timeout — but note it's clamped at 10s, so an endpoint that needs 30 seconds needs to get faster, not a bigger number.
- DNS or TLS failed. An expired certificate will land here.
invalid_response
We got a 2xx we couldn't use.
- You returned an array or a scalar. It must be a JSON object at the top level. If your
endpoint naturally returns a list, wrap it:
{"results": [...]}. - None of your mapped paths resolved. Usually your response shape changed — a renamed
field, or a new wrapper object that shifted
statustodata.status. A dry run shows you exactly which paths resolved and which didn't. - You returned
200with an empty body for a missing record. Return404instead; see the callout above.
unavailable or rate_limited
These are about your system, not the integration — a 5xx or a 429 from your endpoint.
The integration is working correctly; it's faithfully reporting that your service is down or
throttling us.
Both are degraded outcomes, so they're never cached: as soon as your system recovers, the next lookup gets through.