API logs
Every request you made, with both bodies and the reason it failed.
Every call to the API is recorded: the method, the path, the status, how long it took, the headers and both bodies. When a call did not do what you expected, this is the only place that can tell you what you actually sent.
API logsstatus: error
| Method | Endpoint | Status | Duration | Version |
|---|---|---|---|---|
| POST | /payments | 201 | 412 ms | 2026-09-01 |
| POST | /payments/trx_CSUGajfv9xh0XQ5wu2lx | 200 | 88 ms | 2026-09-01 |
| POST | /transfers | 201 | 690 ms | 2026-09-01 |
| POST | /payments/trx_9mWvL2xR7tB5nY4hC6dF | 402 | 1203 ms | 2026-09-01 |
| GET | /balance | 200 | 42 ms | 2026-09-01 |
| POST | /payments | 500 | 5012 ms | 2026-08-01 |
Filters
Nine filters, all combinable, all reflected in the URL so a filtered view can be shared.
| Filter | What it matches |
|---|---|
| Search | Full text over the URL, the request id, the IP, the method and the error message |
| Status | success for under 400, error for 400 and above, or one exact code |
| Method | GET, POST, PUT, PATCH, DELETE |
| Endpoint | A substring of the URL |
| IP address | A substring of the IP as Konsole shows it. The caller's address is stored in pseudonymised form, not in clear |
| Date start, date end | Whole calendar days |
| Min duration, max duration | Milliseconds, for finding the slow tail |
There is no query language
Search is plain text, not a syntax. status:5xx matches nothing, because it is looked for
literally. Use the status selector for codes and the search box for strings.
Results are paginated 50 at a time, newest first. Logs are kept 90 days, live and sandbox alike: export what you need to keep beyond that.
What a row carries
| Field | Note |
|---|---|
| Request ID | The correlation id, also returned to you as X-Request-Id |
| Method, endpoint and full URL | The full URL includes the query string |
| Status | The code and its label |
| Duration | Server side, excluding your network |
| IP address | The caller's address, in pseudonymised form |
| Error | Present only when the call failed |
| API version | The version this call was served with |
| API key | Which key was used: its first 15 characters, as the Keys page lists it |
| Connected account | Set when the call acted on a connected account |
The key is the fastest way to find calls made by a component you forgot about: an old script still holding a rotated key shows up under its own key.
The detail panel
Selecting a row opens the full record: request headers, response headers, request body, response body, user agent and, when the call failed, the error message. No stack trace is stored.
This is where you settle arguments with yourself. The body shown is what the API received, after your framework, your HTTP client and your serialiser had their turn with it.
Large bodies are not kept, and neither are bodies that are not JSON
A request body above roughly 250 KB, or a response above 500 KB, is not stored at all: in its
place the log keeps {"_truncated": true, "_size": …}, the size in bytes. A response that is not
JSON (a file, a page) is stored empty. Both are kept whole below those sizes.
What is hidden, and what is deliberately not
Secrets are redacted before the log is written, so nothing sensitive is stored at all.
| Redacted | Kept visible |
|---|---|
Authentication headers (Authorization, X-API-Key, X-Sync) and cookies | Every other header |
Passwords, private keys and card security codes (password, private_key, cvv, cvc), wherever they appear | phone, email, name, address |
PINs and card numbers: any field named as a PIN or a PAN, card.number at any depth, and a bare number field | account_number, iban, phone_number |
Card expiry (exp_month, exp_year, expiry_date) | Amounts, currencies, metadata, references |
The second column is a deliberate decision: business fields stay readable because a log you cannot read is a log you cannot debug with. Card data is the only thing masked on principle.
The PIN and card number rule errs on the side of hiding: mpin, pincode and transactionPin
are masked, and so can be a field whose name only looks like one. A field you expected to see that
shows as ***REDACTED*** is usually that case.
Your key is never in the log
If you are looking for which key made a call, use the API key shown on the log. The Authorization header
itself is stored as ***REDACTED***.
Correlating with your own logs
Every API response carries X-Request-Id. Log it on your side, and any support conversation or
Konsole search becomes a single lookup instead of a hunt through timestamps.
curl -i https://api.wajub.com/payments \
-H "Authorization: sk.xxxxxxxxxxxxxxxx…" \
-d amount=25000 -d currency=XAF \
| grep -i x-request-idExport
The current filter set can be exported as CSV or JSON, capped at 10,000 rows per export. The CSV carries the id, method, path, status, duration, pseudonymised IP, error and timestamp; the JSON carries the full records.
Exporting takes the Manage Webhooks permission, not only the one that lets you read the logs.
Exports count against your plan's Exports per month allowance, which every export of the
Dashboard shares (payments, transfers, invoices, customers, Konsole…). Past it, the export returns
a 403 telling you so rather than a truncated file.