Deliver webhooks reliably
Tell another system about an event and make sure it hears, even through a 500 error or a five-second timeout: each delivery is signed, retried with growing pauses, and set aside with the details if every attempt runs out. Pay per use, no subscription.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
signed, retried, dead-lettered
Why delivery needs more than one attempt
Every product that works with other systems eventually has to reach another application's address and trust that the message lands. Firing it off is one line of code; making it survive a receiver that restarts, a 500 error or a five-second timeout is where teams quietly lose a week. The simple version, one attempt and no retry, fails silently at the first network blip. This tool is the delivery layer teams keep rebuilding: you give the destination address, the content and any extra headers, and it signs the message, delivers it and keeps trying when the other side does not answer properly.
How each delivery is protected
Each delivery carries a signature made with a shared secret, so the receiver can confirm the content really came from you and was not altered on the way. If the destination answers with an error or does not answer in time, the attempt is repeated after growing pauses, which gives a restarting system room to recover. When every attempt is used up, the message is not dropped: it is set aside and reported back to you with the details of the last answer. A common mistake is a receiver that skips the signature check, which leaves the door open to forged calls.
FAQ
How does the receiving system know the message came from me?
Each delivery carries a signature made with a shared secret. The receiver checks it to confirm that the content really came from you and was not changed on the way, so forged calls can be rejected.
What happens if the other system is down or slow?
If the destination answers with an error or does not answer in time, delivery is tried again after pauses that grow longer each time. That gives a restarting system room to recover instead of losing the message at the first blip.
What if every attempt fails?
Nothing is dropped. Once all attempts are used up, it is set aside and reported back to you with the details of the last answer, so you can fix the cause and send it again instead of discovering the gap weeks later.
Why not just contact the other system from my own code?
Sending it is one line of code; surviving a receiver that restarts, a 500 error or a five-second timeout is where teams quietly lose a week. This adds retries, a signature and a holding place for failures without you building them.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/notify/webhook \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hook"}'const res = await fetch("https://api.kit.forhosting.com/notify/webhook", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"url": "https://example.com/hook"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/notify/webhook",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"url": "https://example.com/hook"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/notify/webhook", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"url":"https://example.com/hook"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"url":"https://example.com/hook"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/notify/webhook", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"url": "https://example.com/hook"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "notify.webhook",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Firing a webhook is one line of code; making it survive a flaky endpoint, a 500 error or a five-second timeout is where teams quietly lose a week. This endpoint takes the payload you give it, signs it, delivers it with retries, and tells you clearly if it can't.
The part of webhooks nobody wants to build twice
Every product that integrates with the outside world eventually needs to call someone else's URL and trust that the call lands. The naive version — a single HTTP POST with no retry — fails silently the moment a receiver restarts or a network blip happens. notify.webhook is the delivery layer teams keep reinventing: exponential backoff, signature headers, and a place for calls to go when they truly can't be delivered.
What a call to the endpoint does
If the target responds with a non-2xx status or times out, delivery is retried on a backoff schedule; if all attempts are exhausted, the failed call is dead-lettered and surfaced back to you with the response details instead of vanishing. POST /notify/webhook takes a target URL, a JSON body and optional custom headers, then attempts delivery with an HMAC signature attached so the receiver can verify the payload actually came from you and wasn't tampered with in transit.
Signing has a real history worth respecting
Payload signing on webhooks became a de facto standard because early webhook systems had no way to prove a callback wasn't forged by a third party who guessed the URL. We generate a signature per delivery using a secret you control, following the same header-and-hash pattern most modern webhook consumers already know how to verify, so integrating on the receiving end takes minutes, not a spec review.
Where it sits in a larger flow
Chain it after an alert-routing decision, or use it as the outbound leg of a sync job that needs to notify a downstream system the moment work completes. Because delivery is async and tracked by task_id, you can fire a webhook from inside another automation — a payment event, a build finishing, a form submission — without blocking on the receiver's response time.
What you see when it's done
Every attempt is logged with its HTTP status, latency and response body excerpt, so a failed delivery isn't a mystery — you get the same detail a curl command would have shown you. Final status, successful or dead-lettered, arrives through a signed webhook of your own or a signed link valid for 24 hours, whichever you've configured for this endpoint's results.
Integration use cases
Notify a downstream system on completion
A batch export job finishes and posts a webhook to the customer's ERP so their inventory updates without anyone polling for status.
Bridge legacy systems that can't poll
An older internal tool that only accepts inbound HTTP calls gets reliably notified of new orders, with retries covering its occasional downtime.
Third-party integration handshake
A marketplace app relays order events to a merchant's own webhook endpoint, with signature verification so the merchant trusts the payload.
Cross-service event fan-out
One internal event triggers webhook calls to three different partner endpoints in parallel, each tracked and retried independently.
Technical questions
How does the webhook delivery API sign requests?
Each delivery includes an HMAC signature header computed from your configured secret and the payload body, so the receiver can verify authenticity before trusting the data.
How many retries happen before a webhook is considered failed?
Failed deliveries are retried automatically up to three times with backoff; after that the call is dead-lettered and reported back with full response details, and it is never charged.
Is notify.webhook free to use?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What does a webhook delivery cost?
It's a flat $0.002 per POST /notify/webhook request, whether the delivery succeeds on the first attempt or after retries, with no extra charge per retry attempt.
Can I send custom headers with the webhook?
Yes, you can attach any custom headers alongside the automatic signature header, useful for auth tokens the receiving endpoint expects.
What happens to webhooks that never succeed?
They land in a dead-letter state you can inspect via the task result, with the last response status and body included so you can debug the receiving side.
Can I use this for bulk webhook delivery?
Yes, each call is an independent async task, so you can fire many in parallel from a loop or a batch job without them blocking each other.
Is the payload data retained after delivery?
No, payloads and delivery logs are deleted after the stated retention period and are never used for training.