Request capture
Included free on every install, replay and diff included, with every request kept for 24 hours. Capture rules, retention up to 30 days and saved replay sets need a license with the
request-capture-profeature. See pricing.
Request capture records each request to the Admin API and the Content API, with its response, for 24 hours. You can see exactly what a client sent and what it got back, compare a failing call with a working one, and send a call again after a fix.
What is recorded
Section titled “What is recorded”| Kept | Left out or masked |
|---|---|
| Method, URL, status, duration in milliseconds, tenant. | Credential headers such as Authorization and X-Api-Key, stored as [REDACTED]. |
| Request and response headers. | Sensitive values in JSON, XML, form and plain-text bodies, redacted before they are stored. |
| Request and response bodies, up to 64 KiB each. | The bodies of routes that carry a credential, such as a password or a secret shown once. |
Captures expire after 24 hours, or after the retention of the capture rule that kept them, and are deleted within ten minutes of expiring. A replay keeps the lifetime of the capture it replays. Capturing never slows a request down. Under heavy load some captures are dropped rather than queued, and the server log counts them.
Deleting a tenant deletes its captures. A privacy erasure request for a person deletes the captures that name them.
Try it
Section titled “Try it”You need an admin token in TOKEN. The
quickstart shows how to get one.
-
List the latest
GETcaptures:Terminal window curl "http://localhost:3001/api/admin/request-captures?method=GET&limit=2" \-H "Authorization: Bearer $TOKEN"{"data": [{"id": "56cea807-47e3-413d-beca-28f6128a172e","captured_at": "2026-10-01T09:14:36Z","method": "GET","url": "/api/admin/logging/levels","request_headers": { "Authorization": ["[REDACTED]"], "User-Agent": ["curl/8.18.0"] },"status_code": 200,"response_headers": { "Content-Type": ["application/json"] },"response_body": "{\"default_level\":\"INFO\",\"plugins\":{},\"tenants\":{}}","duration_ms": 1.548,"tenant_id": "default","ttl_seconds": 86400}],"total_count": 247,"limit": 2,"offset": 0}Copy an
idintoCAPTURE_ID. -
Fetch that one capture in full:
Terminal window curl http://localhost:3001/api/admin/request-captures/$CAPTURE_ID \-H "Authorization: Bearer $TOKEN" -
Replay it. A
GETneeds onlyconfirm_replay:Terminal window curl -X POST http://localhost:3001/api/admin/request-captures/$CAPTURE_ID/replay \-H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" \-d '{"confirm_replay": true}'The answer holds
original,replayanddiff. The replay is stored as a new capture, and its id isreplay.id. Copy it intoREPLAY_ID. -
Compare the two:
Terminal window curl http://localhost:3001/api/admin/request-captures/$CAPTURE_ID/diff/$REPLAY_ID \-H "Authorization: Bearer $TOKEN"{ "id1": "56cea807-47e3-413d-beca-28f6128a172e", "id2": "aa51f554-ab7a-4aca-8e89-a762fb626029", "status_match": true, "status_code1": 200, "status_code2": 200, "duration1_ms": 1.548, "duration2_ms": 1.344 }The real answer also carries
headers_diff, described below. -
Delete the replay when you are done. The answer is
204:Terminal window curl -X DELETE http://localhost:3001/api/admin/request-captures/$REPLAY_ID \-H "Authorization: Bearer $TOKEN"
The admin console has no page for captures, so you work with them over the API.
Replay a call safely
Section titled “Replay a call safely”A replay sends the stored request again, with your own Authorization and
X-Api-Key in place of the redacted ones. You confirm that with
"confirm_replay": true. Replaying a POST, PUT, PATCH or DELETE
changes data again, so it also needs "confirm_mutating": true.
override_url sends the request to a different path. It must keep the
original scheme and host.
Read a diff
Section titled “Read a diff”status_match says whether the two statuses agree. body_diff is a line diff
of the response bodies, and headers_diff lists the response headers that
differ, ignoring Date, X-Request-Id, X-Response-Time and the rate limit
counters. Each
appears only when the two captures differ. Content-Security-Policy and
X-Csp-Nonce carry a fresh value on every answer, so they always show in
headers_diff.
Choose what is captured
Section titled “Choose what is captured”Capture rules need request-capture-pro. Without rules, every request is captured and kept 24 hours. Once a tenant has
at least one enabled rule, it captures only the requests a rule matches, and
the first matching rule, oldest first, decides how long each is kept.
curl -X POST http://localhost:3001/api/admin/request-captures/rules \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"route_pattern": "/api/v1/content/orders/**", "method": "POST", "sample_rate": 0.5, "retention_seconds": 604800}'| Field | Meaning | Default |
|---|---|---|
route_pattern | An absolute path. A segment is literal, a glob such as report-*, * or {name} for any one segment, or ** as the last segment for everything below it. /api/v1/** also matches /api/v1. Required. | |
method | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS or *. | * |
sample_rate | The share of matching requests to keep, from 0 to 1. | 1 |
retention_seconds | How long a capture is kept, from 3600 (one hour) to 2592000 (30 days). | 86400 |
enabled | Whether the rule applies. | true |
The answer is 201 with the rule. PATCH /api/admin/request-captures/rules/{id}
changes the fields you send, and DELETE removes the rule. A change reaches
every replica within 30 seconds.
If the license lapses, enabled rules keep applying with their retention.
Turning a rule off, editing a rule that is off and deleting a rule stay free.
Creating a rule, turning one on and changing one that is on answer 402.
Replay a set of captures
Section titled “Replay a set of captures”Creating a set needs request-capture-pro. A replay set is a named list of up to 50 of your captures, replayed together
in order with each replay compared against its original:
curl -X POST http://localhost:3001/api/admin/request-captures/sets \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "checkout smoke", "capture_ids": ["'"$CAPTURE_ID"'"]}'
curl -X POST http://localhost:3001/api/admin/request-captures/sets/$SET_ID/replay \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"confirm_replay": true}'The replay answers set_id and results, one per capture with its
capture_id, the captured method and path, and either the replay_id and diff, or an error such as
capture not found, it may have expired. A set that holds a write needs
"confirm_mutating": true as well. A capture that expired since is reported
and the rest still run, so keep the captures a set depends on with a rule
whose retention outlasts it.
A set's name is unique in its tenant. Listing, reading, replaying and deleting sets stay free after a lapse, and deleting a set leaves its captures.
Who can see captures
Section titled “Who can see captures”Every route needs an admin or super admin. An admin sees only their own
tenant's captures. A super admin may pass ?tenant_id=<tenant> to the list to
read one tenant, or ?tenant_id=* to read every tenant. An admin who names
another tenant gets 403.
The capture list, the rule list and the set list each carry licensed, which
says whether this install may create rules and sets.
Settings
Section titled “Settings”There is nothing to configure. Request capture runs on every install. If you
set LYEVE_PLUGINS to choose which features start, include request-capture
in it. See licensing and tiers.
Routes
Section titled “Routes”| Method | Path | Purpose |
|---|---|---|
GET | /api/admin/request-captures | List captures, newest first, with limit and offset. Filter with ?method=. |
GET | /api/admin/request-captures/{id} | Fetch one capture. |
DELETE | /api/admin/request-captures/{id} | Delete one capture. Answers 204. |
POST | /api/admin/request-captures/{id}/replay | Send the captured request again and compare the result. |
GET | /api/admin/request-captures/{id1}/diff/{id2} | Compare two captures. |
POST | /api/admin/request-captures/prune | Delete every expired capture now. Super admin only. |
GET | /api/admin/request-captures/rules | Your tenant's capture rules, oldest first, and licensed. |
POST | /api/admin/request-captures/rules | Create a rule. 201. Needs request-capture-pro. |
PATCH | /api/admin/request-captures/rules/{id} | Change a rule. Needs request-capture-pro unless the rule ends up off. |
DELETE | /api/admin/request-captures/rules/{id} | Delete a rule. 204. |
GET | /api/admin/request-captures/sets | Your tenant's replay sets, newest first, and licensed. |
POST | /api/admin/request-captures/sets | Create a set: name and capture_ids. 201. Needs request-capture-pro. |
GET | /api/admin/request-captures/sets/{id} | One set. |
DELETE | /api/admin/request-captures/sets/{id} | Delete a set and keep its captures. 204. |
POST | /api/admin/request-captures/sets/{id}/replay | Replay every capture in the set. |
Errors
Section titled “Errors”| Status | Message | Cause |
|---|---|---|
400 | override_url must have the same scheme and host as the original capture | The override points somewhere else. |
403 | tenant_id mismatch | An admin named another tenant. |
402 | payment_required, naming feature:request-capture-pro | Creating a rule or a set, or turning on or changing an enabled rule, without the license. |
403 | super_admin required | An admin called prune. |
404 | capture record not found | Unknown id, or another tenant's capture. |
404 | capture rule not found, replay set not found | Unknown id, or another tenant's rule or set. |
409 | failed to create replay set | Your tenant already has a set with that name. |
422 | route_pattern must be an absolute path such as /api/v1/orders/{id} or /api/v1/** | The pattern does not parse. route_pattern may use ** only as its last segment is the same cause. |
422 | sample_rate must be between 0 and 1, retention_seconds must be between 3600 (one hour) and 2592000 (30 days), method must be one of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS or * | A rule field is out of range. |
422 | name must be 1 to 255 characters, capture_ids must hold 1 to 50 captures, every capture id must name a capture this tenant holds | A set is not valid. |
428 | replay requires confirm_replay: true, which acknowledges auth header substitution, code request_capture.confirm_replay | confirm_replay is missing. |
428 | replaying a POST request requires confirm_mutating: true, code request_capture.confirm_writes | The capture changes data and confirm_mutating is missing. |
428 | the set holds a write, so replaying it requires confirm_mutating: true, code request_capture.confirm_writes | A set replay without confirm_mutating. |
502 | replay could not reach the target | The replayed request failed to connect. |
Related
Section titled “Related”- Logs: every line a request wrote.
- Request profiling: which routes are slow.
- PII masking: keep personal data out of what is stored.