Developer Platform
Webhook receiver and background-job polling examples
Run the exact-body webhook receiver and bounded AI rebuild poller published at webhooks.buildwithhq.com.
webhooks.buildwithhq.com publishes runnable examples for the two async integration patterns developers need first: accepting signed events in their own backend and polling a public background job to terminal state. The reference site does not collect or retain customer webhook payloads.
1. Run the receiver locally
$env:BWHQ_WEBHOOK_SIGNING_SECRET = "the one-time subscription secret"
$env:BWHQ_WEBHOOK_QUEUE_DIR = "C:\ProgramData\MySaaS\webhook-queue"
node webhook-receiver.mjs
Publish the local /webhooks/buildwithhq route through your own HTTPS reverse proxy and register that URL as the subscription destination. Keep the signing secret and queue storage in the trusted backend.
2. Verify, deduplicate, queue, acknowledge
- Read no more than the configured body limit as exact bytes.
- Verify HMAC-SHA256 over
<timestamp>.<raw-body>before parsing. - Reject timestamps outside five minutes and malformed envelopes.
- Atomically accept the event UUID once in durable storage.
- Return 202 for a new queued event or 200 for a known event.
- Process later with an idempotent CRM, billing, identity, or onboarding handler keyed by
event.id.
The example logs only event ID, type, and version. Replace its filesystem store with a production queue or database when needed, retaining a unique event-ID constraint. The downstream mutation must also be repeat-safe because a worker can fail after applying the effect and before recording completion.
3. Poll the public background-job contract
The corrected file example flushes staging files before atomic publication and validates duplicate bytes. Worker passes share a directory lock, and failed handlers move to a failed-event marker after three attempts so later events continue. Monitor failed/. After a crash, stop all workers before removing a stale worker.lock. To replay a failed event, fix its cause and remove only its failed and attempts markers while workers are stopped. Keep the accepted payload. Use a transactional broker or database when production power-loss durability is required; the local hard-link file store is a reference implementation.
Polling deadlines cover requests and waits. Cancellation interrupts both, and a Retry-After exceeding the remaining budget fails without issuing an early retry.
The current public pollable job is an AI rebuild. Queue it once through POST /v1/apps/{saasAppId}/ai/rebuilds, retain jobId, then read GET /v1/apps/{saasAppId}/ai/rebuilds/{jobId}. Queueing requires ai.write; status requires ai.read; the caller also needs current tenant account-owner authority.
const result = await pollAiRebuild({
saasAppId: process.env.BWHQ_SAAS_APP_ID,
credential: process.env.BWHQ_USER_TOKEN,
jobId: receipt.jobId,
maximumWaitMs: 10 * 60 * 1000,
});
The example stops on completed, failed, or cancelled, sleeps between other states, honors the required whole-second Retry-After header after 429, supports cancellation, and enforces a maximum wait. It never automatically repeats the queue POST after an uncertain response.
4. Verify without credentials
node --test examples.test.mjs
The offline tests send signed raw bytes to a loopback receiver, reject a bad signature, prove duplicate acknowledgement and one-time processing, then exercise queued, rate-limited, completed, and failed polling responses.
An asynchronous installed-service invocation can return HTTP 202, but the current public Developer API does not publish an invocation status-read route. Use only the AI rebuild polling route shown here until a service-invocation status operation appears in OpenAPI.
Capability review: 2026-09-14. For exact current technical availability, use the generated API Map and first-class module inventory.