Usage and diagnostics
What Havnly actually received from you. When an integration misbehaves you can see what your code believes it sent; this is the other half.
GET /usage{
"data": {
"window": { "since": "2026-09-21T19:15:13Z", "until": "2026-09-22T19:15:14Z" },
"requests": {
"total": 28, "ok": 22,
"client_errors": 6, "server_errors": 0, "rate_limited": 0,
"error_rate_pct": 21.4
},
"latency_ms": { "p50": 670, "p95": 2491, "slowest": 2748 },
"by_path": [
{ "path": "/claims", "method": "POST", "calls": 8, "errors": 2, "p95_ms": 1679 },
{ "path": "/claims/{ref}/dates", "method": "POST", "calls": 5, "errors": 3, "p95_ms": 2748 }
],
"rate_limit": { "per_minute": 600, "remaining": 573, "resets_at": "2026-09-22T19:16:00Z" }
}
}The default window is the last 24 hours — enough to cover an overnight batch.
Pass ?since= for anything else.
by_path collapses identifiers, so a thousand calls against a thousand
requests read as one line rather than a thousand. It is the fastest way to see
that your errors are concentrated in one call rather than scattered.
rate_limited is counted separately from other 4xx. Being throttled is a
different problem from sending bad requests, and adding them together sends
people looking in the wrong place.
The calls themselves
GET /usage/requests?errors=true{
"data": [
{
"key": "hvn_sk_e5e91f221",
"method": "POST", "path": "/claims/REQ-95A4A7/dates",
"status": 409, "duration_ms": 612,
"error": null, "at": "2026-09-22T17:33:29Z"
}
]
}This is the one that ends an argument about whether a request arrived. Each row names the key prefix that made it, which is enough to tell a nightly batch from a live system without exposing anything secret.
| Parameter | Meaning |
|---|---|
errors=true | Only 4xx and 5xx |
status | Comma-separated codes, e.g. 409,422 |
method | GET, POST, PATCH, DELETE |
since | ISO timestamp. Default 24 hours ago |
limit, cursor | Up to 250 a page |
Request logs are kept for 30 days.
Your keys
GET /usage/keys{
"data": [
{
"prefix": "hvn_pk_00a8f5040",
"environment": "live",
"scopes": ["claims:read", "claims:write"],
"is_active": true,
"rate_limit_per_minute": 600,
"last_used_at": "2026-09-21T21:41:38Z",
"in_use_now": false
}
]
}Never a secret, only a prefix. last_used_at is the column worth watching:
a key nobody has used in months is a key that should not still work.
Turning off a key you have lost
POST /usage/keys/self/revokeThe key making the call stops working immediately. Use it when you believe a key has leaked and you want it dead before you can reach a browser.
A key can only revoke itself. If a leaked key could disable its siblings, the leak would become a way to take your whole integration down — revoking the key in your own hand is the one action that cannot be turned against you.
To issue a replacement, or to disable a different key, use Havnly → API Console. That deliberately needs a person.