Create a capture session
curl -X POST https://aisenseapi.com/services/v1/webhook_capture{
"ok": true,
"capture_id": "3cdadccf-9ea9-47b3-a221-6e62f63a198d",
"update_url": "https://aisenseapi.com/services/v1/webhook_capture/3cdadccf-9ea9-47b3-a221-6e62f63a198d/update",
"read_url": "https://aisenseapi.com/services/v1/webhook_capture/3cdadccf-9ea9-47b3-a221-6e62f63a198d",
"expire_timestamp": 1787266344,
"expire_datetime": "2026-08-20T22:52:24+00:00"
}The POST body stays empty. There is nothing to configure and nothing to sign up for.
Two URLs come back from that one call. Give the update_url to the service you want to inspect. Keep the read_url for yourself. Both addresses carry the same capture ID, so this is the entire setup step.
Save the ID somewhere. The service publishes no listing call and cannot look a capture up any other way, which is also what keeps strangers out of yours.
Send anything to the update URL
Point a sender at the update URL and trigger it. Stripe posts JSON. GitHub posts JSON with a signature header alongside. An older system might send form data or plain text. Everything lands in the same place.
curl -X POST "https://aisenseapi.com/services/v1/webhook_capture/3cdadccf-9ea9-47b3-a221-6e62f63a198d/update" \
-H "Content-Type: application/json" \
-H "X-Example-Signature: test-123" \
-d '{"event":"payment.created","amount":4200}'{
"ok": true,
"capture_id": "3cdadccf-9ea9-47b3-a221-6e62f63a198d",
"captured_at_timestamp": 1787179950,
"captured_at_datetime": "2026-08-19T22:52:30Z",
"request": {
"method": "POST",
"uri": "/services/v1/webhook_capture/3cdadccf-9ea9-47b3-a221-6e62f63a198d/update",
"headers": {
"host": "aisenseapi.com",
"user-agent": "curl/7.88.1",
"accept": "*/*",
"content-type": "application/json",
"x-example-signature": "test-123",
"content-length": "41"
},
"client_ip": "203.0.113.10",
"body": {
"json": { "event": "payment.created", "amount": 4200 },
"text": null,
"base64": null,
"raw_length": 41
}
}
}Notice that the update call answers with the capture itself. That helps while you are poking at it by hand, because the result appears without a second request.
Read the captured request back
Later, or from a different machine, fetch the capture with a plain GET.
curl https://aisenseapi.com/services/v1/webhook_capture/3cdadccf-9ea9-47b3-a221-6e62f63a198dThe response matches what the update call returned, byte for byte. An unknown or expired capture ID answers with HTTP 404 instead.
One behaviour is worth knowing before you rely on it. Each new request to the update URL replaces the one before it. A capture holds the most recent delivery, never a history. Read a capture before you trigger the next attempt, or the earlier one is gone.
Any method, any body shape
The body arrives reported three ways, and exactly one of them is filled in.
Valid JSON is parsed into body.json. Readable text that is not JSON goes to body.text. Binary content is Base64 encoded into body.base64. The unused slots stay null, so your code can simply branch on whichever one is not null.
curl -X PUT "https://aisenseapi.com/services/v1/webhook_capture/3cdadccf-9ea9-47b3-a221-6e62f63a198d/update" \
-H "Content-Type: text/plain" \
-d 'hello from a sender'"body": {
"json": null,
"text": "hello from a sender",
"base64": null,
"raw_length": 19
}PUT, PATCH, DELETE and GET all work the same way. A request carrying no body at all is still a valid capture: the three slots stay null and raw_length reports 0. That alone proves the sender reached you.
What the free webhook capture API endpoint records
Every capture comes back in the same shape.
| Field | Type | Description |
|---|---|---|
| capture_id | string | The UUID that appears in both the update URL and the read URL. |
| captured_at_timestamp | integer | Unix time at which the request arrived. |
| captured_at_datetime | string | The same moment as an ISO 8601 timestamp in UTC. |
| request.method | string | The inbound HTTP method, such as POST, PUT, PATCH, DELETE or GET. |
| request.uri | string | The path the sender actually requested. |
| request.headers | object | Every header received, with lowercase names. |
| request.client_ip | string | The sender's IP address as the service saw it. |
| request.body.json | object | The parsed value when the body is valid JSON. |
| request.body.text | string | Text content when the body is not JSON. |
| request.body.base64 | string | Binary content, encoded so it survives JSON transport. |
| request.body.raw_length | integer | Byte count of the body as received. |
The three steps in order
- Create the session
One POST to
/webhook_capturereturns the capture ID and the two URLs. Copy them into your notes or a shell variable. - Point the sender at the update URL
Paste the update URL into the webhook settings of Stripe, GitHub, Shopify, a CI job or your own script. Then fire a real event at it.
- Read what arrived
GET the read URL and compare the headers and body against the provider documentation. Most integration surprises show up right here.
Why this beats a local tunnel
Debugging a webhook usually starts badly. You install a tunnel client, sign in, start a listener, then copy a URL that changes again the next time you run it. Providers only deliver to public HTTPS addresses, so your laptop is never an option on its own.
The free webhook capture API endpoint removes that whole step. The URL is public and served over HTTPS from the moment it exists. Nothing runs on your machine. A colleague on another continent opens the same read URL and sees the same capture.
It also works where a tunnel simply cannot. A CI runner, a serverless function or an AI agent can each be handed a callback URL and inspected afterwards, without any of them holding a port open.
Common uses
Most people reach the free webhook capture API endpoint with nothing but a terminal open. Four patterns come up again and again.
Webhook debugging
Compare what a provider really sends with what its documentation claims.
Integration setup
Capture one real sample before writing any parsing or validation code.
Signature inspection
See which signature headers arrive and how long the raw body actually is.
Agent callbacks
Give an automated process a temporary callback URL and read the result later.
Pairing it with the other web endpoints
A capture answers one question: what did the sender actually send. Other endpoints answer the questions that follow.
Run a payload through the Validation API endpoint once you know its shape. Park a large sample in the Storage API endpoint so a colleague can fetch the exact bytes. Confirm a caller is reachable at all with the Ping API endpoint, and check the address it came from with the Client IP API endpoint.
This service is also the passive half of a pair. Its sibling, the Webhook Action API endpoint, asks a person for a decision rather than recording a machine.
Expiry, security and limits
Treat a capture as readable by anyone holding the link. The service records every header and the complete body, so provider signatures and Authorization tokens end up stored alongside the payload. No key and no account exist here, which means the unguessable read URL is the only thing protecting a capture.
Captures become unreachable 24 hours after the session is created. The expire_timestamp and expire_datetime fields in the create response state exactly when that happens.
The base URL is https://aisenseapi.com/services/v1. Every service on the free public REST APIs hub shares one limit of 5000 requests per IP per 24 hours. A create call, the inbound webhook and your read all count against it, so a full debugging round costs three requests.