Webhooks for your customers
Webhooks for an API's developers, delivered to their own receiver under the Startup's name, signed, retried for three days and replayable from any point.
A Customer hears a Startup's Events through a Watch: a standing request to post each Event a selector matches to the Customer's URL. Deliveries carry the Startup's own signature header, retry for three days, and replay from any point.
The catalog a receiver selects from
Every Verb the Publication exposes emits one Event named for its Noun and the past tense. Leaseline declares a Lease Noun, so Lease.renew emits Lease.renewed. That list is the Startup's Event catalog, in each Verb's reference.
A Watch hears only what its key may read. Harbor Residential's key reaches its own Tenant, so its Watch never sees another Customer's leases.
Create a Watch
curl -X POST https: //api.leaseline.app/ watches \
-H "Authorization: Bearer $LEASELINE_API_KEY" \
-H "Content-Type: application/ json" \
-d '{"selector": "Lease.renewed", "url": "https: //harborresidential.com/ hooks/ leaseline"}'{
"api": "https: //api.leaseline.app",
"watch": {
"$id": "https: //api.leaseline.app/ watches/ watch_9o4vCuR17L",
"$type": "Watch",
"selector": "Lease.renewed",
"url": "https: //harborresidential.com/ hooks/ leaseline"
},
"secret": "whsec_7d2b9e4a1c6f3b8e0a5d2c9f4b7e1a3c",
"links": { "self": "https: //api.leaseline.app/ watches/ watch_9o4vCuR17L" }
}The signing secret appears once. A selector names one Event or every Event of a Noun, Lease.*, and where narrows it: Lease.renewed where rent.amount > 1500.
The delivery
{
"$id": "https: //api.leaseline.app/ events/ evt_e7XmKZ6oGO",
"$type": "Event",
"name": "Lease.renewed",
"at": "2026-10-03T14: 10: 00Z",
"seq": 21,
"data": {
"lease": { "$id": "https: //api.leaseline.app/ leases/ lease_qImdtSGZVN", "$type": "Lease", "status": "renewed" }
}
}Leaseline-Signature: t= 1791036600, v1= 5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdEach v1 is a hex HMAC-SHA256 of t, a dot and the raw body, keyed with the signing secret. Verify the raw bytes, accept any matching v1, and reject a timestamp older than five minutes.
Retries, pauses and replay
Answer any 2xx within 10 seconds. An unacknowledged delivery retries 12 times over three days, and each try is recorded with its status code.
A Watch whose deliveries all fail for three days pauses itself, and its Events keep queuing. Watch.resume delivers them oldest first, and Watch.replay sends everything since a chosen time again. Events are kept forever, so any point works.
Watch.rotate issues a new secret and signs with both for 24 hours. A Watch created with "payload": sends only the record's versioned address. Webhooks has the full delivery rules.