Ashby Webhooks API
Manage outbound webhook subscriptions (webhook.create / update / info / delete) and consume 22 event types: applicationSubmit, applicationUpdate, candidateDelete, candidateHire, candidateMerge, candidateStageChange, interviewPlanTransition, interviewScheduleCreate, interviewScheduleUpdate, jobCreate, jobUpdate, jobPostingPublish, jobPostingUnpublish, jobPostingUpdate, offerCreate, offerDelete, offerUpdate, openingCreate, ping, pushToHRIS, signatureRequestUpdate, surveySubmit. HMAC-SHA256 signed via Ashby-Signature header; automatic retries on non-2xx responses.
Ashby Webhooks API is one of 15 APIs that Ashby publishes on the APIs.io network, described by a machine-readable OpenAPI specification.
This API exposes 1 machine-runnable capability that can be deployed as REST, MCP, or Agent Skill surfaces via Naftiko.
Tagged areas include Webhooks, Events, Eventing, and Recruiting. The published artifact set on APIs.io includes API documentation, an OpenAPI specification, and 1 Naftiko capability spec.
Documentation
Specifications
Other Resources
openapi: 3.1.0
info:
version: 1.0.0
title: Ashby API
description: Complete public API for accessing resources in your Ashby instance. Includes applications, candidates, jobs, interviews, offers, surveys, custom fields, organization metadata, files, reports, approvals, and webhooks.
contact:
name: Ashby Support
url: https://app.ashbyhq.com/support
email: [email protected]
servers:
- url: https://api.ashbyhq.com
security:
- BasicAuth: []
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
description: HTTP Basic Auth. Send your Ashby API key as the username and leave the password blank.
WebhookSignature:
type: apiKey
in: header
name: Ashby-Signature
description: HMAC-SHA256 signature of the webhook payload, used to verify webhook authenticity.
paths:
/apiKey.info:
post:
summary: apiKey.info
description: 'Retrieve information about the API key being used to make the request.
**Requires the [`apiKeysRead`](authentication#permissions-apikeyinfo) permission.**
'
operationId: apiKeyInfo
tags:
- API Key
requestBody:
content:
application/json:
schema:
type: object
responses:
'200':
description: Responses for the apiKey.info endpoint
content:
application/json:
schema:
oneOf:
- title: Success response
allOf:
- $ref: '#/paths/~1job.info/post/responses/200/content/application~1json/schema/oneOf/0/allOf/0'
- type: object
properties:
results:
type: object
properties:
title:
type: string
description: The name of the API key.
example: Custom Job Board API key
createdAt:
$ref: '#/paths/~1candidate.createNote/post/requestBody/content/application~1json/schema/properties/createdAt'
scopes:
type: array
description: List of permission scopes that this API key is authorized for.
items:
type: string
example:
- jobs:read
- candidates:read
required:
- results
- title: Error response
$ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
security:
- BasicAuth: []
/application.addHiringTeamMember:
post:
summary: application.addHiringTeamMember
description: "Adds an Ashby user to the hiring team at the application level. \n\n**Requires the [`candidateWrite`](authentication#permissions-applicationaddhiringteammember) permission.**\n"
operationId: applicationaddhiringteammember
tags:
- Application
requestBody:
content:
application/json:
schema:
required:
- applicationId
- teamMemberId
- roleId
properties:
applicationId:
allOf:
- description: The application to assign the user a role on.
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
teamMemberId:
allOf:
- description: The id of the user to assign the role to.
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
roleId:
allOf:
- description: The id of the hiring team role to assign.
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
responses:
'200':
description: Responses for the application.addHiringTeamMember endpoint
content:
application/json:
schema:
oneOf:
- title: Success response
allOf:
- $ref: '#/paths/~1job.info/post/responses/200/content/application~1json/schema/oneOf/0/allOf/0'
- type: object
properties:
results:
$ref: '#/paths/~1hiringTeam.addMember/post/responses/200/content/application~1json/schema/oneOf/0/allOf/1/properties/results'
required:
- results
- title: Error response
$ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
/application.changeSource:
post:
summary: application.changeSource
operationId: applicationChangeSource
description: 'Change the source of an application.
**Requires the [`candidatesWrite`](authentication#permissions-applicationchangesource) permission.**
'
tags:
- Application
requestBody:
content:
application/json:
schema:
properties:
applicationId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the application to update the source of
sourceId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The source to set on the application. Pass null to unset an application's source.
required:
- applicationId
- sourceId
example:
applicationId: 3ae2b801-19f6-41ef-ad28-214bd731948f
sourceId: 2c6991c5-c9e2-4af8-879e-29c5a9d26509
responses:
'200':
description: Responses from the application.changeSource endpoint
content:
application/json:
schema:
oneOf:
- title: Success response
allOf:
- $ref: '#/paths/~1job.info/post/responses/200/content/application~1json/schema/oneOf/0/allOf/0'
- type: object
properties:
results:
$ref: '#/webhooks/pushToHRIS/post/requestBody/content/application~1json/schema/properties/data/properties/application'
required:
- results
- $ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
/application.changeStage:
post:
summary: application.changeStage
operationId: applicationChangeStage
description: 'Change the stage of an application
**Requires the [`candidatesWrite`](authentication#permissions-applicationchangestage) permission.**
'
tags:
- Application
requestBody:
content:
application/json:
schema:
properties:
applicationId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the application to update the stage of
interviewStageId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The interview stage to move the application to.
archiveReasonId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: "Archive Reason to set when moving to an Interview Stage with type: `Archived`. \nNote: You must pass this parameter when moving to an Interview Stage with type: `Archived`\n"
archiveEmail:
type: object
properties:
communicationTemplateId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: 'The id of the communication template to use for the email.
Note: This communication template''s intendedTypes must include: `email`.
'
sendAt:
type: string
format: date-time
description: 'The date and time to send the email as an ISO 8601 date and time string, e.g. `2025-02-24T08:27:01Z`.
If omitted, the email will be scheduled for the following morning at 9:32am in your default timezone.
If the time is in the past, the email will be sent immediately.
Note: If a timezone is not specified, timestamps will be interpreted in UTC, regardless of your timezone setting.
'
description: 'Email to send to the candidate when moving to an Interview Stage with type: `Archived`.
Note: If application is already archived, the email will not be sent.
Note: If email send fails, the application stage will not be updated. If this is the case, you can retry the request without this parameter.
Note: You may only pass this parameter if the application is moving to an Interview Stage with type: `Archived`.
'
required:
- communicationTemplateId
required:
- applicationId
- interviewStageId
example:
applicationId: 3ae2b801-19f6-41ef-ad28-214bd731948f
interviewStageId: 2c6991c5-c9e2-4af8-879e-29c5a9d26509
responses:
'200':
description: Responses from the application.changeStage endpoint
content:
application/json:
schema:
oneOf:
- title: Success response
allOf:
- $ref: '#/paths/~1job.info/post/responses/200/content/application~1json/schema/oneOf/0/allOf/0'
- type: object
properties:
results:
$ref: '#/webhooks/pushToHRIS/post/requestBody/content/application~1json/schema/properties/data/properties/application'
required:
- results
- allOf:
- $ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
- type: object
properties:
errors:
type: array
items:
type: string
enum:
- invalid_input
- application_not_found
- interview_plan_not_found
- interview_stage_not_found
- archive_reason_not_found
- communication_template_not_found
- communication_template_not_allowed
description: '| Error Code | Description |
|------------|-------------|
| `invalid_input` | A parameter is the wrong type |
| `invalid_input` | Archive fields are not allowed for non-archive stages |
| `invalid_input` | Archive reason ID is required for archive stages |
| `application_not_found` | |
| `interview_plan_not_found` | |
| `interview_stage_not_found` | |
| `archive_reason_not_found` | |
| `communication_template_not_found` | |
| `communication_template_not_allowed` | Template contains unresolvable substitution tokens |
| `communication_template_not_allowed` | Template intended types does not include ''email'' |
| `communication_template_not_allowed` | [Less common] Template is archived |
| `communication_template_not_allowed` | [Less common] Template is a one-off template |
'
/application.create:
post:
summary: application.create
operationId: applicationCreate
description: 'Consider a candidate for a job (eg when sourcing a candidate for a job posting).
If you''re submitting an application as a job board, use the [`applicationForm.submit`](https://developers.ashbyhq.com/reference/applicationformsubmit) endpoint instead. See [Creating a custom careers page](https://developers.ashbyhq.com/docs/creating-a-custom-careers-page) for details.
**Requires the [`candidatesWrite`](authentication#permissions-applicationcreate) permission.**
To set values for custom fields on Applications, use the [`customFields.setValue`](https://developers.ashbyhq.com/reference/customfieldsetvalue) endpoint.
'
tags:
- Application
requestBody:
content:
application/json:
schema:
properties:
candidateId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the candidate to consider for a job
jobId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the job to consider the candidate for
interviewPlanId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: 'The id of the interview plan to place the application in. If none is provided, the default interview plan is used.
'
interviewStageId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: "The interview stage of the interview plan (either default or provided) to place the application in. \nIf none is provided, the application is placed in the first \"Lead\" stage. \nYou can also supply the special string \"FirstPreInterviewScreen\", which will choose the first pre-interview-screen stage on the specified job's interview plan.\n"
sourceId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The source to set on the application being created.
creditedToUserId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the user the application will be credited to.
createdAt:
allOf:
- description: 'An ISO date string to set the application''s `createdAt` timestamp. When this value isn''t provided, the `createdAt` timestamp defaults to the time the call was made.
'
- $ref: '#/paths/~1candidate.createNote/post/requestBody/content/application~1json/schema/properties/createdAt'
applicationHistory:
allOf:
- type: array
description: An array of objects representing the application history.
items:
type: object
properties:
stageId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The ID of the interview stage for this history event. This stage must belong to the interview plan associated with the application.
stageNumber:
allOf:
- type: integer
- description: The sort order of this event. 0 is the first, the highest number will be the current stage.
enteredStageAt:
allOf:
- $ref: '#/paths/~1candidate.addEmailMessage/post/requestBody/content/application~1json/schema/properties/sentAt/allOf/0'
- description: An ISO date string representing the time the application entered this stage.
archiveReasonId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The ID of the archive reason. If the interview stage is an `Archived` stage type, this field is required.
required:
- stageId
- stageNumber
- enteredStageAt
- description: 'An array of objects representing the application history.
'
required:
- candidateId
- jobId
example:
candidateId: 3ae2b801-19f6-41ef-ad28-214bd731948f
jobId: 2c6991c5-c9e2-4af8-879e-29c5a9d26509
responses:
'200':
description: Responses from the application.create endpoint
content:
application/json:
schema:
oneOf:
- title: Success response
allOf:
- $ref: '#/paths/~1job.info/post/responses/200/content/application~1json/schema/oneOf/0/allOf/0'
- type: object
properties:
results:
$ref: '#/webhooks/pushToHRIS/post/requestBody/content/application~1json/schema/properties/data/properties/application'
required:
- results
- $ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
/applicationForm.submit:
post:
summary: applicationForm.submit
description: 'Submit an application for a job posting.
**Requires the [`candidatesWrite`](authentication#permissions-applicationformsubmit) permission.**
The Content-Type of this request must be `multipart/form-data` or `application/json`.
When using `application/json`, file fields should contain a handle string obtained from [`file.createFileUploadHandle`](https://developers.ashbyhq.com/reference/filecreatefileuploadhandle) with `fileUploadContext` set to `ApplicationForm`. This allows files to be uploaded directly to storage, bypassing the need to send file bytes through the API.
When using `multipart/form-data`, file fields should reference a file part included in the multipart request body (existing behavior).
**Important:** Please ensure that you check the response''s `success` field and display any validation errors to candidates if it is `false`. Not doing so will result in applications not being recorded, without any notification to the candidate prompting them to resubmit. For testing purposes, you can simulate a validation failure by submitting the email address `[email protected]`.
**Note: The requests generated from this documentation will not work for this endpoint.**
The values accepted for each field depend on the type of field that''s being filled out:
- `Boolean` - A boolean value
- `Date` - A date string in the format YYYY-MM-DD
- `Email` - A valid email address
- `Number` - An integer
- `RichText` - We do not support submitting rich text documents via the API but we do support submitting plain text values for these fields. Plain text values must be submitted in the format `{ type: "PlainText", value: "A plain text string" }`
- `Score` - An integer between 1 and 4 submitted in the format `{ score: 4 }`
- `Phone`, `String` A string
- `ValueSelect` - A string that matches the value of one of the ValueSelect field''s selectable options
- `MultiValueSelect` - An array of strings that exist in the MultiValueSelect field''s selectable options
- `Location` - An object with the following properties: `{ country: "USA", city: "San Francisco", region: "California" }`. You may provide any combination of these properties and we will attempt to geocode the location. For best results, provide all three properties.
- `EducationHistory` - [Early Access] An array of objects with the following properties: `{ schoolName: "University of California, Berkeley", degree: "Bachelor of Science", major: "Computer Science", startDate: "2020-09-01", endDate: "2024-05-15", isCurrent: true }`. Only `schoolName` is required. We will attempt to match the school name to a known school in our database. If `isCurrent` is true, `endDate` must not be provided.
'
operationId: applicationformsubmit
tags:
- Application Form
requestBody:
content:
application/json:
schema:
type: object
required:
- jobPostingId
- applicationForm
properties:
jobPostingId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the job posting to submit an application for
applicationForm:
$ref: '#/paths/~1offer.create/post/requestBody/content/application~1json/schema/properties/offerForm'
utmData:
type: object
properties:
utm_source:
type: string
utm_campaign:
type: string
utm_medium:
type: string
utm_term:
type: string
utm_content:
type: string
tagIds:
type: array
description: The ids of the tags to apply to the candidate
items:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
allowSubmissionForUnpublishedJobPosting:
type: boolean
example: true
description: Defaults to true. If you are running a custom built careers page that is statically generated, you may want to set this to true to allow applications to be submitted in the time a job is unpublished and your careers page is refreshed. If set to false, applications submitted for unpublished job postings will return a `job_posting_not_published` error.
multipart/form-data:
schema:
type: object
required:
- jobPostingId
- applicationForm
properties:
jobPostingId:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
- description: The id of the job posting to submit an application for
applicationForm:
$ref: '#/paths/~1offer.create/post/requestBody/content/application~1json/schema/properties/offerForm'
utmData:
type: object
properties:
utm_source:
type: string
utm_campaign:
type: string
utm_medium:
type: string
utm_term:
type: string
utm_content:
type: string
tagIds:
type: array
description: The ids of the tags to apply to the candidate
items:
allOf:
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
allowSubmissionForUnpublishedJobPosting:
type: boolean
example: true
description: Defaults to true. If you are running a custom built careers page that is statically generated, you may want to set this to true to allow applications to be submitted in the time a job is unpublished and your careers page is refreshed. If set to false, applications submitted for unpublished job postings will return a `job_posting_not_published` error.
<file key>:
type: string
description: Any file referenced in the `applicationForm`. The name of this field must exactly match the `value` on the `fieldSubmission` that references this file.
format: binary
responses:
'200':
description: Responses from the applicationFeedback.submit endpoint
content:
application/json:
schema:
oneOf:
- title: Success response
allOf:
- $ref: '#/paths/~1job.info/post/responses/200/content/application~1json/schema/oneOf/0/allOf/0'
- type: object
properties:
results:
type: object
properties:
submittedFormInstance:
type: object
properties:
id:
$ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
formDefinition:
$ref: '#/paths/~1offer.start/post/responses/200/content/application~1json/schema/oneOf/0/allOf/1/properties/results/properties/formDefinition'
submittedValues:
type: object
example:
_systemfield_name: Gob Bluth
required:
- id
- formDefinition
- submittedValues
formMessages:
allOf:
- type: object
description: Messages related to the application form submission. Check if the application was blocked before submitting any surveys.
properties:
blocked:
type: boolean
description: Whether the application was blocked due to configured application limits (eg too many applications submitted for a single candidate). If true, do not attempt to submit any surveys, if any.
example: true
blockMessageForCandidateHtml:
type: string
description: A message to display to the candidates regarding the application limits. If there is no message, then the candidate should not be informed that they were blocked.
example: '<div><p>Please Note: we have set up limits for applications for this role. Candidates may not apply more than 2 times in any 60 day span for any job in the Application Limit Group.</p></div>
'
required:
- blocked
- description: "Contains information about the form submission. When `blocked` is true, \nthe application was blocked by organizational rules and no Application \nrecord was created. Do not attempt to call `application.info` with the \n`submittedFormInstanceId` as it will return an `application_blocked` error. \nThe submission is saved as a blocked submission and can be reviewed by \nadministrators.\n"
required:
- submittedFormInstance
- formMessages
required:
- results
- $ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
/applicationHiringTeamRole.list:
post:
summary: applicationHiringTeamRole.list
operationId: applicationHiringTeamRoleList
description: 'Gets all available hiring team roles for applications in the organization.
**Requires the [`candidatesRead`](authentication#permissions-applicationHiringTeamRoleList) permission.**
'
tags:
- Application Hiring Team Role
responses:
'200':
description: Responses from the applicationHiringTeamRole.list endpoint
content:
application/json:
schema:
oneOf:
- allOf:
- properties:
results:
type: array
items:
allOf:
- type: object
properties:
id:
$ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
title:
type: string
required:
- id
- title
required:
- results
- $ref: '#/paths/~1report.generate/post/responses/429/content/application~1json/schema'
/application.info:
post:
summary: application.info
operationId: applicationInfo
description: 'Fetch application details by application id or by submitted form instance id (which is return by the `applicationForm.submit` endpoint). If both applicationId and submittedFormInstanceId are provided, we will lookup by applicationId.
**Requires the [`candidatesRead`](authentication#permissions-applicationinfo) permission.**
'
tags:
- Application
requestBody:
content:
application/json:
schema:
oneOf:
- type: object
properties:
applicationId:
allOf:
- description: The id of the application to fetch.
- $ref: '#/paths/~1interviewerPool.addUser/post/requestBody/content/application~1json/schema/properties/userId'
expand:
type: array
description: "Choose to expand the result and include additional data for related objects. \n"
items:
type: string
enum:
- openings
- applicationFormSubmissions
- referrals
required:
- applicationId
- type: object
properties:
submittedFormInstanceId:
allOf:
- description: The id of
# --- truncated at 32 KB (641 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/ashby-hq/refs/heads/main/openapi/ashby-openapi.yml