Agent skill
INGENIOUS.BUILD Webhooks Skill
Receive and authenticate INGENIOUS.BUILD Custom Accounting Integration webhooks. Use when setting up an INGENIOUS.BUILD webhook endpoint, checking the auth header you add to each webhook URL, debugging 401s, or handling invoice_exported_to_accounting, contract_exported_to_accounting and budget_exported_to_accounting events.
Install this skill
npx skills add hookdeck/webhook-skills --skill ingenious-build-webhooks
INGENIOUS.BUILD (construction project management) pushes approved financial records to your accounting middleware through the Custom Accounting Integration. You configure one webhook URL per object type (Budgets, Budget Changes, Contracts, Contract Changes, Invoices). A webhook fires only when a user clicks Sync to Accounting on an approved record.
Requests are not signed. You authenticate them with a secret header that you add to each webhook URL in the integration settings, and your endpoint compares that header with the value it has stored.
Setup note (same as Hookdeck's INGENIOUS.BUILD source): "Add an auth header, such as Authorization: Bearer <token>, to each webhook URL on the Custom Accounting Integration."
When to Use This Skill
- How do I receive INGENIOUS.BUILD webhooks?
- How do I authenticate INGENIOUS.BUILD Custom Accounting Integration requests?
- Does INGENIOUS.BUILD sign webhook payloads? (No: use the auth header you configure)
- How do I handle
invoice_orexported_ to_ accounting contract_?exported_ to_ accounting - Why does my INGENIOUS.BUILD webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyIngeniousBuildRequest(
headers,
name = process.env.INGENIOUS_BUILD_WEBHOOK_HEADER || 'Authorization',
secret = process.env.INGENIOUS_BUILD_WEBHOOK_SECRET
) {
if (!secret) return false; // never accept when unconfigured
const received = headers[name.toLowerCase()]; // Node lowercases header names
return Boolean(received) && safeEqual(received, secret); // WHOLE value, no 'Bearer ' parsing
}
- The header name is whatever you typed in the integration (
Authorizationby default here). Look it up case-insensitively and compare the whole value toINGENIOUS_, including anyBUILD_ WEBHOOK_ SECRET Bearerprefix you typed. Do not strip or parse the prefix. - Return
401for a missing header or a mismatch,400for a body that isn't a JSON object,200once handled. - Read the body raw (
express.raw({ type: '*/*' })) so any content type works, and parse it only after the header check. - A static shared credential authenticates the sender, not the body, and gives no replay protection (there is no signature and no timestamp). Use HTTPS only and rotate the credential if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
Every request is a POST with a JSON body { name, subject, data }. Dispatch on name.
name | subject | Sent When |
|---|---|---|
budget_ | budget | Sync to Accounting is clicked on a Budget |
budget_ | budget_change | ... on a Budget Change |
contract_ | contract | ... on a Contract |
contract_ | contract_change | ... on a Contract Change |
invoice_ | invoice | ... on an Invoice |
Environment Variables
INGENIOUS_BUILD_WEBHOOK_HEADER=Authorization # header name you added to each webhook URL
INGENIOUS_BUILD_WEBHOOK_SECRET="Bearer your-token" # exact, whole header value
Local Development
INGENIOUS.BUILD can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 ingenious-build --path /webhooks/ingenious-build
Use the printed URL as a webhook URL in the Custom Accounting Integration.
Resources
- references/overview.md - What the webhooks are, events and payload shape
- references/setup.md - Enable the integration, add URLs and the auth header
- references/verification.md - Header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests