Agent skill
Quickbase Webhooks Skill
Receive and authenticate Quickbase webhooks. Use when setting up a Quickbase table webhook endpoint, checking the secret header you add to the webhook's Message header list, debugging 401s, or handling record added, modified and deleted requests that use any HTTP method.
Install this skill
npx skills add hookdeck/webhook-skills --skill quickbase-webhooks
Quickbase table webhooks fire when a record is added, modified or deleted. You write the whole request: endpoint URL, HTTP method (POST, GET, PUT, PATCH or DELETE; POST is the default), message format (XML, JSON or RAW), up to 10 message headers and the message body. Webhooks are not signed. The only way to authenticate the sender is a secret header you add yourself in the webhook's Message header list, which your endpoint then checks.
When to Use This Skill
- How do I receive Quickbase webhooks?
- How do I authenticate a Quickbase webhook if it has no signature?
- How do I handle record added, modified and deleted webhooks?
- Why does my Quickbase webhook endpoint return 401?
- Why does my endpoint get a PUT, PATCH, GET or DELETE instead of a POST?
Verification (core)
The header name is your choice, so the examples read it from QUICKBASE_WEBHOOK_HEADER (default Authorization) and the expected value from QUICKBASE_WEBHOOK_SECRET: the exact, whole header value, including any Bearer prefix you typed. Look the header up case-insensitively, compare the whole value (never strip or parse Bearer ), and compare in constant time.
const crypto = require('crypto');
function verifyQuickbaseRequest(headers, secret = process.env.QUICKBASE_WEBHOOK_SECRET) {
if (!secret) return false; // never accept when unconfigured
const name = (process.env.QUICKBASE_WEBHOOK_HEADER || 'Authorization').toLowerCase();
const received = headers[name]; // Node lowercases header names
if (typeof received !== 'string') return false;
const a = Buffer.from(received, 'latin1'); // headers arrive as latin1 text
const b = Buffer.from(secret, 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
import hmac
def verify_quickbase_request(headers, secret, header_name="Authorization") -> bool:
received = headers.get(header_name) # Starlette headers are case-insensitive
if not secret or received is None:
return False
# compare_digest needs bytes: str raises TypeError on non-ASCII
return hmac.compare_digest(received.encode("latin-1"), secret.encode("utf-8"))
- Missing header, wrong value or unset secret:
401. - A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, and rotate the credential if it leaks.
- Webhooks can be viewed and copied by every admin of the Quickbase app, so the secret is visible to them. Use a value dedicated to this webhook.
- Read the body raw (
express.raw({ type: '*/*' })) so any content type works, and parse it only after the credential check. - Accept
POST,PUT,PATCH,GETandDELETEon the route, and handle an empty body.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Hookdeck Source
Hookdeck's Quickbase source (type QUICKBASE) performs the same check: header name plus the exact whole value. It pre-selects POST without managing the method, so if your webhook uses PUT, PATCH, GET or DELETE, also allow that method on the source.
Event Types
Quickbase adds no event field to the request, and there are no event name strings. The three triggers you can select under When are a record being added, modified or deleted. What the request contains is the message body template you wrote. The examples dispatch on an illustrative event field that you add yourself in the template (one webhook per trigger, with the value typed as a literal). It is not a Quickbase field.
| Trigger (When) | Illustrative event value in your template |
|---|---|
| A record is added | record_added |
| A record is modified | record_modified |
| A record is deleted | record_deleted |
Illustrative JSON template (field references chosen from the dropdown go where the values are):
{ "event": "record_modified", "record_id": 42, "table": "Projects" }
Environment Variables
QUICKBASE_WEBHOOK_HEADER=Authorization # name of the header you added (default: Authorization)
QUICKBASE_WEBHOOK_SECRET=Bearer your-secret # exact, whole header value
Local Development
Quickbase can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 quickbase --path /webhooks/quickbase
Use the printed URL as the Endpoint URL in the Quickbase webhook (it must be https://).
Resources
- references/overview.md - What Quickbase webhooks are, triggers, message format
- references/setup.md - Configure the webhook and secret header
- references/verification.md - Header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests