Overview
Last 24 hours · Stellar Testnet
Webhook watches your wallets, checks every payment against your rules, and POSTs a signed event to your server seconds after the ledger closes. Server down? We keep retrying for two days.
No card · No node to run · First webhook in about five minutes
Paste a public G… address. We confirm its trustlines and start reading every ledger for payments to it.
Asset, exact or ranged amount, memo, allowed senders. A payment that misses is rejected with a reason code, so you always know why.
Verified or rejected, your server hears about it seconds after the ledger closes. If it doesn't answer 2xx, we keep trying for two days.
Watch payments land live, see exactly why one was rejected, and replay anything your server missed.
Overview
Last 24 hours · Stellar Testnet
Exact, min, max or range on the amount. Memo equals, present or absent. A sender allowlist when you need one. Every payment shows which rule passed and which failed.
Every attempt is logged with its status code, latency and the first 1 KB of your response. Missed a deploy? Replay every failed event since any time.
Signing secrets and API keys are shown once. Rotating keeps the old secret valid for 24 hours, so you deploy without dropping events. Paste an S… secret key by mistake and we refuse it.
The dashboard is light, works on a 360 px phone and holds its place on a slow connection. If the network lags, we catch up ledger by ledger. Nothing is skipped.
One shared secret, one HMAC, one comparison. The payload carries the payment exactly as it landed on the ledger, tx hash included, so you can check it yourself.
// Express: verify the signature, then trust the payload const raw = express.raw({ type: "application/json" }); app.post("/hooks/stellar", raw, (req, res) => { const t = req.get("Webhook-Timestamp"); const expected = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(t + "." + req.body) .digest("hex"); const sent = req.get("Webhook-Signature"); // v1=<hex> if (!matches(sent, expected)) { return res.sendStatus(400); } const { type, data } = JSON.parse(req.body); if (type === "payment.received") { markInvoicePaid(data.payment.memo, data.payment.amount); } res.sendStatus(200); // anything else and we retry }); // matches(): a constant-time compare. Full version in the docs.
Free on testnet. All you need is a wallet address and a URL that returns 200.