The translation
Each step a webhook.site user takes has a counterpart here. The webhook.site facts are from its own documentation, pricing and terms pages as read on 4 November 2026; where those pages give two different numbers, both are quoted.
| Step | webhook.site | aisenseapi.com |
|---|---|---|
| Get a URL | Open the site, or create a token through its API | POST /services/v1/webhook_capture with an empty JSON body |
| What you get | A unique URL, an email address and a DNS name, without signing up | capture_id, update_url, read_url, wait_url and expire_timestamp |
| Send | Any HTTP method to the URL | Any HTTP method to update_url |
| What the sender gets back | The URL's default response, which you can edit | The capture itself, as JSON |
| Read | The web page, or the requests route of its API | GET read_url: one JSON document with method, path, headers, client address and body |
| Wait for arrival | Watch the page update | GET wait_url, 0 to 25 seconds per call; returns early with waited_seconds and wait_reason |
| Be told | Custom Actions, with a subscription | notify_url at creation: one signal after the first request |
| Requests kept per URL | Up to a cap on a free URL; its pages give 50 in one place and 100 in another | One. The first request wins, and retries return it |
| Body size | Under 10 MB | 256 KB |
| Lifetime | 7 days for a free URL | 24 hours |
| Email and DNS capture | Yes | No |
| Account or key | None for a free URL; API keys belong to accounts | None |
| Who can read it | Anyone who knows the URL's id, on a free URL | Anyone who knows the capture id |
The create call looks like this, and the three URLs in its answer are all you need:
curl -X POST https://aisenseapi.com/services/v1/webhook_capture \
-H "Content-Type: application/json" \
-d '{}'{
"ok": true,
"capture_id": "6f8c9e52-3f2c-4e73-9d3b-8d6c3f6d1c91",
"status": "pending",
"update_url": "https://aisenseapi.com/services/v1/webhook_capture/6f8c9e52-.../update",
"read_url": "https://aisenseapi.com/services/v1/webhook_capture/6f8c9e52-...",
"wait_url": "https://aisenseapi.com/services/v1/webhook_capture/6f8c9e52-.../wait/25",
"expire_timestamp": 1772893200
}What differs
- One request per capture. webhook.site keeps a list of requests behind one URL. A capture here holds the first request that reaches its update URL and nothing after it: it is the proof of one delivery, not an inbox. A test that expects three deliveries creates three captures. For a stream of requests you want to browse, webhook.site is the better tool.
- No page to look at. Every answer is JSON, written to be read by code. The same capture is also available to AI agents as the
create_webhook_captureandread_webhook_capturetools of the free MCP server, so an agent can receive a callback without writing a client. - Built to be awaited.
wait_urlholds the connection for up to 25 seconds and returns the moment the request lands, andnotify_urlsends one signal to a URL you name, which must use a host name rather than an IP address. webhook.site answers the same need with Custom Actions on a subscription. - Shorter and smaller. A capture lives 24 hours from creation, against 7 days for a free webhook.site URL, and takes a body of at most 256 KB, against 10 MB. Both are enough for a webhook payload; neither is file transfer.
- The same openness. On both services a free capture is readable by whoever knows its id, which is a long random identifier in both cases. Send test secrets only. The sender's address, every header and the complete body are stored as they arrived.
A script that waits for the callback
Create the capture, hand the update URL to the sender, then wait. One call to wait_url costs one request and returns after at most 25 seconds, so a loop that waits all day spends about 3,500 of the 5,000 requests an address has per day. For anything longer than a few minutes, give notify_url instead and let the signal come to you.
import requests
BASE = "https://aisenseapi.com/services/v1/webhook_capture"
created = requests.post(BASE, json={}, timeout=10).json()
print("Give the sender:", created["update_url"])
while True:
answer = requests.get(created["wait_url"], timeout=35).json()
if "request" in answer:
break
request = answer["request"]
print(request["method"], request["uri"])
print(request["headers"])
print(request["body"]) # one of json, text or base64 is filled, with raw_lengthThe same three steps in curl, for a shell test:
curl -s -X POST https://aisenseapi.com/services/v1/webhook_capture -H "Content-Type: application/json" -d '{}'
# point the sender at update_url, then:
curl -s "https://aisenseapi.com/services/v1/webhook_capture/{capture_id}/wait/25"The update call answers the sender with the capture itself, so a sender that logs its responses shows you the same document.
What a capture records
| Field | Type | Meaning |
|---|---|---|
| captured_at_timestamp | integer | Unix time of the first request. |
| captured_at_datetime | string | The same moment in ISO 8601, UTC. |
| request.method | string | The HTTP method the sender used. |
| request.uri | string | The path the sender actually requested, query string included. |
| request.headers | object | Every header received, with lower-case names. |
| request.client_ip | string | The sender's address as the service saw it. |
| request.body | object | json, text or base64, exactly one of them filled, and raw_length in bytes. A request with no body is still a capture, with all three null and a length of 0. |
Errors
| Case | Status | What it means |
|---|---|---|
| Unknown capture id | 404 | No capture has that id. |
| Capture older than 24 hours | 410 | The capture has expired and its data is gone. |
notify_url with an IP address as host | 400 | The signal URL must use a host name. |
| The day's requests used up | 429 | The shared limit of 5,000 requests per address per day; wait for the reset. |
Every refusal is JSON with error and fix.
Privacy and limits
A capture stores the sender's address, every header and the complete body, so provider signatures and Authorization headers end up stored beside the payload for 24 hours, readable by anyone who holds the capture id. Use it with test credentials, and see our privacy policy for log retention.
Every path shares the service-wide ceiling of 5,000 requests per IP address per day. A full round costs three: the create call, the inbound request and your read. A capture takes one request of at most 256 KB and lives 24 hours from creation. webhook.site, for comparison, states that a free URL and its data are removed after 7 days, that a free URL stops accepting requests at its cap, and that its paid plans start at 9 dollars a month; those are its figures as of November 2026 and may change.