local-webhooks
A small webhook receiver for development and testing. It accepts
GET and POST requests, returns a deterministic
response ID (the MD5 of the raw request body), and can mimic a couple of
Svix Play response options — echoing bodies back, forcing failure
status codes, and failing a random percentage of requests.
Send requests to any non-root path of
https://webhooks.leonardofaria.net, e.g.
/webhook/ — the root path serves this page.
Sending Webhooks
curl -X POST https://webhooks.leonardofaria.net/webhook/ \
-H 'content-type: application/json' \
-d '{"event":"created"}'
Successful responses return the MD5 of the raw request body:
{ "webhook_response_id": "e18ea42a5b23a8989f556dc23ec73749" }
With echo_body=true or echo=body, object
request bodies are merged with the generated response ID:
curl -X POST 'https://webhooks.leonardofaria.net/webhook/?echo_body=true' \
-H 'content-type: application/json' \
-d '{"hello":"world"}'
{
"hello": "world",
"webhook_response_id": "fbc24bcc7a1794758fc1327fcfebdaf6"
}
Non-object request bodies are returned under body because
there is no object to merge into:
curl -X POST 'https://webhooks.leonardofaria.net/webhook/?echo_body=true' -d 'hello'
{
"webhook_response_id": "5d41402abc4b2a76b9719d911017c592",
"body": "hello"
}
Object-like payloads such as { a: "b" } are accepted too:
curl -X POST 'https://webhooks.leonardofaria.net/webhook/?echo_body=true' \
-H 'content-type: application/json' \
-d '{ a: "b" }'
{
"a": "b",
"webhook_response_id": "c80eec3dbe6421d3768baf181aaf24ac"
}
Loosely-formatted bodies with unquoted keys and unquoted string values
(common when a templating engine renders into the body without quoting,
e.g. { city: {{ customer.city }} }) are parsed leniently
— unquoted multi-word values get wrapped in strings before merging:
curl -X POST 'https://webhooks.leonardofaria.net/webhook/?echo_body=true' \
-H 'content-type: application/json' \
--data-binary $'\n\n{ city: bbbb, a: "b", c: 1 }'
{
"city": "bbbb",
"a": "b",
"c": 1,
"webhook_response_id": "25b54ba83a1362ed4e5dcb3d00d3ebf8"
}
GET Requests
Any path also accepts GET requests, including subpaths like
/webhook/whatever. A bare GET returns the
response id just like an empty POST:
curl https://webhooks.leonardofaria.net/webhook
{ "webhook_response_id": "d41d8cd98f00b204e9800998ecf8427e" }
Query params are merged into the response, the same way
echo_body merges object bodies for POST:
curl 'https://webhooks.leonardofaria.net/webhook/whatever?foo=bar&baz=qux'
{
"foo": "bar",
"baz": "qux",
"webhook_response_id": "3229135643aff3d4c314486e8945ad47"
}
force_status_code and random_failure_rate work
on GET requests too.
Query Parameters
| Parameter | Description |
|---|---|
echo_body=true or echo=body |
Merge object request bodies into the JSON response with
webhook_response_id.
|
random_failure_rate=0..100 |
Percentage of requests that should fail randomly. Failed requests
return 400 unless force_status_code is
set.
|
force_status_code=400..599 |
Force this status on responses. Alone, every request returns this
code with an empty body. Paired with
random_failure_rate, only the failing slice uses it.
|
Examples:
curl -X POST 'https://webhooks.leonardofaria.net/webhook?echo_body=true' -d 'hello'
curl -X POST 'https://webhooks.leonardofaria.net/webhook?echo=body' -d '{"ok":true}'
curl -X POST 'https://webhooks.leonardofaria.net/webhook?random_failure_rate=30' -d 'hello'
curl -X POST 'https://webhooks.leonardofaria.net/webhook?force_status_code=404' -d 'hello'
curl -X POST 'https://webhooks.leonardofaria.net/webhook?random_failure_rate=30&force_status_code=503' -d 'hello'
Response Headers
Every webhook response (success and forced failure) includes:
-
webhook_response_id: MD5 of the raw request body, matching the JSON field. -
Server: local: identifies this service as the local mock. -
X-Cache: miss: signals that no upstream cache served the response.