Agent skill
Procore Webhooks Skill
Receive and authenticate Procore construction webhooks. Use when setting up a Procore company or project webhook (hook) endpoint, checking the Authorization Header credential, debugging 401s, or handling resource create, update and delete events such as RFIs or submittals.
Install this skill
npx skills add hookdeck/webhook-skills --skill procore-webhooks
Procore (construction management platform) lets company and project admins create hooks (Company Admin > Webhooks) that POST a notification to your endpoint whenever a Procore API resource is created, updated or deleted. Payloads are not signed. The only credential is the optional Authorization Header field, which Procore places verbatim in the Authorization header of each POST.
When to Use This Skill
- How do I receive Procore webhooks?
- How do I verify the Procore
Authorizationheader on a webhook? - Does Procore sign webhook payloads? (No: shared header credential only)
- Why does my Procore webhook endpoint return 401?
- How do I handle Procore
create,updateanddeleteevents for a resource like RFIs?
Verification (core)
Set the Authorization Header on the hook, store the exact same string as PROCORE_AUTHORIZATION, and compare it to the whole Authorization header. Paste the Authorization Header value exactly as set in Procore, including any "Bearer " or "Basic " prefix. Never strip, parse or base64-decode it: the docs' example, Basic c3a24b8208ac5199d083d54a1234e94b8864, is a token, not base64 user:pass.
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 verifyProcoreRequest(headers, expected = process.env.PROCORE_AUTHORIZATION) {
if (!expected) return false; // never accept when unconfigured
const received = headers['authorization']; // full value, e.g. "Basic c3a2..."
return Boolean(received) && safeEqual(received, expected);
}
Return 401 when the header is missing or does not match, 400 for a body that isn't a JSON object, and 200 once handled. Read the body raw (express.raw({ type: '*/*' })) so any content type works, and parse it only after the credential check. Python and Next.js equivalents are in references/verification.md.
What this does and does not give you: a static shared credential authenticates the sender, not the body, and offers no replay protection (no signature, no timestamp). Use HTTPS, and rotate the credential in Procore and in your environment if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
Each event has a resource name (the Procore API resource, e.g. RFIs) and an event type:
| Event type | Triggered when |
|---|---|
create | A resource record is created |
update | A resource record is updated |
delete | A resource record is deleted |
The v2 payload is described as carrying fields like id, ulid, timestamp, user_id, company_id, project_id, api_version, event_type, resource_name and resource_id. The Procore support article confirms only the resource name, resource ID, event type and timestamp; the developer docs are not readable here, so treat the payload in the examples as illustrative.
Environment Variables
PROCORE_AUTHORIZATION=Basic c3a24b8208ac5199d083d54a1234e94b8864 # full header value
Local Development
npx hookdeck-cli listen 3000 procore --path /webhooks/procore
No account required. Hookdeck's Procore source shows this note: Paste the Authorization Header value exactly as set in Procore, including any "Bearer " or "Basic " prefix. Give the printed URL to Procore as the hook's Endpoint URL.
Resources
- references/overview.md - Hooks, resources, event types, payload fields
- references/setup.md - Creating a hook in Company Admin and setting the Authorization Header
- references/verification.md - Header check in Node, Python and Next.js, gotchas
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests