Monitor and troubleshoot

Common API problems

401, 403, 404, 429 and the validation failure — plus the webhook that never arrives, the signature that never matches, and the firewall nobody mentioned.

Almost every API support case is one of about ten things. This is the list, in the order to check them, with the fix rather than the theory.

Read the error first

Every error comes back in the same shape:

{ "error": true, "message": "missing X-Revenexx-Tenant header" }

The message is usually specific enough to solve the problem on its own. Read it before forming a theory. And take the X-Request-ID from the response headers — it is what turns a support case from a conversation into an answer.

Status code to cause

CodeMeansUsual causeFix
400Malformed requestX-Revenexx-Tenant missing, or the body failed validationFix the request. Do not retry it unchanged
401No valid credentialKey mistyped, truncated on copy, or revokedCheck the key. Reissue if in doubt
403Authenticated, not allowedThe key's scope does not cover this capabilityAdd the specific scope — not *
404No such resource hereThe app is not installed on this tenant, or the path is wrongCheck openapi.json for this tenant
429Rate limitedToo many requests, tenant-wideHonour Retry-After, then back off
502The implementing app or an upstream failedTransientSafe to retry with backoff

Some apps answer a failed field validation with 422 rather than 400. Treat them the same way: both mean the request was understood and rejected, and neither should be retried unchanged.

400 and 404 are worth telling apart carefully.400 with "missing X-Revenexx-Tenant" means the header never arrived — often stripped by a proxy on the integrator's side. 404 on a path that definitely exists usually means the app that provides it is not installed on this tenant, which is the classic sandbox-versus-production difference. See Apps and the Marketplace.

401 right after creating a key

Almost always the copy, not the key. Long secrets get truncated by chat clients, spreadsheet cells and terminal wrapping. Revoke and reissue rather than debugging it; it takes two minutes and the alternative takes an afternoon.

The other cause: two credentials sent at once. Send eitherX-Revenexx-Api-Key or Authorization: Bearer …, never both.

403 on some calls and not others

This is a scope gap, and it is good news — the key works, it is not allowed to do this one thing. The message names the capability. Add that capability scope to the key, or issue a new key with it.

Resist the temptation to widen to * "for now". See Keys, scopes and tenants.

The other 403 case: the app that implements the capability is not installed, or the caller is not a member of the tenant.

429 that will not go away

One-off 429s are normal and mean your backoff is working. A steady stream means something is designed wrong.

Check three things, in order:

  1. Is something looping? A job that makes one request per article is the usual culprit. It should be a bulk job — see Rate limits, quotas and fair use.
  2. Is something else running at the same time? The limit is per tenant, not per key. Your BI extract and your ERP sync compete, and so does your storefront.
  3. Does the client back off with jitter? Parallel workers retrying in lockstep recreate the spike that caused the 429.

The webhook that never arrives

Work down this list. It is in the order that resolves fastest.

  1. Did the event happen? Look in Events › Event Stream. If the event is not there, this is not a webhook problem.
  2. Does anything subscribe to that topic? The event's Routed to section answers it. "No destination subscribes to this topic yet" is the most common answer, and the fix is a subscription, not a debugging session.
  3. Is the destination enabled? A destination that failed persistently is disabled automatically. Re-enabling does not replay what was missed.
  4. What do the delivery attempts say? Persistent 4xx is your payload or credentials; persistent 5xx is their system; timeouts mean the receiver is too slow.
  5. Is the receiver reachable from the internet at all? See the firewall section below.
  6. Did it arrive and get discarded? A receiver that rejects an unverified signature answers 401, which looks like a delivery failure from your side and is actually a verification failure on theirs.

The signature that never matches

Four causes, in order of how often they turn out to be the answer.

1. The body was re-serialised. By far the most common. The signature covers the raw request bytes, exactly as received. A framework that parses JSON and re-encodes it before the verification step changes whitespace or key order, and nothing will ever match. Capture the raw body before any parsing.

2. The wrong secret. A secret copied from the wrong destination, or one that was rotated on one side only.

3. Rotation not handled. During a rotation the signature header can carry more than one comma-separated signature. A receiver that reads only the first one will reject half the deliveries. Accept the delivery if any signature matches.

4. Encoding. HMAC-SHA256, hexadecimal, compared against the value after v0=. Base64 will not match hex.

Clock skew

If the receiver checks the delivery timestamp — and it should — a clock that has drifted rejects perfectly valid deliveries. The symptom is distinctive: everything fails, all at once, with no configuration change, and the failures are 401 from the receiver rather than errors from the platform.

Fix it at the source: run NTP on the receiving host. Do not widen the tolerance window to compensate, because the window is what prevents an old captured delivery being replayed at you later.

The same applies to signed download URLs from an export: they are short-lived, and a machine whose clock is wrong will believe a valid URL has expired.

Firewall and egress

The problem that always presents as "the API is broken" and is always a network rule.

Inbound to your receiver. A webhook destination has to be reachable from the public internet, over HTTPS, with a certificate that validates. Endpoints behind a corporate VPN, on a .local name, or on a private IP range will never receive anything. Nor will an endpoint that requires a client certificate.

Outbound from your systems. A machine calling api.revenexx.com needs outbound HTTPS on port 443. In a Mittelstand network that is often blocked by default, and the symptom is a connection timeout rather than an HTTP error.

Proxies that rewrite requests. A corporate proxy that strips unknown headers will remove X-Revenexx-Tenant, and you get 400 with a message saying the header is missing — while the integrator is looking at code that clearly sets it. This is the hardest one to spot and the message is the clue.

A three-minute triage that separates all of these:

TestIf it worksIf it fails
curl from your own laptop with the keyThe key and the scopes are fineThe credential is the problem
The same curl from the integrator's serverThe network path is fineEgress, DNS or proxy
A test delivery to their endpointInbound worksTheir firewall, TLS or certificate

Do all three before anyone opens a ticket. It removes most cases immediately and tells you whose problem the rest are.

Degraded, not broken

Two response headers are worth knowing about because they explain otherwise puzzling behaviour:

  • X-Capability-Degraded — a fallback implementation served the request. It worked, but not from the usual path. Sustained degradation is worth a support case even though nothing is erroring.
  • X-Cache: HIT — the response was cached. If a write does not appear to take effect on the next read, this is the first thing to check.

When to open a support case

You have checked the status code and its message, confirmed the key and its scopes, confirmed the network path from the calling machine, and the failure is reproducible. Bring:

  • The X-Request-ID from a failing response.
  • The tenant slug, the key name (never the secret), and the exact path and method.
  • When it started, and what changed around then.
  • For a webhook: the event id and the destination name.

Next