App-level webhook toolkits (HubSpot is the first) need provisioning credentials at trigger-create time so mercury's setup() can register the provider-side subscription. These are stored as authz_fields setup_fields on the shared webhook_endpoint (encrypted in encryptedData). Until now the auth dict passed to setupWebhookTrigger was OAuth-derived only, so those fields never reached setup() — setup() saw an empty config and 500'd.
This PR decrypts the webhook_endpoint setup_fields and merges the spec-declared authz_fields into auth right before setupWebhookTrigger, mirroring the existing authz-resolution path (fetch_authorized_entities) that already injects authz_fields into auth for per-event entity resolution.
webhook_endpoint has encryptedData AND the trigger spec declares setup_fields.authz_fields. Toolkits without webhook-endpoint setup_fields are untouched.createNewTrigger in trigger_instances/[slug]/upsert.ts, just before TriggerInstances.setupWebhookTrigger.authz_fields is already the slot apollo merges into the auth dict (Slack's app_token uses it for authz resolution). HubSpot overloads it to carry app_id + developer_api_key (the v3 subscription-management hapikey). Mercury reads them as auth["app_id"] / auth["developer_api_key"] with a config fallback for legacy trigger_instances — so this apollo change requires zero mercury-side change to the credential-resolution code.
These surfaced while validating the HubSpot catalog end-to-end through the full pipeline (ingress → thermos → mercury → broadcast → receiver). Only the first is fixed here; the rest are tracked for separate PRs.
authz_fields into mercury setup(). This PR.src/lib/triggers/webhook_ingress/signature.ts checkReplayProtection computes now = Math.floor(Date.now()/1000) (seconds) and compares against the raw provider timestamp. HubSpot's x-hubspot-request-timestamp is in milliseconds, so skew is always ~now*1000, and every HubSpot event is wrongly rejected replay_timestamp_expired. Worked around by omitting replay_protection from the HubSpot spec (mercury side). Proper fix: normalize the timestamp to seconds when the spec declares a millisecond unit. Not fixed here to keep this PR scoped.contact.creation + contact.propertyChange together). Worth a durable retry/queue.validateTriggerConfig strips undeclared config keys even with additionalProperties: true (zod default strips). This is why app-level provisioning creds must flow via authz_fields (decrypted server-side) rather than through trigger_config. Context, not necessarily a bug — but non-obvious and worth a code comment near the validator.c.req.url behind a local cloudflared tunnel is the http://localhost origin, not the public https:// URL the provider signed, so HMAC verification fails locally. Production behind Vercel gets the public URL in c.req.url, so no change is needed. Mentioned only so the next person testing locally doesn't chase a phantom signature mismatch.setup() now receives app_id + developer_api_key, registers the v3 subscription, returns 200 (previously 500 on empty config).setup_fields.authz_fields are unaffected (guard short-circuits).decryptSetupData is the correct helper (same envelope as webhook_endpoints.encryptedData).🤖 Generated with Claude Code
Based on git blame analysis of 1 file(s):
| Contributor | Contribution | Files |
|---|---|---|
| Jayesh Sharma | 57% | 1 |
| Anshu Garg |
| 10% |
| 1 |
| lingalarahul7 | 7% | 1 |
| Zen Agent | 7% | 1 |
| Himanshu Dixit | 5% | 1 |
Recommend Anshu Garg — recently edited this file (today) and will know the latest changes. Also include Jayesh Sharma — the largest contributor and edited it about a month ago, so has strong context on design and intent.
🤖 Based on git blame with recency weighting (recent edits count more).
:x: Patch coverage is 3.22581% with 30 lines in your changes missing coverage. Please review.
| Files with missing lines | Patch % | Lines |
|---|---|---|
| ...rc/pages/api/v3/trigger_instances/[slug]/upsert.ts | 3.22% | 30 Missing :warning: |
| Flag | Coverage Δ | |
|---|---|---|
| e2e-tests | 6.03% <0.00%> (-0.01%) | :arrow_down: |
| self-hosted-tests | 5.58% <0.00%> (-0.01%) | :arrow_down: |
| unit-tests | 58.94% <5.26%> (+<0.01%) | :arrow_up: |
Flags with carried forward coverage won't be shown. Click here to find out more.
| Files with missing lines | Coverage Δ | |
|---|---|---|
| ...rc/pages/api/v3/trigger_instances/[slug]/upsert.ts | 43.63% <3.22%> (-1.65%) | :arrow_down: |
... and 6 files with indirect coverage changes