{ code, message, body, errors } — code mirrors the HTTP status, body carries the payload, and errors is an empty array.
Error responses use a different, smaller envelope. They are returned as:
requestId whenever you contact support — it lets us locate the exact request in our logs.
The export endpoints do not perform dedicated input validation that returns
400. A malformed cursor is not rejected with a 400; it surfaces as a 500 (see Invalid cursor below). Malformed since/until values are likewise not separately validated. Treat the cursor as opaque and send ISO-8601 timestamps to avoid these cases.Status code overview
| Code | When | Retry? |
|---|---|---|
200 | Page returned (may be empty rows) | n/a |
401 | Authorization header missing or not Bearer … | No — attach a token |
401 | JWT invalid or expired | Yes, after re-auth |
401 | Lender record not found or inactive in the main DB | No — contact support |
403 | lenderPublicId in the URL does not match lenderId in the JWT | No |
404 | Unknown section code (e.g. /S9/D01) | No |
404 | Unknown dataset code (e.g. /S1/D99) | No |
404 | Dataset listed in the catalog but available: false (reserved) | No — revisit later |
500 | Lender database_name missing on the LenderAccount record | No — contact support |
500 | Invalid / corrupted cursor | No — fix client |
500 | Other internal server error | Yes, after backoff |
504 | Gateway timeout — query exceeded the proxy limit (large high-volume via datasets) | Yes — narrow with a time window, lower limit, then retry |
Error reference
401 — Missing Authorization header
401 — Missing Authorization header
401 — Invalid or expired JWT
401 — Invalid or expired JWT
Trigger: The token fails signature verification or has expired. Both cases are reported identically.Recovery: Obtain a fresh token using the Refresh Token endpoint, or re-authenticate via Lender Login.
401 — Lender not found or inactive
401 — Lender not found or inactive
Trigger: The token is well-formed and matches the URL, but the lender record cannot be found in the main database or is not active.Recovery: This usually indicates an account state issue rather than a client bug. Capture the
requestId and contact support.403 — Lender mismatch
403 — Lender mismatch
Trigger: The Recovery: Re-authenticate with the correct lender and use the matching
lenderPublicId in the URL path does not equal the lenderId encoded in the JWT. This strict check (requireLenderMatch) runs on every export request.lenderPublicId in the path.404 — Unknown section
404 — Unknown section
Trigger: The Recovery: Call the catalog endpoint to discover valid section codes.
sectionCode in the path is not one of S1–S5 (e.g. /exports/S9/D01).404 — Unknown dataset
404 — Unknown dataset
Trigger: The section exists but the Recovery: Call the catalog endpoint to discover valid dataset codes for each section.
datasetCode does not exist within it (e.g. /exports/S1/D99).404 — Dataset unavailable
404 — Dataset unavailable
500 — Database not configured
500 — Database not configured
Trigger: The lender record was found but has no Recovery: This is a server-side configuration issue. Capture the
database_name, so the tenant connection cannot be resolved.requestId and contact support.500 — Invalid cursor
500 — Invalid cursor
Trigger: The Recovery: Never modify a cursor. Drop the bad cursor and restart from the last known-good
cursor query value is malformed or corrupted and cannot be decoded. Because cursor decoding is not surfaced as a validation error, it is returned as a generic internal error rather than a 400.nextCursor, or from the beginning of the dataset.500 — Generic internal error
500 — Generic internal error
Trigger: Any other unexpected server-side failure.Recovery: Retry after a short backoff. If it persists, capture the request URL, the response body, and the time, then contact support.
504 — Gateway timeout
504 — Gateway timeout
Trigger: The query took longer than the API gateway’s timeout to return. This happens on the highest-volume
via (TIER 2) datasets — most notably D23 Manual Communication Logs (tens of millions of rows) — when a page is requested without a time window, so the scan/sort spans the entire collection.Because the timeout is raised by the gateway (not the application), the response is not the standard JSON error envelope — it’s a plain gateway 504 with no requestId.Recovery:- Always bound large
viapulls with asince(and optionallyuntil) window — pull in slices (e.g. month-by-month) rather than one unbounded scan. - Lower the
limit(e.g.200–500) so each page does less work. - Retry the same windowed request after a short backoff.
?includeTotal=trueon aviadataset also requires a time window (it returnstotalNote: "window_required"otherwise); addsince/untilbefore requesting a total.
limit request still times out, capture the full request URL and time, then contact support.Common debugging hints
- Unexpected
401? Your token most likely expired. Refresh it via/admin/auth/{lender}/refresh-tokenand retry. 403? The JWT was issued for a different lender than the one in the URL path. Re-authenticate with the correct lender subdomain.404on a dataset that appears in the catalog? Check itsavailableflag. Iffalse, the dataset has not been activated for your tenant yet — contact support to schedule it.- Unexpected
500? Capture the request URL, the response body (includingrequestId), and the time, then contact support. 504on a large dataset (e.g.D23)? The query is scanning too much. Add asince/untilwindow and pull in smaller time slices, and consider loweringlimit. See the 504 — Gateway timeout guidance above.