Skip to content

refactor(ashby): align tools, block, and triggers with Ashby API#4288

Merged
waleedlatif1 merged 9 commits intostagingfrom
waleedlatif1/ashby-integration-audit
Apr 24, 2026
Merged

refactor(ashby): align tools, block, and triggers with Ashby API#4288
waleedlatif1 merged 9 commits intostagingfrom
waleedlatif1/ashby-integration-audit

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

@waleedlatif1 waleedlatif1 commented Apr 24, 2026

Summary

  • Audit-driven refactor of Ashby integration against Ashby's API docs. All tools, block, provider handler, and triggers now destructure rich fields per the documented response shapes.
  • Centralizes output shapes in a shared tools/ashby/utils.ts via mappers + *_OUTPUTS constants, eliminating drift between tools and block outputs.
  • Aligns webhook provider handler with trigger IDs via a single ASHBY_TRIGGER_ACTION_MAP source of truth, and filters Ashby's ping events in matchEvent.

Notable changes

  • tools/ashby/utils.ts (new) — shared mappers (mapCandidate, mapJob, mapApplication, mapOffer, mapOpenings, etc.) and output constants.
  • tools/ashby/* — 28 tool files refactored to use shared mappers; ID fields .trim()-ed; body keys aligned with Ashby API (id for get_candidate/get_job, single-string status for list_applications).
  • blocks/blocks/ashby.ts — advanced-mode switches added (syncToken, includeArchived, expandApplicationFormDefinition, expandSurveyFormDefinitions); stale top-level outputs removed.
  • lib/webhooks/providers/ashby.tsmatchEvent added to filter ping events; uses shared ASHBY_TRIGGER_ACTION_MAP.
  • triggers/ashby/utils.tsASHBY_TRIGGER_ACTION_MAP + isAshbyEventMatch helper; candidateHire outputs expanded with accepted offer details.

Breaking (intentional, API-accurate)

  • add_candidate_tag / remove_candidate_tag return full candidate (was { success: true }).
  • create_application / change_application_stage return full application (was { applicationId, stageId? }).
  • create_note returns full note (was { noteId }).
  • get_offer / list_offers nest startDate, salary, openingId, createdAt under latestVersion per Ashby's response shape.

Test plan

  • bun run type-check passes
  • Verify Ashby webhook delivery end-to-end (signature verification + ping filter)
  • Run each tool operation in a workflow (candidate/job/application/offer read + write paths)
  • Confirm block output autocomplete reflects new shapes

Audit-driven refactor to destructure rich fields per Ashby's API docs,
centralize output shapes via shared mappers in tools/ashby/utils.ts,
and align webhook provider handler with trigger IDs via a shared
action map. Removes stale block outputs left over from prior flat
response shapes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Apr 24, 2026 8:21pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 24, 2026

PR Summary

Medium Risk
Touches many Ashby tools’ request/response contracts and block parameters (including intentional breaking output shape changes), so downstream workflows/autocomplete could break if assumptions were relying on prior minimal outputs. Webhook auth/matching logic changes could also affect trigger firing if misconfigured secrets or action mapping diverge.

Overview
Refactors the Ashby integration to match documented API request/response shapes, centralizing mapping and output schemas in tools/ashby/utils.ts and updating many tools to return rich, consistent objects (e.g., candidate/application/offer) instead of minimal IDs/flags.

Updates the Ashby block UI and parameter mapping to remove the candidateId filter for list_applications, add new advanced options (includeArchived, syncToken, and job posting expand flags), and keep output autocomplete aligned with the new shared schemas.

Improves webhook handling by implementing explicit Ashby signature verification with clearer 401 errors, adding matchEvent filtering (ignore ping and skip mismatched actions), and using a single ASHBY_TRIGGER_ACTION_MAP source of truth; docs/triggers are updated accordingly (including candidateHire trigger output including accepted offer details).

Reviewed by Cursor Bugbot for commit 611a1f7. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR is a comprehensive audit-driven refactoring of the Ashby integration that centralizes output shapes and mappers into a new shared tools/ashby/utils.ts, aligns 28 tool files with Ashby's documented API request/response shapes, and adds a matchEvent filter in the webhook provider to reject ping events.

All three previously-flagged concerns from the review thread (stale noteId descriptor, ping-event bypass when triggerId is absent, and the missing data key in formatInput) have been correctly addressed in commit 611a1f7.

Confidence Score: 5/5

Safe to merge — all previously flagged issues are resolved, no new P0/P1 defects found.

All three review-thread concerns (stale noteId descriptor, ping-event bypass, missing data key) were fixed in the final commit. The refactoring is well-scoped: the centralized utils.ts eliminates output drift, the trigger action map is a single source of truth, and subblock migrations correctly tombstone the removed filterCandidateId field. Intentional breaking changes (offer latestVersion nesting, tag/application/note output enrichment) are documented and accepted.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/tools/ashby/utils.ts New shared module with typed mappers (mapCandidate, mapJob, mapApplication, mapOffer, etc.) and *_OUTPUTS constants — well-structured, eliminates drift between tools and block outputs.
apps/sim/lib/webhooks/providers/ashby.ts matchEvent now correctly rejects ping events unconditionally before the triggerId fallback; data key restored in formatInput so both input.data.* and flat input.* paths work for trigger workflows.
apps/sim/triggers/ashby/utils.ts ASHBY_TRIGGER_ACTION_MAP provides a single source of truth for trigger-to-action mapping; isAshbyEventMatch helper is clean and correct; candidateHire outputs expanded with offer details.
apps/sim/blocks/blocks/ashby.ts Advanced-mode switches added for syncToken, includeArchived, expandApplicationFormDefinition, expandSurveyFormDefinitions; stale top-level output descriptors replaced with API-accurate shapes.
apps/sim/lib/workflows/migrations/subblock-migrations.ts filterCandidateId correctly migrated to _removed_filterCandidateId to prevent orphaned block params in existing workflows.
apps/sim/tools/ashby/list_applications.ts candidateId filter removed; status now sent as single string per Ashby API; createdAfter NaN-guarded before sending to API; mapped via shared APPLICATION_OUTPUTS.
apps/sim/tools/ashby/create_candidate.ts Email made optional (required: false) — aligns with Ashby API where only name is required; output now uses shared mapCandidate/CANDIDATE_OUTPUTS.
apps/sim/tools/ashby/get_offer.ts Now uses shared mapOffer/OFFER_OUTPUTS; startDate/salary/openingId/createdAt correctly nested under latestVersion per Ashby response shape.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Inbound Ashby Webhook] --> B[verifyAuth\nHMAC sha256 validation]
    B -->|Invalid signature| C[401 Unauthorized]
    B -->|Valid| D[matchEvent]
    D -->|action == 'ping'| E[return false\nSkip execution]
    D -->|no triggerId| F[return true\nAllow all]
    D -->|triggerId present| G[isAshbyEventMatch\nASHBY_TRIGGER_ACTION_MAP lookup]
    G -->|mismatch| H[return false\nSkip execution]
    G -->|match| I[formatInput\nspread data + keep data key]
    I --> J[Workflow Execution\ninput.application.* ✓\ninput.data.application.* ✓]

    subgraph Tools
      K[get_candidate / list_candidates / search_candidates\ncreate_candidate / update_candidate\nadd_candidate_tag / remove_candidate_tag] --> L[mapCandidate + CANDIDATE_OUTPUTS]
      M[get_application / list_applications\ncreate_application / change_application_stage] --> N[mapApplication + APPLICATION_OUTPUTS]
      O[get_offer / list_offers] --> P[mapOffer + OFFER_OUTPUTS]
      Q[get_job / list_jobs] --> R[mapJob + JOB_OUTPUTS]
    end

    subgraph SharedUtils["tools/ashby/utils.ts"]
      L
      N
      P
      R
    end
Loading

Reviews (3): Last reviewed commit: "fix(ashby): preserve input.data path in ..." | Re-trigger Greptile

Comment thread apps/sim/blocks/blocks/ashby.ts Outdated
Comment thread apps/sim/lib/webhooks/providers/ashby.ts
Comment thread apps/sim/lib/webhooks/providers/ashby.ts
Comment thread apps/sim/blocks/blocks/ashby.ts
- Remove stale `noteId` output descriptor from block (create_note
  now returns `id` at the top level via the shared note mapper).
- Explicitly reject `ping` events in the webhook matchEvent before
  falling back to the generic triggerId check, so webhook records
  missing triggerId cannot execute workflows on Ashby ping probes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Optional ID params in create_application, change_application_stage,
and update_candidate were passed through to the request body without
.trim(), unlike their required ID siblings. Normalize to prevent
copy-paste whitespace errors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add SUBBLOCK_ID_MIGRATIONS entry so deployed workflows that previously
used the `filterCandidateId` subBlock on `list_applications` don't break
after the field was removed (Ashby's application.list doesn't filter by
candidateId). Also regenerate docs to sync noteId removal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/webhooks/providers/ashby.ts
Comment thread apps/sim/lib/webhooks/providers/ashby.ts
- create_candidate: email is optional per Ashby docs (only name is
  required); tool, types, and block all made non-required.
- list_applications: guard NaN when createdAfter can't be parsed so we
  don't send a bad value to Ashby's API.
- webhook provider: replace createHmacVerifier with explicit
  fail-closed verifyAuth that 401s when secretToken, signature header,
  or signature match is missing (was previously fail-open on missing
  secret).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Restore the explicit `data` key alongside the spread so deployed
workflows that reference `input.data.application.*`, `input.data.candidate.*`,
etc. keep working. The spread alone dropped those paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 611a1f7. Configure here.

Keep formatInput aligned with the advertised trigger outputs schema
(flat top-level entities) and drop the legacy input.data.* compat path.
Every field declared in each trigger's outputs is now populated 1:1 by
the data spread plus the explicit action key — no undeclared keys.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add .trim() on sourceId (create_candidate), jobId (list_applications),
applicationId and interviewStageId (list_interviews) to match the
trim-on-IDs pattern used across the rest of the Ashby tools and guard
against copy-paste whitespace.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@waleedlatif1 waleedlatif1 merged commit f330fe2 into staging Apr 24, 2026
13 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/ashby-integration-audit branch April 24, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant