Documentation
How Tapwire works
Quick start
- Open the inspector and select Create endpoint.
- Copy the webhook URL and send a request to it, or register it with the service you are testing.
- The request appears in the list immediately. Select it to see headers, query parameters and body.
curl -X POST 'https://<host>/h/<endpoint-id>/orders?source=docs' \
-H 'Content-Type: application/json' \
-d '{"id": 1042, "status": "paid"}'Endpoints and keys
Creating an endpoint gives you two things:
- A receiving URL of the form
/h/<endpoint-id>. It is public by design: anyone who has it can send requests to it. Anything you append (/h/<id>/any/path?x=1) is captured as the request path and query. - A private inspector key. It is required to read, change or delete anything. The server stores only a hash of it, so it cannot be recovered if lost. Your browser keeps it in local storage.
To open the inspector on another device, or to let a colleague watch the same endpoint, use Copy private inspector link. The key is placed after # in that link. Browsers never send that part of a URL to a server, and the inspector removes it from the address bar after reading it. Anyone with the link can read and delete the endpoint's requests, so share it only with people who should.
Endpoint IDs are random and carry a cryptographic check, so they cannot be guessed or enumerated.
What is captured
- Methods: GET, POST, PUT, PATCH, DELETE, HEAD and OPTIONS. Other methods receive
405and are not stored. - The path after the endpoint ID and the raw query string.
- All request headers (names in lowercase), except headers added by Cloudflare's network such as
cf-rayandx-forwarded-for. The sender's IP address and country are shown separately. - The body, byte for byte, up to 64 KB. Text is shown as text; anything that is not valid UTF-8 is shown as hex. Bodies up to 1.0 MB are accepted and truncated for storage; larger requests receive
413and only their metadata is recorded.
CORS preflight requests (OPTIONS with Access-Control-Request-Method) are captured and answered with permissive CORS headers, so browsers can call your URL from any site. Credentials are never allowed cross-origin.
Custom responses
By default an endpoint answers 200 with {"ok":true,"requestId":"…"}. Use Configure response to change:
- Status code: any value from 200 to 599. 204, 205 and 304 are always sent without a body.
- Content type:
application/json,application/problem+json,application/xml,text/xml,text/plain,text/csv,application/x-www-form-urlencoded. - Up to 20 custom headers, with printable ASCII values up to 1024 characters.
- A body of up to 16 KB.
- A delay of up to 10 seconds before responding, for testing timeouts and retries.
Responses are served from Tapwire's own domain, so some headers are reserved to protect visitors and other users. These cannot be set: set-cookie, set-cookie2, content-type, content-length, content-encoding, content-disposition, transfer-encoding, connection, keep-alive, upgrade, te, trailer, host, location, refresh, link, cache-control, pragma, expires, x-content-type-options, x-frame-options, x-xss-protection, strict-transport-security, referrer-policy, permissions-policy, feature-policy, clear-site-data, service-worker-allowed, www-authenticate, alt-svc, nel, report-to, reporting-endpoints, origin-agent-cluster, timing-allow-origin, server-timing, and any header starting with access-control-, content-security-policy, cross-origin-, sec-, proxy-, cf-, x-tapwire-.
Every response carries Content-Security-Policy: sandbox and X-Content-Type-Options: nosniff. HTML is not an allowed content type, and even if a browser opens a response directly, it cannot run scripts or submit forms.
Sending and replaying
The request sender and the Replay button send requests from your browser. That means:
- Requests to your Tapwire URL always work, because receiving URLs allow cross-origin requests.
- Requests to other servers only show a response if that server allows your origin with CORS headers. Otherwise the browser reports a network error without details.
- Requests to
localhostwork if your local server allows the Tapwire origin, which makes replaying a captured delivery to your development server straightforward. - Browsers refuse to let pages set some headers, such as
Host,CookieandUser-Agent.
For anything else, use Copy as cURL on a captured request. cURL sends the exact headers and body and has no CORS restrictions.
Server-side replay (sending from Tapwire's servers rather than your browser) is disabled on this deployment. It only ever allows public http and https destinations on ports 80 and 443, blocks private, loopback, link-local and cloud metadata addresses, checks DNS before connecting, and never follows redirects.
Limits
| Endpoint lifetime | 72 hours |
| Requests kept per endpoint | Newest 100; older ones are removed automatically |
| Largest accepted request body | 1.0 MB |
| Body stored per request | 64 KB |
| Requests per endpoint | 60 per minute; extra requests receive 429 and are not stored |
| Endpoint creation | Limited per network per hour and per day |
Tapwire runs on free-tier infrastructure with daily quotas. If a quota runs out, receiving URLs answer 503 until it resets, and the inspector shows a clear error. There is no unlimited tier.
Data retention
Endpoints and all of their captured requests are deleted automatically after 72 hours. Expired endpoints stop accepting requests immediately; the stored data is removed by a cleanup job that runs every 30 minutes. You can delete individual requests, clear the history, or delete the endpoint at any time. Deleted data cannot be recovered. See the privacy policy for details.
HTTP API
The inspector uses a small JSON API that you can call directly. Authenticate with Authorization: Bearer <inspector-key>. The API does not allow cross-origin browser requests.
| Request | Description |
|---|---|
POST /api/endpoints | Create an endpoint. Returns id, token, url and expiresAt. No authentication. |
GET /api/endpoints/:id | Endpoint details, request count and response config. |
DELETE /api/endpoints/:id | Delete the endpoint and all its requests. |
GET /api/endpoints/:id/requests | Newest first. Query: limit (1 to 100), cursor, method. |
GET /api/endpoints/:id/requests/:rid | One request with headers and body. |
DELETE /api/endpoints/:id/requests/:rid | Delete one request. |
DELETE /api/endpoints/:id/requests | Clear the history. |
PUT /api/endpoints/:id/response | Set the custom response. Invalid configs return 422 with reasons. |
DELETE /api/endpoints/:id/response | Restore the default response. |
GET /api/limits | Current limits. No authentication. |
# Create an endpoint
curl -X POST https://<host>/api/endpoints
# List requests
curl https://<host>/api/endpoints/<id>/requests \
-H 'Authorization: Bearer <inspector-key>'
# Respond with 503 after a 2 second delay
curl -X PUT https://<host>/api/endpoints/<id>/response \
-H 'Authorization: Bearer <inspector-key>' \
-H 'Content-Type: application/json' \
-d '{"status":503,"contentType":"application/json","headers":[["Retry-After","30"]],"body":"{\"error\":\"busy\"}","delayMs":2000}'Errors use one shape: {"error":{"code":"…","message":"…"}}. Request bodies in API responses have an encoding of utf8, base64 or none.
Live updates
The inspector opens a WebSocket to /api/endpoints/:id/live and authenticates by offering two subprotocols: tw.v1 and tw.auth.<inspector-key>. The server sends JSON events:
{"type":"request","request":{"id":"…","method":"POST","path":"/","query":"","bodySize":42,…}}
{"type":"deleted","id":"…"} {"type":"pruned","ids":["…"]} {"type":"cleared"}
{"type":"config"} {"type":"endpoint_deleted"} {"type":"expired"}Events contain summaries only; fetch a request by ID for its headers and body. Send the text ping to receive pong. If the connection drops, the inspector reconnects with backoff and reloads the list, so nothing is missed.
Keyboard shortcuts
/ | Focus the request search |
↑ ↓ or k j | Move through the request list |
Home End | First or last request |
← → | Switch tabs in the detail panel |
Esc | Clear the search, or close a dialog |