Reference
On this page
Request bins API
Endpoints for request bins and the requests they capture. Authentication, roles, errors and paging are described in the API overview. How bins answer, the header rules and the signature check are explained in HTTP request bins.
The capture URL itself (https://app.testlie.com/in/<token>/...) takes no authentication. The endpoints below need a team API key or a session.
Bins
| Method and path | Role | Description |
|---|---|---|
GET /api/bins |
Member | Every bin of the team, with captureUrl and requestCount. Signature secrets are shown to admins only |
POST /api/bins |
Admin | Create a bin (fields below). 201 { bin } |
GET /api/bins/:id |
Member | One bin |
PATCH /api/bins/:id |
Admin | Change any field below. signatureCheck: null removes the check |
DELETE /api/bins/:id |
Admin | Delete the bin and its requests. The capture URL stops working. 204 |
| Field | Notes |
|---|---|
name |
Required, 1 to 80 characters |
projectId |
Optional. A project of your team, or null |
responseStatus |
200 to 599 |
responseHeaders |
Object of header names and values, at most 30, from the allowed headers |
responseBody |
Up to 64 KB |
responseDelayMs |
0 to 10,000 |
rules |
Up to 20 rules, first match wins: { "method"?, "path"?, "status", "headers"?, "body"?, "delayMs"? }. method empty or * matches any method; path is a glob on the path after the token (/hooks/*) |
signatureCheck |
{ "format", "header"?, "secret" } or null. format is sha256=hex, hex, base64, stripe or testlie; header defaults to X-Signature and is ignored for stripe and testlie |
curl -X POST https://app.testlie.com/api/bins \
-H "Authorization: Bearer $TESTLIE_API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "Payment callbacks", "responseStatus": 200, "responseBody": "{\"ok\":true}",
"responseHeaders": { "Content-Type": "application/json" },
"rules": [{ "method": "POST", "path": "/refunds/*", "status": 503, "headers": { "Retry-After": "5" } }] }'{
"bin": {
"id": "cm3b8…",
"name": "Payment callbacks",
"projectId": null,
"token": "q8Zk…",
"captureUrl": "https://app.testlie.com/in/q8Zk…",
"responseStatus": 200,
"responseHeaders": { "Content-Type": "application/json" },
"responseBody": "{\"ok\":true}",
"responseDelayMs": 0,
"rules": [{ "method": "POST", "path": "/refunds/*", "status": 503, "headers": { "Retry-After": "5" } }],
"signatureCheck": null,
"requestCount": 0,
"createdAt": "2026-10-23T10:30:00.000Z"
}
}A header that bins cannot send is refused with 400 and the header named in issues. Creating more bins than the plan allows answers 402.
Captured requests
| Method and path | Role | Description |
|---|---|---|
GET /api/bins/:id/requests |
Member | List requests, newest first, without headers or bodies: method, page, limit (default 100, max 500). { total, page, limit, requests } |
DELETE /api/bins/:id/requests |
Member | Clear the bin. { deleted } |
GET /api/bins/:id/requests/wait |
Member | Long-poll for a request (see below) |
GET /api/requests/:id |
Member | One request with headers and body. { request } |
DELETE /api/requests/:id |
Member | Delete one request. 204 |
GET /api/requests/:id/raw |
Member | The stored body bytes as a download (application/octet-stream); the original type is in X-Original-Content-Type |
POST /api/requests/:id/replay |
Admin | Send the request again to another URL (see below) |
A request in a list has id, binId, method, path, query, contentType, size, truncated, remoteIp, responseStatus (what the bin answered), signature (pass, fail, missing, or null without a check) and receivedAt. A single request adds:
| Field | Notes |
|---|---|
headers |
[name, value] pairs as received. Testlie's own session cookies are removed |
queryParams |
[name, value] pairs |
body.text |
The body as text, when it is valid UTF-8 |
body.json |
The parsed body, for JSON bodies and form-encoded bodies (as an object); otherwise null |
body.hex |
A hex dump of the first 4 KB |
body.isText |
Whether the body is valid UTF-8 |
Bodies over 1 MB are stored truncated (truncated: true).
Wait for a request
| Parameter | Notes |
|---|---|
method |
GET, POST, ... (case-insensitive) |
path |
The path after the token: exact (/order.created) or a glob (/hooks/*, ? for one character). A glob is checked against the newest 200 requests |
since |
ISO time; only requests received at or after it match |
timeout |
1 to 120 seconds, default 30 |
curl -f -H "Authorization: Bearer $TESTLIE_API_KEY" \
"https://app.testlie.com/api/bins/$BIN_ID/requests/wait?method=POST&path=/hooks/*&since=2026-10-23T10:30:00Z"{
"request": {
"id": "cm3c1…",
"method": "POST",
"path": "/hooks/order.created",
"query": "",
"contentType": "application/json",
"size": 42,
"responseStatus": 200,
"signature": "pass",
"receivedAt": "2026-10-23T10:30:03.008Z",
"headers": [["content-type", "application/json"], ["x-signature", "sha256=…"]],
"queryParams": [],
"body": { "text": "{\"order_id\":\"10482\"}", "json": { "order_id": "10482" }, "hex": "…", "isText": true }
}
}The response is abridged here. With several matches it returns the newest; on timeout it answers 408.
Replay
POST /api/requests/:id/replay with { "url": "https://staging.example.com/hooks/order" } sends the captured method, headers and body to url and answers with what the target returned:
{ "status": 200, "headers": { "content-type": "application/json" }, "body": "{\"received\":true}", "truncated": false, "durationMs": 184 }| Answer | When |
|---|---|
200 |
The target answered (any status; it is in status). body is its first 64 KB |
400 |
The URL is not allowed (private, loopback or link-local addresses, credentials in the URL), or the captured body was truncated and cannot be replayed |
402 |
The plan's daily replays or monthly captured requests are used up |
429 |
More than 30 replays per team per minute |
502 |
The target could not be reached: { error, durationMs } |
Hop-by-hop, proxy and forwarding headers (Host, Connection, X-Forwarded-*, ...) are not replayed, and the request carries Via: 1.1 testlie-replay. Each replay counts as one captured request, and the Free plan allows 20 replays per day.