Skip to content

Commit 12908c1

Browse files
authored
feat(ashby): add 15 new tools and fix existing tool accuracy (#3662)
* feat(ashby): add 15 new tools and fix existing tool accuracy * fix(ashby): fix response field mappings for changeStage and createNote * fix(ashby): fix websiteUrl field name in candidate.update request * fix(ashby): revert body field names to candidateId and jobId for info endpoints * fix(ashby): add subblock ID migrations for removed emailType and phoneType * fix(ashby): map removed emailType/phoneType to dummy keys to avoid data corruption
1 parent 638063c commit 12908c1

29 files changed

+1780
-303
lines changed

apps/sim/blocks/blocks/ashby.ts

Lines changed: 186 additions & 65 deletions
Large diffs are not rendered by default.

apps/sim/lib/workflows/migrations/subblock-migrations.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export const SUBBLOCK_ID_MIGRATIONS: Record<string, Record<string, string>> = {
2323
knowledge: {
2424
knowledgeBaseId: 'knowledgeBaseSelector',
2525
},
26+
ashby: {
27+
emailType: '_removed_emailType',
28+
phoneType: '_removed_phoneType',
29+
},
2630
}
2731

2832
/**
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { ToolConfig, ToolResponse } from '@/tools/types'
2+
3+
interface AshbyAddCandidateTagParams {
4+
apiKey: string
5+
candidateId: string
6+
tagId: string
7+
}
8+
9+
interface AshbyAddCandidateTagResponse extends ToolResponse {
10+
output: {
11+
success: boolean
12+
}
13+
}
14+
15+
export const addCandidateTagTool: ToolConfig<
16+
AshbyAddCandidateTagParams,
17+
AshbyAddCandidateTagResponse
18+
> = {
19+
id: 'ashby_add_candidate_tag',
20+
name: 'Ashby Add Candidate Tag',
21+
description: 'Adds a tag to a candidate in Ashby.',
22+
version: '1.0.0',
23+
24+
params: {
25+
apiKey: {
26+
type: 'string',
27+
required: true,
28+
visibility: 'user-only',
29+
description: 'Ashby API Key',
30+
},
31+
candidateId: {
32+
type: 'string',
33+
required: true,
34+
visibility: 'user-or-llm',
35+
description: 'The UUID of the candidate to add the tag to',
36+
},
37+
tagId: {
38+
type: 'string',
39+
required: true,
40+
visibility: 'user-or-llm',
41+
description: 'The UUID of the tag to add',
42+
},
43+
},
44+
45+
request: {
46+
url: 'https://api.ashbyhq.com/candidate.addTag',
47+
method: 'POST',
48+
headers: (params) => ({
49+
'Content-Type': 'application/json',
50+
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,
51+
}),
52+
body: (params) => ({
53+
candidateId: params.candidateId,
54+
tagId: params.tagId,
55+
}),
56+
},
57+
58+
transformResponse: async (response: Response) => {
59+
const data = await response.json()
60+
61+
if (!data.success) {
62+
throw new Error(data.errorInfo?.message || 'Failed to add tag to candidate')
63+
}
64+
65+
return {
66+
success: true,
67+
output: {
68+
success: true,
69+
},
70+
}
71+
},
72+
73+
outputs: {
74+
success: { type: 'boolean', description: 'Whether the tag was successfully added' },
75+
},
76+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { ToolConfig, ToolResponse } from '@/tools/types'
2+
3+
interface AshbyChangeApplicationStageParams {
4+
apiKey: string
5+
applicationId: string
6+
interviewStageId: string
7+
archiveReasonId?: string
8+
}
9+
10+
interface AshbyChangeApplicationStageResponse extends ToolResponse {
11+
output: {
12+
applicationId: string
13+
stageId: string | null
14+
}
15+
}
16+
17+
export const changeApplicationStageTool: ToolConfig<
18+
AshbyChangeApplicationStageParams,
19+
AshbyChangeApplicationStageResponse
20+
> = {
21+
id: 'ashby_change_application_stage',
22+
name: 'Ashby Change Application Stage',
23+
description:
24+
'Moves an application to a different interview stage. Requires an archive reason when moving to an Archived stage.',
25+
version: '1.0.0',
26+
27+
params: {
28+
apiKey: {
29+
type: 'string',
30+
required: true,
31+
visibility: 'user-only',
32+
description: 'Ashby API Key',
33+
},
34+
applicationId: {
35+
type: 'string',
36+
required: true,
37+
visibility: 'user-or-llm',
38+
description: 'The UUID of the application to update the stage of',
39+
},
40+
interviewStageId: {
41+
type: 'string',
42+
required: true,
43+
visibility: 'user-or-llm',
44+
description: 'The UUID of the interview stage to move the application to',
45+
},
46+
archiveReasonId: {
47+
type: 'string',
48+
required: false,
49+
visibility: 'user-or-llm',
50+
description:
51+
'Archive reason UUID. Required when moving to an Archived stage, ignored otherwise',
52+
},
53+
},
54+
55+
request: {
56+
url: 'https://api.ashbyhq.com/application.changeStage',
57+
method: 'POST',
58+
headers: (params) => ({
59+
'Content-Type': 'application/json',
60+
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,
61+
}),
62+
body: (params) => {
63+
const body: Record<string, unknown> = {
64+
applicationId: params.applicationId,
65+
interviewStageId: params.interviewStageId,
66+
}
67+
if (params.archiveReasonId) body.archiveReasonId = params.archiveReasonId
68+
return body
69+
},
70+
},
71+
72+
transformResponse: async (response: Response) => {
73+
const data = await response.json()
74+
75+
if (!data.success) {
76+
throw new Error(data.errorInfo?.message || 'Failed to change application stage')
77+
}
78+
79+
const r = data.results
80+
81+
return {
82+
success: true,
83+
output: {
84+
applicationId: r.id ?? null,
85+
stageId: r.currentInterviewStage?.id ?? null,
86+
},
87+
}
88+
},
89+
90+
outputs: {
91+
applicationId: { type: 'string', description: 'Application UUID' },
92+
stageId: { type: 'string', description: 'New interview stage UUID' },
93+
},
94+
}

apps/sim/tools/ashby/create_application.ts

Lines changed: 3 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,7 @@ interface AshbyCreateApplicationParams {
1313

1414
interface AshbyCreateApplicationResponse extends ToolResponse {
1515
output: {
16-
id: string
17-
status: string
18-
candidate: {
19-
id: string
20-
name: string
21-
}
22-
job: {
23-
id: string
24-
title: string
25-
}
26-
currentInterviewStage: {
27-
id: string
28-
title: string
29-
type: string
30-
} | null
31-
source: {
32-
id: string
33-
title: string
34-
} | null
35-
createdAt: string
36-
updatedAt: string
16+
applicationId: string
3717
}
3818
}
3919

@@ -132,74 +112,12 @@ export const createApplicationTool: ToolConfig<
132112
return {
133113
success: true,
134114
output: {
135-
id: r.id ?? null,
136-
status: r.status ?? null,
137-
candidate: {
138-
id: r.candidate?.id ?? null,
139-
name: r.candidate?.name ?? null,
140-
},
141-
job: {
142-
id: r.job?.id ?? null,
143-
title: r.job?.title ?? null,
144-
},
145-
currentInterviewStage: r.currentInterviewStage
146-
? {
147-
id: r.currentInterviewStage.id ?? null,
148-
title: r.currentInterviewStage.title ?? null,
149-
type: r.currentInterviewStage.type ?? null,
150-
}
151-
: null,
152-
source: r.source
153-
? {
154-
id: r.source.id ?? null,
155-
title: r.source.title ?? null,
156-
}
157-
: null,
158-
createdAt: r.createdAt ?? null,
159-
updatedAt: r.updatedAt ?? null,
115+
applicationId: r.applicationId ?? null,
160116
},
161117
}
162118
},
163119

164120
outputs: {
165-
id: { type: 'string', description: 'Created application UUID' },
166-
status: { type: 'string', description: 'Application status (Active, Hired, Archived, Lead)' },
167-
candidate: {
168-
type: 'object',
169-
description: 'Associated candidate',
170-
properties: {
171-
id: { type: 'string', description: 'Candidate UUID' },
172-
name: { type: 'string', description: 'Candidate name' },
173-
},
174-
},
175-
job: {
176-
type: 'object',
177-
description: 'Associated job',
178-
properties: {
179-
id: { type: 'string', description: 'Job UUID' },
180-
title: { type: 'string', description: 'Job title' },
181-
},
182-
},
183-
currentInterviewStage: {
184-
type: 'object',
185-
description: 'Current interview stage',
186-
optional: true,
187-
properties: {
188-
id: { type: 'string', description: 'Stage UUID' },
189-
title: { type: 'string', description: 'Stage title' },
190-
type: { type: 'string', description: 'Stage type' },
191-
},
192-
},
193-
source: {
194-
type: 'object',
195-
description: 'Application source',
196-
optional: true,
197-
properties: {
198-
id: { type: 'string', description: 'Source UUID' },
199-
title: { type: 'string', description: 'Source title' },
200-
},
201-
},
202-
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
203-
updatedAt: { type: 'string', description: 'ISO 8601 last update timestamp' },
121+
applicationId: { type: 'string', description: 'Created application UUID' },
204122
},
205123
}

apps/sim/tools/ashby/create_candidate.ts

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,16 @@ export const createCandidateTool: ToolConfig<
2525
},
2626
email: {
2727
type: 'string',
28-
required: false,
28+
required: true,
2929
visibility: 'user-or-llm',
3030
description: 'Primary email address for the candidate',
3131
},
32-
emailType: {
33-
type: 'string',
34-
required: false,
35-
visibility: 'user-or-llm',
36-
description: 'Email address type: Personal, Work, or Other (default Work)',
37-
},
3832
phoneNumber: {
3933
type: 'string',
4034
required: false,
4135
visibility: 'user-or-llm',
4236
description: 'Primary phone number for the candidate',
4337
},
44-
phoneType: {
45-
type: 'string',
46-
required: false,
47-
visibility: 'user-or-llm',
48-
description: 'Phone number type: Personal, Work, or Other (default Work)',
49-
},
5038
linkedInUrl: {
5139
type: 'string',
5240
required: false,
@@ -77,27 +65,11 @@ export const createCandidateTool: ToolConfig<
7765
body: (params) => {
7866
const body: Record<string, unknown> = {
7967
name: params.name,
68+
email: params.email,
8069
}
81-
if (params.email) {
82-
body.primaryEmailAddress = {
83-
value: params.email,
84-
type: params.emailType || 'Work',
85-
isPrimary: true,
86-
}
87-
}
88-
if (params.phoneNumber) {
89-
body.primaryPhoneNumber = {
90-
value: params.phoneNumber,
91-
type: params.phoneType || 'Work',
92-
isPrimary: true,
93-
}
94-
}
95-
if (params.linkedInUrl || params.githubUrl) {
96-
const socialLinks: Array<{ url: string; type: string }> = []
97-
if (params.linkedInUrl) socialLinks.push({ url: params.linkedInUrl, type: 'LinkedIn' })
98-
if (params.githubUrl) socialLinks.push({ url: params.githubUrl, type: 'GitHub' })
99-
body.socialLinks = socialLinks
100-
}
70+
if (params.phoneNumber) body.phoneNumber = params.phoneNumber
71+
if (params.linkedInUrl) body.linkedInUrl = params.linkedInUrl
72+
if (params.githubUrl) body.githubUrl = params.githubUrl
10173
if (params.sourceId) body.sourceId = params.sourceId
10274
return body
10375
},

apps/sim/tools/ashby/create_note.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,35 +78,12 @@ export const createNoteTool: ToolConfig<AshbyCreateNoteParams, AshbyCreateNoteRe
7878
return {
7979
success: true,
8080
output: {
81-
id: r.id ?? null,
82-
content: r.content ?? null,
83-
author: r.author
84-
? {
85-
id: r.author.id ?? null,
86-
firstName: r.author.firstName ?? null,
87-
lastName: r.author.lastName ?? null,
88-
email: r.author.email ?? null,
89-
}
90-
: null,
91-
createdAt: r.createdAt ?? null,
81+
noteId: r.id ?? null,
9282
},
9383
}
9484
},
9585

9686
outputs: {
97-
id: { type: 'string', description: 'Created note UUID' },
98-
content: { type: 'string', description: 'Note content as stored' },
99-
author: {
100-
type: 'object',
101-
description: 'Note author',
102-
optional: true,
103-
properties: {
104-
id: { type: 'string', description: 'Author user UUID' },
105-
firstName: { type: 'string', description: 'First name' },
106-
lastName: { type: 'string', description: 'Last name' },
107-
email: { type: 'string', description: 'Email address' },
108-
},
109-
},
110-
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
87+
noteId: { type: 'string', description: 'Created note UUID' },
11188
},
11289
}

apps/sim/tools/ashby/get_candidate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const getCandidateTool: ToolConfig<AshbyGetCandidateParams, AshbyGetCandi
3030
Authorization: `Basic ${btoa(`${params.apiKey}:`)}`,
3131
}),
3232
body: (params) => ({
33-
candidateId: params.candidateId,
33+
candidateId: params.candidateId.trim(),
3434
}),
3535
},
3636

0 commit comments

Comments
 (0)