feat(ashby): add webhook triggers with automatic lifecycle management#3548
feat(ashby): add webhook triggers with automatic lifecycle management#3548waleedlatif1 merged 7 commits intostagingfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Adds automatic Ashby webhook lifecycle management in Updates docs to mention Ashby trigger support and adds richer intro content for Written by Cursor Bugbot for commit cfc8e71. Configure here. |
Greptile SummaryThis PR adds six Ashby webhook triggers (Application Submitted, Candidate Stage Change, Candidate Hired, Candidate Deleted, Job Created, Offer Created) with fully automatic lifecycle management — Sim creates the webhook in Ashby when a trigger is saved and deletes it when removed. The implementation follows existing provider patterns, registers triggers in Key changes:
Remaining concern:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Sim UI
participant Sim Server
participant Ashby API
participant Workflow Engine
User->>Sim UI: Selects Ashby trigger type & enters API key
Sim UI->>Sim Server: Save trigger configuration
Sim Server->>Ashby API: POST /webhook.create (requestUrl, webhookType, Basic Auth)
Ashby API-->>Sim Server: { results: { id: webhookId } }
Sim Server->>Sim Server: Store externalId in providerConfig
Sim Server-->>Sim UI: Trigger saved successfully
Note over Ashby API, Sim Server: On recruiting event (e.g. applicationSubmit)
Ashby API->>Sim Server: POST /api/webhooks/trigger/{uuid-path}
Sim Server->>Sim Server: formatWebhookInput — spread body.data to top level
Sim Server->>Workflow Engine: Execute workflow with formatted payload
User->>Sim UI: Remove trigger
Sim UI->>Sim Server: Delete trigger
Sim Server->>Ashby API: POST /webhook.delete { webhookId: externalId }
Ashby API-->>Sim Server: 200 OK (or 404 if already removed)
Sim Server-->>Sim UI: Trigger removed
Last reviewed commit: cfc8e71 |
- Restore mode: 'advanced' on updateName sub-block - Move action after spread in formatWebhookInput to prevent override - Remove generic webhook trigger (Ashby requires webhookType)
|
@cursor review |
|
@greptile |
|
@cursor review |
|
@greptile |
…json catch - Add paramVisibility: 'user-only' to apiKey extra field - Remove stageType from candidateStageChange/candidateHire outputs (TriggerOutput type conflict with 'type' field) - Add .catch() fallback to .json() parse in createAshbyWebhookSubscription - Fix candidateStageChange outputs to match actual Ashby application payload structure
|
All items from the latest review addressed in c14fabb:
|
|
@cursor review |
|
@greptile |
…nches - Add candidate, currentInterviewStage, job to applicationSubmit outputs - Split delete webhook log into ok/404/error branches for accurate logging
|
@cursor review |
|
@greptile |
- Cancel unconsumed response body in ok/404 delete branches to free connections - Update decidedAt description to note it's typically null at offer creation
|
@cursor review |
|
@greptile |
- Remove pre-throw warn/error logs; catch block is single logging point - Remove hiringTeam from candidateHire JSDoc (TriggerOutput doesn't support arrays)
|
@cursor review |
|
@greptile |
| const requestBody: Record<string, unknown> = { | ||
| requestUrl: notificationUrl, | ||
| webhookType, | ||
| } | ||
|
|
||
| const ashbyResponse = await fetch('https://api.ashbyhq.com/webhook.create', { | ||
| method: 'POST', | ||
| headers: { | ||
| Authorization: `Basic ${authString}`, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(requestBody), |
There was a problem hiding this comment.
No webhook payload origin verification
webhook.create is called with only requestUrl and webhookType. Ashby's API also accepts a secretToken parameter: when set, Ashby signs every delivery with HMAC-SHA256 and includes the signature in the X-Ashby-Signature header. Without it, any actor who discovers the webhook URL (which is a UUID path but still guessable or leak-able) can POST an arbitrary payload and trigger workflows.
Other providers in this codebase — for example, Calendly — generate and persist a webhookSecret so incoming requests can be verified against the signature header before the payload is acted upon. Consider adopting the same approach here: generate a random token at creation time, include it in requestBody, store it alongside externalId in providerConfig, and verify the X-Ashby-Signature header in formatWebhookInput or the processor before accepting the payload.
Summary
Type of Change
Testing
Tested manually
Checklist