Creating a Custom Careers Page

There are three main steps to using Ashby's API to create a fully functioning careers page, from rendering job openings to submitting applications.

Step 1 Get a list of open job postings using the jobPosting.list endpoint.

Step 2 For each job posting in the list, use the endpoint to get detailed information, including a specification of the application form, for that job.

These application form specifications will allow you to programmatically build forms (for example, HTML forms) that your applicants can fill out.

Step 3 Using these application forms, you can send applications to Ashby using the applicationForm.submit endpoint.

Get all open jobs

Use the jobPosting.list endpoint to get a list of your organization's open job postings. You can use this list to create a main careers page that lists opportunities. See the guide on pagination and incremental synchronization for detailed instructions on how to fetch all available data from the endpoint.

Get detailed info about a specific job posting

Use the endpoint to get detailed information about a particular job posting, including the definition of the form that should be used for submitting applications to this job posting.

Form definitions

A form definition looks like this:

  "fields": [
                    "field": {
                        "id": "afc7d3c7-5616-423a-b0db-261888f6f10b",
                        "type": "String",
                        "path": "_systemfield_name",
                        "humanReadablePath": "Name",
                        "title": "Name",
                        "isNullable": false
                    "isRequired": true
                    "field": {
                        "id": "1c23a4e4-d40a-4574-a3b9-8ec4318421ec",
                        "type": "Email",
                        "path": "_systemfield_email",
                        "humanReadablePath": "Email",
                        "title": "Email",
                        "isNullable": false
                    "isRequired": true
                    "field": {
                        "id": "fe05b423-e580-4c4b-8a67-32d354bdfe91",
                        "type": "File",
                        "path": "_systemfield_resume",
                        "humanReadablePath": "Resume",
                        "title": "Resume",
                        "isNullable": false
                    "isRequired": true

Each field in fields describes a field that should be in the resulting form, for example a text box or file input. Each field includes its title, a unique id, whether it's a required part of the form, and its type.

The field type can be one of the following values: "String", "Email", "File", "Date", "Number", "Boolean", "LongText", "ValueSelect" (a single-option select field), "MultiValueSelect" (select multiple values), "Phone", "Score", and "SocialLink". For example, for a "String" field you'll likely want an <input> element in your HTML form. For a "File", a file selector, etc.

Submit an application

When a candidate submits an application for a job posting, you should send the application data to the applicationForm.submit endpoint.

This endpoint requires that request bodies be Content-Type: multipart/form-data. This is to support file uploads (for example, resumes) along with other data for the application.

Suppose, for example, your application form includes a name field, an email field, and resume file field. Each of these fields in the form definition will have a path property (see the example above). This path property is used internally by Ashby to identify the individual fields.

A submission to the applicationForm.submit endpoint for this form might look like this (leaving out the auth header for clarity):

curl --request POST '' \
--header 'Content-Type: multipart/form-data' \
--form 'applicationForm={"fieldSubmissions":[{"path":"_systemfield_name","value":"Monty Api"},{"path":"_systemfield_email","value":"[email protected]"},{"path":"_systemfield_resume","value":"resume_1"}]}' \
--form 'resume_1=@/path/to/file' \
--form 'jobPostingId=26a4281b-4ab2-4829-b664-7c43d7dbd409' \
--form 'utmData={ "utm_source": "whatever", "utm_campaign": "something" }'

The applicationForm field's value is a JSON string that encodes the applicant's response to each question on the form, identified by the path that was returned on the form definition. The jobPostingId field holds the value of the id for the job posting this application is for.

Note the value for the file field: "resume_1". This value matches another key in this request form: this identifies the file that is being uploaded as a response to the _systemfield_resume question. Note that the identifier resume_1 can be any string you want - it just has to be unique in the request form.

The value of the resume_1 parameter is the actual binary of the file being uploaded (note in this example we use a cURL utility @ that allows specifying a local file path. See the endpoint documentation for an example of using Node to get the file data).

Finally, the utmData parameter is the only optional one here. If you have UTM data that you'd like attached to this application, you can include it here in a JSON-encoded string.

After sending this request, if it's successful you'll receive a response that includes both the original form definition as well as an array of the recorded responses. If there is a problem with the form (for example, if "name" is required but not provided), the response will contain a list of errors.