openapi: 3.1.0
info:
title: HeyMilo Public ATS Questions API
description: External developer-facing API for HeyMilo. Create interviewers with agentic workflows, ingest candidates, retrieve interview results, and manage workspace resources.
version: 2.0.0
servers:
- url: https://api.heymilo.ai
description: Production
security:
- ApiKeyAuth: []
tags:
- name: Questions
description: 'Create, read, update, delete, and reorder questions and criteria for an interviewer''s workflow. Covers all modalities: voice, sms, form, resume_eligibility, resume_scoring, and voice_tags.'
paths:
/api/v2/postings/{posting_id}/questions:
get:
tags:
- Questions
summary: List questions
description: Returns all questions and criteria for a posting. Optionally filter by modality (voice, sms, form, resume_eligibility, resume_scoring, voice_tags).
operationId: listQuestions
parameters:
- name: posting_id
in: path
required: true
schema:
type: string
title: Posting Id
- name: modality
in: query
required: false
schema:
anyOf:
- enum:
- voice
- sms
- form
- resume_eligibility
- resume_scoring
- voice_tags
type: string
- type: 'null'
description: 'Filter by question modality: voice, sms, form, resume_eligibility, resume_scoring, voice_tags.'
title: Modality
description: 'Filter by question modality: voice, sms, form, resume_eligibility, resume_scoring, voice_tags.'
- name: limit
in: query
required: false
schema:
type: integer
maximum: 100
minimum: 1
default: 20
title: Limit
- name: starting_after
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Starting After
- name: X-API-KEY
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Api-Key
- name: Authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: X-Workspace-Id
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Workspace-Id
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ListResponse_QuestionResponse_'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
post:
tags:
- Questions
summary: Create a question
description: Creates a new question or criterion for a posting. The ``modality`` field determines the question type.
operationId: createQuestion
parameters:
- name: posting_id
in: path
required: true
schema:
type: string
title: Posting Id
- name: X-API-KEY
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Api-Key
- name: Authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: X-Workspace-Id
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Workspace-Id
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateQuestionRequest'
responses:
'201':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponse_QuestionResponse_'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/api/v2/postings/{posting_id}/questions/{question_id}:
get:
tags:
- Questions
summary: Retrieve a question
description: Returns a single question or criterion by its ID.
operationId: getQuestion
parameters:
- name: posting_id
in: path
required: true
schema:
type: string
title: Posting Id
- name: question_id
in: path
required: true
schema:
type: string
title: Question Id
- name: X-API-KEY
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Api-Key
- name: Authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: X-Workspace-Id
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Workspace-Id
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponse_QuestionResponse_'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
patch:
tags:
- Questions
summary: Update a question
description: Updates the specified question. Only fields present in the request body are modified; omitted fields remain unchanged.
operationId: updateQuestion
parameters:
- name: posting_id
in: path
required: true
schema:
type: string
title: Posting Id
- name: question_id
in: path
required: true
schema:
type: string
title: Question Id
- name: X-API-KEY
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Api-Key
- name: Authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: X-Workspace-Id
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Workspace-Id
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateQuestionRequest'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponse_QuestionResponse_'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
delete:
tags:
- Questions
summary: Delete a question
description: Soft-deletes a question or criterion. It will no longer appear in listings.
operationId: deleteQuestion
parameters:
- name: posting_id
in: path
required: true
schema:
type: string
title: Posting Id
- name: question_id
in: path
required: true
schema:
type: string
title: Question Id
- name: X-API-KEY
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Api-Key
- name: Authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: X-Workspace-Id
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Workspace-Id
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponse_QuestionResponse_'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/api/v2/postings/{posting_id}/questions/reorder:
post:
tags:
- Questions
summary: Reorder questions
description: Sets the display order for questions of a given modality. You must include every active question ID for the specified modality in the desired order.
operationId: reorderQuestions
parameters:
- name: posting_id
in: path
required: true
schema:
type: string
title: Posting Id
- name: X-API-KEY
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Api-Key
- name: Authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: X-Workspace-Id
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: X-Workspace-Id
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReorderQuestionsRequest'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ListResponse_QuestionResponse_'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded.
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
APIErrorDetail:
properties:
code:
type: string
title: Code
description: Machine-readable error code
examples:
- invalid_param
message:
type: string
title: Message
description: Human-readable explanation
examples:
- title must be between 3 and 200 characters
param:
anyOf:
- type: string
- type: 'null'
title: Param
description: Parameter that caused the error
examples:
- title
type: object
required:
- code
- message
title: APIErrorDetail
UpdateQuestionRequest:
properties:
text:
anyOf:
- type: string
- type: 'null'
title: Text
description: Updated question text.
evaluation_criteria:
anyOf:
- type: string
- type: 'null'
title: Evaluation Criteria
description: Updated evaluation criteria.
score_of_1:
anyOf:
- type: string
- type: 'null'
title: Score Of 1
description: Updated score-1 description.
score_of_5:
anyOf:
- type: string
- type: 'null'
title: Score Of 5
description: Updated score-5 description.
score_weight:
anyOf:
- type: integer
maximum: 10.0
minimum: 1.0
- type: 'null'
title: Score Weight
description: Updated importance weight for scoring (1–10).
not_scored:
anyOf:
- type: boolean
- type: 'null'
title: Not Scored
description: Updated not-scored flag.
question_type:
anyOf:
- type: string
- type: 'null'
title: Question Type
description: Updated question format.
min_follow_ups:
anyOf:
- type: integer
minimum: 0.0
- type: 'null'
title: Min Follow Ups
description: Updated min follow-ups.
max_follow_ups:
anyOf:
- type: integer
minimum: 0.0
- type: 'null'
title: Max Follow Ups
description: Updated max follow-ups.
additional_instructions:
anyOf:
- type: string
- type: 'null'
title: Additional Instructions
description: Updated instructions.
post_processing_evaluation_criteria:
anyOf:
- type: string
- type: 'null'
title: Post Processing Evaluation Criteria
description: Updated post-processing criteria.
expected_answer:
anyOf:
- type: string
- type: 'null'
title: Expected Answer
description: Updated expected answer.
expected_value:
anyOf:
- type: integer
- type: 'null'
title: Expected Value
description: Updated expected value.
is_dealbreaker:
anyOf:
- type: boolean
- type: 'null'
title: Is Dealbreaker
description: Updated dealbreaker flag.
answer:
anyOf:
- {}
- type: 'null'
title: Answer
description: Updated expected answer (form).
is_knockout:
anyOf:
- type: boolean
- type: 'null'
title: Is Knockout
description: Updated knockout flag.
dropdown_values:
anyOf:
- items:
additionalProperties:
type: string
type: object
type: array
- type: 'null'
title: Dropdown Values
description: Updated dropdown options.
is_answer_mandated:
anyOf:
- type: boolean
- type: 'null'
title: Is Answer Mandated
description: Updated require-answer flag.
mcq_type:
anyOf:
- type: string
- type: 'null'
title: Mcq Type
description: Updated MCQ type.
operator:
anyOf:
- type: string
- type: 'null'
title: Operator
description: Updated operator.
file_types:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: File Types
description: Updated file types.
allow_multiple_files:
anyOf:
- type: boolean
- type: 'null'
title: Allow Multiple Files
description: Updated multiple files flag.
slider_min:
anyOf:
- type: number
- type: 'null'
title: Slider Min
description: Updated slider min.
slider_max:
anyOf:
- type: number
- type: 'null'
title: Slider Max
description: Updated slider max.
slider_step:
anyOf:
- type: number
- type: 'null'
title: Slider Step
description: Updated slider step.
response_format:
anyOf:
- type: string
enum:
- text
- freeform
- date
- number
- type: 'null'
title: Response Format
description: 'Updated response format: ''text'', ''freeform'', ''date'', or ''number''.'
instructions:
anyOf:
- type: string
- type: 'null'
title: Instructions
description: Updated instructions.
options:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Options
description: Updated options.
structured_config:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Structured Config
description: Updated advanced structured configuration (voice).
type: object
title: UpdateQuestionRequest
description: 'Request body for updating a question.
Only provided fields are modified; omitted fields remain unchanged.'
PaginationMeta:
properties:
has_more:
type: boolean
title: Has More
description: Whether more results exist beyond this page
total_count:
anyOf:
- type: integer
- type: 'null'
title: Total Count
description: Total number of results (if available)
url:
anyOf:
- type: string
- type: 'null'
title: Url
description: URL of this resource
type: object
required:
- has_more
title: PaginationMeta
QuestionResponse:
properties:
object:
type: string
const: question
title: Object
description: Object type identifier.
default: question
id:
type: string
title: Id
description: Unique question/criterion identifier.
examples:
- A1B2C3D4
posting_id:
type: string
title: Posting Id
description: ID of the posting this question belongs to.
examples:
- 13D77095
modality:
type: string
title: Modality
description: 'Question modality: voice, sms, form, resume_eligibility, resume_scoring, voice_tags.'
examples:
- voice
text:
type: string
title: Text
description: The question text, criterion text, or tag name.
examples:
- Tell me about a challenging project you led.
rank:
anyOf:
- type: integer
- type: 'null'
title: Rank
description: Display order (1-based). Lower ranks appear first.
examples:
- 1
evaluation_criteria:
anyOf:
- type: string
- type: 'null'
title: Evaluation Criteria
description: Evaluation criteria for scoring.
score_of_1:
anyOf:
- type: string
- type: 'null'
title: Score Of 1
description: Description of what a score of 1 means.
score_of_5:
anyOf:
- type: string
- type: 'null'
title: Score Of 5
description: Description of what a score of 5 means.
score_weight:
anyOf:
- type: integer
- type: 'null'
title: Score Weight
description: Importance weight for scoring (1–10).
not_scored:
anyOf:
- type: boolean
- type: 'null'
title: Not Scored
description: Whether this question is asked but not scored (voice).
question_type:
anyOf:
- type: string
- type: 'null'
title: Question Type
description: 'Question format. For voice: ''open-ended'' or ''scored''. Auto-inferred by the backend for SMS (''YES_NO'', ''NUMERIC'') and form (''dropdown'', ''file_upload'', ''slider'', etc.) modalities.'
min_follow_ups:
anyOf:
- type: integer
- type: 'null'
title: Min Follow Ups
description: Minimum follow-up questions (voice).
max_follow_ups:
anyOf:
- type: integer
- type: 'null'
title: Max Follow Ups
description: Maximum follow-up questions (voice).
additional_instructions:
anyOf:
- type: string
- type: 'null'
title: Additional Instructions
description: Per-question instructions for the AI interviewer.
post_processing_evaluation_criteria:
anyOf:
- type: string
- type: 'null'
title: Post Processing Evaluation Criteria
description: Evaluation criteria applied post-interview.
expected_answer:
anyOf:
- type: string
- type: 'null'
title: Expected Answer
description: Expected answer text (SMS yes/no).
expected_value:
anyOf:
- type: integer
- type: 'null'
title: Expected Value
description: Expected numeric value (SMS numeric).
is_dealbreaker:
anyOf:
- type: boolean
- type: 'null'
title: Is Dealbreaker
description: Whether wrong answer disqualifies (SMS).
answer:
anyOf:
- {}
- type: 'null'
title: Answer
description: Expected answer for knockout evaluation (form).
is_knockout:
anyOf:
- type: boolean
- type: 'null'
title: Is Knockout
description: Whether this is a knockout question (form).
dropdown_values:
anyOf:
- items:
additionalProperties:
type: string
type: object
type: array
- type: 'null'
title: Dropdown Values
description: Dropdown option list (form).
is_answer_mandated:
anyOf:
- type: boolean
- type: 'null'
title: Is Answer Mandated
description: Whether an answer is required (form).
mcq_type:
anyOf:
- type: string
- type: 'null'
title: Mcq Type
description: 'Multiple-choice type: ''single'' or ''multiple'' (form). May be auto-set to ''multiple'' by the backend for dropdown questions.'
operator:
anyOf:
- type: string
- type: 'null'
title: Operator
description: 'Validation operator: eq, neq, gt, gte, lt, lte (form). May be auto-set by the backend (e.g. ''minimum'' for slider questions).'
file_types:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: File Types
description: Allowed file extensions (form file_upload).
allow_multiple_files:
anyOf:
- type: boolean
- type: 'null'
title: Allow Multiple Files
description: Allow multiple file uploads (form).
slider_min:
anyOf:
- type: number
- type: 'null'
title: Slider Min
description: Minimum slider value (form).
slider_max:
anyOf:
- type: number
- type: 'null'
title: Slider Max
description: Maximum slider value (form).
slider_step:
anyOf:
- type: number
- type: 'null'
title: Slider Step
description: Slider step increment (form).
response_format:
anyOf:
- type: string
enum:
- text
- freeform
- date
- number
- type: 'null'
title: Response Format
description: 'Expected response format for voice_tags: ''text'', ''freeform'', ''date'', or ''number''.'
instructions:
anyOf:
- type: string
- type: 'null'
title: Instructions
description: Extraction instructions (voice_tags).
options:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Options
description: Allowed options (voice_tags).
structured_config:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Structured Config
description: Advanced structured configuration (voice). Opaque key-value object forwarded to the AI interviewer.
created_at:
anyOf:
- type: number
- type: 'null'
title: Created At
description: Unix timestamp when created.
updated_at:
anyOf:
- type: number
- type: 'null'
title: Updated At
description: Unix timestamp when last updated.
type: object
required:
- id
- posting_id
- modality
- text
title: QuestionResponse
description: 'A question or criterion belonging to a posting.
The ``modality`` field indicates the question type (voice, sms, form,
resume_eligibility, resume_scoring, voice_tags). Fields that are not
applicable to a given modality will be ``null``.'
SingleResponse_QuestionResponse_:
properties:
data:
$ref: '#/components/schemas/QuestionResponse'
meta:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Meta
description: Optional metadata. Shape varies by endpoint.
type: object
required:
- data
title: SingleResponse[QuestionResponse]
APIError:
properties:
type:
type: string
title: Type
description: Error category
examples:
- invalid_request_error
code:
type: string
title: Code
description: Machine-readable error code
examples:
- validation_error
message:
type: string
title: Message
description: Human-readable summary
examples:
- The request body failed validation
param:
anyOf:
- type: string
- type: 'null'
title: Param
description: Top-level parameter that caused the error
doc_url:
anyOf:
- type: string
- type: 'null'
title: Doc Url
description: Link to relevant documentation
errors:
items:
$ref: '#/components/schemas/APIErrorDetail'
type: array
title: Errors
description: Detailed per-field validation errors
type: object
required:
- type
- code
- message
title: APIError
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
type: object
required:
- loc
- msg
- type
title: ValidationError
ReorderQuestionsRequest:
properties:
modality:
type: string
enum:
- voice
- sms
- form
- resume_eligibility
- resume_scoring
- voice_tags
title: Modality
description: 'Modality to reorder: voice, sms, form, resume_eligibility, resume_scoring, voice_tags.'
examples:
- voice
order:
items:
type: string
type: array
title: Order
description: Ordered list of question IDs. Every active question for the specified modality must be included.
examples:
- - Q1
- Q3
- Q2
type: object
required:
- modality
- order
title: ReorderQuestionsRequest
description: Request body for reordering questions within a modality.
APIErrorResponse:
properties:
error:
$ref: '#/components/schemas/APIError'
type: object
required:
- error
title: APIErrorResponse
ListResponse_QuestionResponse_:
properties:
data:
items:
$ref: '#/components/schemas/QuestionResponse'
type: array
title: Data
pagination:
$ref: '#/components/schemas/PaginationMeta'
type: object
required:
- data
- pagination
title: ListResponse[QuestionResponse]
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
CreateQuestionRequest:
properties:
modality:
type: string
enum:
# --- truncated at 32 KB (38 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/heymilo/refs/heads/main/openapi/heymilo-questions-api-openapi.yml