Skip to content

Commit 5d52ee5

Browse files
committed
fix(ashby): correct API contracts and webhook handling
1 parent d50b17c commit 5d52ee5

17 files changed

Lines changed: 369 additions & 100 deletions

File tree

apps/docs/content/docs/integrations/ashby.mdx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ Moves an application to a different interview stage. Requires an archive reason
249249
| `applicationId` | string | Yes | The UUID of the application to update the stage of |
250250
| `interviewStageId` | string | Yes | The UUID of the interview stage to move the application to |
251251
| `archiveReasonId` | string | No | Archive reason UUID. Required when moving to an Archived stage, ignored otherwise |
252-
| `archiveEmail` | boolean | No | Send Ashby archive email automation when archiving |
252+
| `archiveEmail` | json | No | Archive email configuration with communicationTemplateId and optional sendAt ISO 8601 timestamp. Pass null or omit to send no archive email. |
253253

254254
#### Output
255255

@@ -306,7 +306,7 @@ Creates a new application for a candidate on a job. Optionally specify interview
306306
| `candidateId` | string | Yes | The UUID of the candidate to consider for the job |
307307
| `jobId` | string | Yes | The UUID of the job to consider the candidate for |
308308
| `interviewPlanId` | string | No | UUID of the interview plan to use \(defaults to the job default plan\) |
309-
| `interviewStageId` | string | No | UUID of the interview stage to place the application in \(defaults to first Lead stage\) |
309+
| `interviewStageId` | string | No | UUID of the interview stage to place the application in, or FirstPreInterviewScreen \(defaults to the first Lead stage\) |
310310
| `sourceId` | string | No | UUID of the source to set on the application |
311311
| `creditedToUserId` | string | No | UUID of the user the application is credited to |
312312
| `createdAt` | string | No | ISO 8601 timestamp to set as the application creation date \(defaults to now\) |
@@ -882,7 +882,7 @@ Lists all applications in an Ashby organization with pagination and optional fil
882882
| `apiKey` | string | Yes | Ashby API Key |
883883
| `cursor` | string | No | Opaque pagination cursor from a previous response nextCursor value |
884884
| `perPage` | number | No | Number of results per page \(default 100\) |
885-
| `status` | json | No | One application status or an array of statuses to include: Active, Hired, Archived, or Lead |
885+
| `status` | string | No | Application status to include: Active, Hired, Archived, or Lead |
886886
| `jobId` | string | No | Filter applications by a specific job UUID |
887887
| `createdAfter` | string | No | Filter to applications created after this ISO 8601 timestamp \(e.g. 2024-01-01T00:00:00Z\) |
888888
| `createdBefore` | string | No | Filter to applications created before this ISO 8601 timestamp |
@@ -1128,7 +1128,6 @@ Lists the ordered stages in an Ashby interview plan.
11281128
|`interviewPlanId` | string | Parent plan UUID |
11291129
|`orderInInterviewPlan` | number | Zero-based plan order |
11301130
|`interviewStageGroupId` | string | Stage group UUID |
1131-
| `moreDataAvailable` | boolean | Whether more data is available |
11321131

11331132
### Ashby List Job Postings
11341133

@@ -1617,21 +1616,21 @@ Updates an existing candidate record in Ashby. Only provided fields are changed.
16171616
| `apiKey` | string | Yes | Ashby API Key |
16181617
| `onBehalfOfUserId` | string | No | Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls |
16191618
| `candidateId` | string | Yes | The UUID of the candidate to update |
1620-
| `name` | string | No | Updated full name |
1621-
| `email` | string | No | Updated primary email address |
1622-
| `phoneNumber` | string | No | Updated primary phone number |
1623-
| `linkedInUrl` | string | No | LinkedIn profile URL |
1624-
| `githubUrl` | string | No | GitHub profile URL |
1625-
| `websiteUrl` | string | No | Personal website URL |
1626-
| `alternateEmail` | string | No | An additional email address to add to the candidate |
1619+
| `name` | string | No | Updated full name, or null |
1620+
| `email` | string | No | Updated primary email address, or null |
1621+
| `phoneNumber` | string | No | Updated primary phone number, or null |
1622+
| `linkedInUrl` | string | No | LinkedIn profile URL, or null |
1623+
| `githubUrl` | string | No | GitHub profile URL, or null |
1624+
| `websiteUrl` | string | No | Personal website URL, or null |
1625+
| `alternateEmail` | string | No | An additional email address to add to the candidate, or null |
16271626
| `sourceId` | string | No | UUID of the source to attribute the candidate to |
16281627
| `creditedToUserId` | string | No | UUID of the Ashby user to credit with sourcing this candidate |
16291628
| `clearSource` | boolean | No | Explicitly clear the candidate source; mutually exclusive with sourceId |
16301629
| `clearCreditedToUser` | boolean | No | Explicitly clear the credited Ashby user; mutually exclusive with creditedToUserId |
1631-
| `location` | json | No | Candidate location object with optional city, region, and country; pass null fields to clear parts |
1632-
| `createdAt` | string | No | Backdated creation timestamp in ISO 8601. Only updatable if originally backdated. |
1633-
| `sendNotifications` | boolean | No | Whether to send a notification when the source is updated \(default true\) |
1634-
| `socialLinks` | json | No | Array of social link objects to set on the candidate, e.g. \[\{"type":"LinkedIn","url":"https://..."\}\]. Replaces existing social links. |
1630+
| `location` | json | No | Candidate location object with optional city, region, and country; the object and its fields accept null |
1631+
| `createdAt` | string | No | Backdated creation timestamp in ISO 8601, or null. Only updatable if originally backdated. |
1632+
| `sendNotifications` | boolean | No | Whether to send a notification when the source is updated \(default true\), or null |
1633+
| `socialLinks` | json | No | Array of social link objects to set on the candidate, e.g. \[\{"type":"LinkedIn","url":"https://..."\}\]. Replaces existing links; pass \[\] to clear them. Null is also accepted. Mutually exclusive with linkedInUrl, githubUrl, and websiteUrl. |
16351634

16361635
#### Output
16371636

@@ -1866,6 +1865,7 @@ Trigger workflow when a new application is submitted
18661865
| Parameter | Type | Description |
18671866
| --------- | ---- | ----------- |
18681867
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
1868+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
18691869
| `application` | object | application output from the tool |
18701870
|`id` | string | Application UUID |
18711871
|`createdAt` | string | Application creation timestamp \(ISO 8601\) |
@@ -1900,6 +1900,7 @@ Trigger workflow when an application is updated
19001900
| Parameter | Type | Description |
19011901
| --------- | ---- | ----------- |
19021902
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
1903+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
19031904
| `application` | object | application output from the tool |
19041905
|`id` | string | Application UUID |
19051906
|`createdAt` | string | Application creation timestamp \(ISO 8601\) |
@@ -1934,6 +1935,7 @@ Trigger workflow when a candidate is deleted
19341935
| Parameter | Type | Description |
19351936
| --------- | ---- | ----------- |
19361937
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
1938+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
19371939
| `candidate` | object | candidate output from the tool |
19381940
|`id` | string | Deleted candidate UUID |
19391941

@@ -1955,6 +1957,7 @@ Trigger workflow when a candidate is hired
19551957
| Parameter | Type | Description |
19561958
| --------- | ---- | ----------- |
19571959
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
1960+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
19581961
| `application` | object | application output from the tool |
19591962
|`id` | string | Application UUID |
19601963
|`createdAt` | string | Application creation timestamp \(ISO 8601\) |
@@ -1997,6 +2000,7 @@ Trigger workflow when two candidate records are merged
19972000
| Parameter | Type | Description |
19982001
| --------- | ---- | ----------- |
19992002
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2003+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
20002004
| `deletedCandidate` | object | deletedCandidate output from the tool |
20012005
|`id` | string | Deleted candidate UUID |
20022006
| `mergedCandidate` | object | mergedCandidate output from the tool |
@@ -2020,6 +2024,7 @@ Trigger workflow when a candidate changes interview stages
20202024
| Parameter | Type | Description |
20212025
| --------- | ---- | ----------- |
20222026
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2027+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
20232028
| `application` | object | application output from the tool |
20242029
|`id` | string | Application UUID |
20252030
|`createdAt` | string | Application creation timestamp \(ISO 8601\) |
@@ -2054,6 +2059,7 @@ Trigger workflow when an interview schedule is created
20542059
| Parameter | Type | Description |
20552060
| --------- | ---- | ----------- |
20562061
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2062+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
20572063
| `interviewSchedule` | object | interviewSchedule output from the tool |
20582064
|`id` | string | Interview schedule UUID |
20592065
|`status` | string | Interview schedule status |
@@ -2082,6 +2088,7 @@ Trigger workflow when an interview schedule is updated
20822088
| Parameter | Type | Description |
20832089
| --------- | ---- | ----------- |
20842090
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2091+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
20852092
| `interviewSchedule` | object | interviewSchedule output from the tool |
20862093
|`id` | string | Interview schedule UUID |
20872094
|`status` | string | Interview schedule status |
@@ -2111,6 +2118,7 @@ Trigger workflow when a new job is created
21112118
| Parameter | Type | Description |
21122119
| --------- | ---- | ----------- |
21132120
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2121+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
21142122
| `job` | object | job output from the tool |
21152123
|`id` | string | Job UUID |
21162124
|`title` | string | Job title |
@@ -2136,6 +2144,7 @@ Trigger workflow when a job posting is deleted
21362144
| Parameter | Type | Description |
21372145
| --------- | ---- | ----------- |
21382146
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2147+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
21392148
| `jobPosting` | object | jobPosting output from the tool |
21402149
|`id` | string | Deleted job posting UUID |
21412150
|`jobId` | string | Associated job UUID |
@@ -2158,6 +2167,7 @@ Trigger workflow when a job posting is updated
21582167
| Parameter | Type | Description |
21592168
| --------- | ---- | ----------- |
21602169
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2170+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
21612171
| `jobPosting` | object | jobPosting output from the tool |
21622172
|`id` | string | Job posting UUID |
21632173
|`title` | string | Job posting title |
@@ -2188,6 +2198,7 @@ Trigger workflow when a job is updated
21882198
| Parameter | Type | Description |
21892199
| --------- | ---- | ----------- |
21902200
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2201+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
21912202
| `job` | object | job output from the tool |
21922203
|`id` | string | Job UUID |
21932204
|`title` | string | Job title |
@@ -2213,6 +2224,7 @@ Trigger workflow when a new offer is created
22132224
| Parameter | Type | Description |
22142225
| --------- | ---- | ----------- |
22152226
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2227+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
22162228
| `offer` | object | offer output from the tool |
22172229
|`id` | string | Offer UUID |
22182230
|`applicationId` | string | Associated application UUID |
@@ -2240,6 +2252,7 @@ Trigger workflow when an offer is deleted
22402252
| Parameter | Type | Description |
22412253
| --------- | ---- | ----------- |
22422254
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2255+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
22432256
| `offer` | object | offer output from the tool |
22442257
|`id` | string | Deleted offer UUID |
22452258
|`applicationId` | string | Associated application UUID |
@@ -2262,6 +2275,7 @@ Trigger workflow when an offer is updated
22622275
| Parameter | Type | Description |
22632276
| --------- | ---- | ----------- |
22642277
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2278+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
22652279
| `offer` | object | offer output from the tool |
22662280
|`id` | string | Offer UUID |
22672281
|`applicationId` | string | Associated application UUID |
@@ -2289,6 +2303,7 @@ Trigger workflow when a headcount opening is created
22892303
| Parameter | Type | Description |
22902304
| --------- | ---- | ----------- |
22912305
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2306+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
22922307
| `opening` | object | opening output from the tool |
22932308
|`id` | string | Opening UUID |
22942309
|`openedAt` | string | Open timestamp |
@@ -2317,6 +2332,7 @@ Trigger workflow when an e-signature request changes state
23172332
| Parameter | Type | Description |
23182333
| --------- | ---- | ----------- |
23192334
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
2335+
| `webhookActionId` | string | Ashby delivery identifier, stable across retries |
23202336
| `relatedEntityType` | string | Related entity type: application or offer |
23212337
| `applicationId` | string | Related application UUID |
23222338
| `offerId` | string | Related offer UUID |

apps/sim/blocks/blocks/ashby.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ describe('AshbyBlock', () => {
4747
expect(result.socialLinks).toEqual([{ type: 'Twitter', url: 'https://twitter.com/jane' }])
4848
})
4949

50+
it('preserves an empty JSON array so users can clear all social links', () => {
51+
const result = AshbyBlock.tools.config.params!(
52+
buildParams('update_candidate', { socialLinks: '[]' })
53+
)
54+
expect(result.socialLinks).toEqual([])
55+
})
56+
5057
it('throws instead of silently dropping the field when the JSON is malformed', () => {
5158
// A silent [] here would let the Ashby update proceed without applying
5259
// the requested links and with no error shown to the workflow author.
@@ -66,6 +73,43 @@ describe('AshbyBlock', () => {
6673
})
6774
})
6875

76+
describe('nullable candidate updates', () => {
77+
it('preserves nulls from dynamic references for aliased update fields', () => {
78+
const result = AshbyBlock.tools.config.params!(
79+
buildParams('update_candidate', {
80+
updateName: null,
81+
candidateLocation: null,
82+
candidateCreatedAt: null,
83+
})
84+
)
85+
expect(result.name).toBeNull()
86+
expect(result.location).toBeNull()
87+
expect(result.createdAt).toBeNull()
88+
})
89+
})
90+
91+
describe('archiveEmail parsing (change_application_stage)', () => {
92+
it('parses the documented archive email object', () => {
93+
const result = AshbyBlock.tools.config.params!(
94+
buildParams('change_application_stage', {
95+
archiveEmail: '{"communicationTemplateId":"template-1","sendAt":"2026-09-02T16:32:00Z"}',
96+
})
97+
)
98+
expect(result.archiveEmail).toEqual({
99+
communicationTemplateId: 'template-1',
100+
sendAt: '2026-09-02T16:32:00Z',
101+
})
102+
})
103+
104+
it('rejects non-object archive email input', () => {
105+
expect(() =>
106+
AshbyBlock.tools.config.params!(
107+
buildParams('change_application_stage', { archiveEmail: 'true' })
108+
)
109+
).toThrow(/expected a JSON object/)
110+
})
111+
})
112+
69113
describe('wandConfig on array-shaped fields', () => {
70114
it('does not request object-wrapped output for alternateEmailAddresses or socialLinks', () => {
71115
// generationType 'json-object' makes the wand API append "the response

0 commit comments

Comments
 (0)