Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: JobMojito Resume & Form verification API
version: 1.0.0
description: Public API for JobMojito, served by Supabase Edge Functions. Authenticate with a Supabase JWT access token via the Authorization header.
servers:
- url: https://cool.jobmojito.com/functions/v1
description: Production
tags:
- name: Resume & Form verification
description: Pre-screen candidates from resumes and forms.
paths:
/pre-screening-create:
post:
tags:
- Resume & Form verification
summary: Create or update a pre-screening position
description: 'Creates a new pre-screening position (and optionally activates it) or, when update_position_def_set_id is provided, updates an existing pre-screening position. Only merchant_owner, merchant, or admin users may call this.
The screening behaviour is driven by two related arrays:
- `assessment_rules` — the rules enforced on each candidate. Only rules with `type: "screening"` (plus `resume_match` / `resume_technical_experience`) actually pass/reject; other entries just declare collected fields.
- `form_fields` — the candidate-facing form (standard field references and custom fields).
For `form` and `resume_with_form` types, enable a standard field in BOTH arrays: add the rule to `assessment_rules` and a matching `{ id, mandatory }` entry to `form_fields`. `resume`-only positions use `assessment_rules` (resume_* rules) and ignore `form_fields`. Both arrays are replaced wholesale on update — send the complete array, not a delta.'
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PreScreeningCreateRequest'
responses:
'200':
description: The pre-screening position was created or updated.
content:
application/json:
schema:
$ref: '#/components/schemas/PreScreeningCreateResponse'
'401':
description: Missing, expired or invalid access token.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: Your token is not allowed to use this endpoint (it is not a merchant, merchant owner or admin token).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Pre-screening step not found for the given position (update path).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Validation error or unresolved merchant_id.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: upsert_pre_screening
/job-interview-pre-screening-api-resume-text:
post:
tags:
- Resume & Form verification
summary: Run pre-screening on a plaintext resume
description: Stores a plaintext candidate resume, runs AI pre-screening against the position definition, records the result and returns the AI recommendation and analysis. If the candidate is not in the pre-screening candidate_action state, an early `{ decision_status, message }` object is returned with HTTP 200.
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PreScreeningResumeTextRequest'
responses:
'200':
description: Pre-screening completed, or an early `{ decision_status, message }` result when the candidate is not in the pre-screening candidate_action state.
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/PreScreeningResumeTextResponse'
- $ref: '#/components/schemas/PreScreeningNotApplicable'
'401':
description: Missing or invalid authentication.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: Authenticated user is not a merchant user.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Position not found or does not belong to this merchant.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Validation error (missing required field or unresolved merchant_id).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: pre_screen_resume_text
/job-interview-pre-screening-api-resume-binary:
post:
tags:
- Resume & Form verification
summary: Pre-screen a candidate from a binary resume upload
description: Accepts a multipart/form-data upload with candidate details and a binary resume file, converts the resume to text, runs AI pre-screening against the position, stores the result, and returns the decision.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/PreScreeningResumeBinaryRequest'
responses:
'200':
description: Pre-screening result, or an early-exit status message.
content:
application/json:
schema:
$ref: '#/components/schemas/PreScreeningResumeBinaryResponse'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: 'Forbidden: only merchant users can access this endpoint.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Position not found or does not belong to this merchant.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Validation error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: pre_screen_resume_binary
components:
schemas:
PreScreeningNotApplicable:
type: object
properties:
decision_status:
type: string
example: unknown
message:
type: string
required:
- decision_status
- message
PreScreeningFormField:
type: object
properties:
id:
type: string
description: Field id. Standard fields reuse the rule id (e.g. form_education). Custom fields use custom_<timestamp>.
example: custom_1772004521633
mandatory:
type: boolean
description: 'Standard fields: whether the candidate must answer. Derived from the matching rule type (required/screening → true, optional → false).'
title:
type: string
description: 'Custom fields: the question/label shown to the candidate.'
example: Do you hold a valid work permit?
description:
type: string
description: 'Custom fields: optional helper text shown under the title.'
type:
type: string
enum:
- optional
- required
- screening
description: 'Custom fields: "required" or "optional" (custom fields are never "screening").'
example: required
field_type:
type: string
enum:
- text
- file
- radio
description: 'Custom fields: input widget — "text" (free text), "file" (upload) or "radio" (single choice from `options`).'
example: radio
options:
type: array
items:
type: string
description: 'Custom fields: the choices when field_type is "radio" (at least 2 required).'
example:
- 'Yes'
- 'No'
required:
- id
description: 'A single candidate-form field: either a standard field reference or a custom field.'
PreScreeningResumeTextRequest:
type: object
properties:
position_id:
type: string
minLength: 1
description: Position (position_def_set) id the candidate is being screened against.
example: 00000000-0000-0000-0000-000000000000
candidate_name:
type: string
minLength: 1
description: Candidate full name.
example: Jane Doe
candidate_email:
type: string
minLength: 1
description: Candidate email (normalised to trimmed lowercase).
example: jane.doe@example.com
candidate_country_code:
type: string
description: Optional ISO country code, used as the default residency/nationality when those are not supplied in `form`. If the position has a `form_nationality` or `form_residency` screening rule and neither this field nor the corresponding `form` value is provided, the candidate is screened with no nationality/residency and will fail that rule (marked for review or rejected per its action).
example: US
candidate_resume:
type: string
minLength: 1
description: Plaintext candidate resume.
example: Experienced software engineer with 8 years ...
candidate_external_id:
type: string
description: Optional external identifier stored on the candidate profile.
example: ATS-12345
candidate_linkedin_url:
type: string
description: Optional LinkedIn profile URL stored on the candidate profile.
example: https://www.linkedin.com/in/jane-doe
form:
type: object
properties:
education:
type: string
description: 'Candidate''s highest education level. Must be one of the education keys used by the position''s form_education rule: "primary school", "high school no graduation", "high school", "vocational", "bachelors", "masters", "doctorate".'
example: masters
languages:
type: array
items:
type: object
properties:
code:
type: string
description: Platform language code.
example: en
level:
type: string
description: 'Proficiency level key: "beginner", "intermediate", "advanced" or "fluent".'
example: advanced
description: Candidate language proficiencies, matched against the position's form_languages rule.
residency:
type: string
description: ISO country code of residency. Defaults to candidate_country_code when omitted.
example: US
nationality:
type: string
description: ISO country code of nationality. Defaults to candidate_country_code when omitted.
example: US
visa:
type: string
description: Visa key, matched against the position's form_visa allowlist (keys are country-specific).
example: 18_transferable
age:
type: integer
description: Candidate age in years.
example: 30
gender:
type: string
description: 'Candidate gender key (standard values: male, female, other).'
example: male
additionalProperties:
nullable: true
description: Optional structured pre-screening form used by the assessment rules. Custom fields (defined via form_fields) are also accepted.
required:
- position_id
- candidate_name
- candidate_email
- candidate_resume
PreScreeningCreateResponse:
type: object
properties:
created:
type: boolean
description: True when a new pre-screening position was created.
updated:
type: boolean
description: True when an existing pre-screening position was updated.
position_def_set_id:
type: string
nullable: true
description: The position_def_set id created or updated.
interview_pre_screening_id:
type: string
nullable: true
description: The pre-screening definition id created or updated.
description: 'On create returns `{ created: true, position_def_set_id, interview_pre_screening_id }`; on update returns `{ updated: true, position_def_set_id, interview_pre_screening_id }`.'
PreScreeningResumeBinaryRequest:
type: object
properties:
position_id:
type: string
minLength: 1
description: The position the candidate is being pre-screened against.
candidate_name:
type: string
minLength: 1
description: Full name of the candidate.
candidate_email:
type: string
minLength: 1
description: Candidate email (normalized to lowercase, trimmed).
candidate_country_code:
type: string
description: Optional ISO country code used as the residency/nationality fallback when those are not supplied in `form`. If the position has a `form_nationality` or `form_residency` screening rule and neither this field nor the corresponding `form` value is provided, the candidate is screened with no nationality/residency and will fail that rule (marked for review or rejected per its action).
candidate_external_id:
type: string
description: Optional external identifier for the candidate.
candidate_linkedin_url:
type: string
description: Optional candidate LinkedIn profile URL.
file:
type: string
description: Resume file (pdf, doc, docx, txt, etc.)
format: binary
form:
type: string
description: 'Optional JSON string with the candidate form answers, same shape as the `form` object of job-interview-pre-screening-api-resume-text: education (level key), languages ([{ code, level }]), residency/nationality (ISO codes), visa (key), age (int), gender (key). Custom fields (custom_<timestamp>) may also be included.'
example: '{"education":"masters","languages":[{"code":"en","level":"fluent"}],"residency":"US"}'
required:
- position_id
- candidate_name
- candidate_email
- file
Error:
type: object
properties:
error:
type: string
example: Field is required.
name:
type: string
example: interview_result_id
required:
- error
PreScreeningCreateRequest:
type: object
properties:
update_position_def_set_id:
type: string
nullable: true
description: When set to an existing position id, the request updates that pre-screening position instead of creating a new one. When absent/empty, a new pre-screening position is created and the create-only mandatory fields are required.
example: 00000000-0000-0000-0000-000000000000
position_name:
type: string
nullable: true
description: Position name. Required when creating (no update_position_def_set_id).
example: Customer Support Specialist
position_location:
type: string
nullable: true
description: Interview/position location. Required when creating.
example: Remote
position_country_code:
type: string
nullable: true
description: ISO country code; stored upper-cased. Required when creating.
example: US
position_description:
type: string
nullable: true
description: Short position description.
example: Handle inbound customer requests.
position_description_long:
type: string
nullable: true
description: Long position description.
mojito_language_code:
type: string
nullable: true
enum:
- ar
- bg
- zh
- hr
- cs
- da
- nl
- en
- fil
- fi
- fr
- de
- el
- hi
- hu
- id
- it
- ja
- ko
- ms
- 'no'
- pl
- pt
- br
- ro
- ru
- sk
- es
- sv
- ta
- th
- tr
- uk
- vi
description: Mojito language code (one of the listed codes). Required when creating.
example: en
merchant_id:
type: string
nullable: true
description: Target merchant id. Only honored for admin or sub-merchant users; otherwise the caller's own merchant is used.
type:
type: string
nullable: true
enum:
- resume
- form
- resume_with_form
description: Pre-screening type. Defaults to "resume" on create.
status:
type: string
nullable: true
enum:
- draft
- active
description: When "active", the pre-screening position is activated after create/update.
assessment_rules:
type: array
nullable: true
items:
$ref: '#/components/schemas/PreScreeningAssessmentRule'
description: 'Ordered array of assessment rules. Only rules with `type: "screening"` (plus resume_match / resume_technical_experience) are enforced; other entries just declare which fields the form collects. Keep in sync with `form_fields`: a standard form field enabled here should have a matching `{ id, mandatory }` entry in form_fields.'
example:
- id: form_education
type: screening
action: reject
education: bachelors
- id: form_languages
type: screening
action: mark_for_review
languages:
- code: en
level: advanced
- id: form_nationality
type: required
- id: resume_match
resume_score_reject: 2
resume_score_accept: 7
- id: resume_technical_experience
action: mark_for_review
technical_experience_years: 3
form_fields:
type: array
nullable: true
items:
$ref: '#/components/schemas/PreScreeningFormField'
description: 'Array describing the candidate form: standard field references ({ id, mandatory }) and/or custom fields ({ id: "custom_…", title, field_type, … }). For `form` and `resume_with_form` types at least one entry is required.'
example:
- id: form_education
mandatory: true
- id: form_languages
mandatory: true
- id: form_nationality
mandatory: true
- id: custom_1772004521633
title: Do you hold a valid work permit?
type: required
field_type: radio
options:
- 'Yes'
- 'No'
candidate_expectations:
type: string
nullable: true
description: Candidate expectations text.
visibility:
type: string
nullable: true
enum:
- merchant_public
- merchant_invite
- merchant_unlisted
description: 'Position visibility. Defaults to `merchant_invite` on create. Options — `merchant_public`: Listed on the merchant''s public interview list — anyone with the merchant link can find and start it. | `merchant_invite`: Invite-only — only candidates explicitly invited (by email/link) can access it; not listed anywhere. | `merchant_unlisted`: Reachable only via a direct link — not listed anywhere; share the link manually.'
example: merchant_public
kombo_parameters:
nullable: true
description: Kombo integration parameters (any JSON).
example:
kombo_job_id: 7QzbCBaxX6fa4BMXBRuD7Lwm
kombo_stage_id: CVjhZwxbtcer5P15gakmEhsM
kombo_sync_all_results: true
kombo_selected_stage_id: null
kombo_sync_all_results_stage_id: 9c5VZXrcMRmyr5vr1nvZWXSX
interview_available_till:
type: string
nullable: true
description: Timestamp until which the interview is available.
creation_parameters:
nullable: true
description: Creation parameters, any JSON (create only).
PreScreeningResumeTextResponse:
type: object
properties:
success:
type: boolean
example: true
decision_status:
type: string
description: Applied decision status for the pre-screening step.
example: recruiter_action
recommendation:
type: string
enum:
- ai_accept
- ai_reject
- recruiter_action
description: AI recommendation derived from the pre-screening status.
example: recruiter_action
profile_interview_id:
type: string
description: Candidate profile_interview id.
interview_result_pre_screening_id:
type: string
description: Inserted interview_result_pre_screening id.
interview_file_id:
type: string
description: Stored resume interview_file id.
pre_screening_score:
type: number
nullable: true
description: AI match score.
resume_ai_analysis:
type: string
nullable: true
description: AI resume analysis feedback.
resume_ai_education:
type: string
nullable: true
description: AI-detected education level of the candidate.
resume_ai_technical_experience:
type: number
nullable: true
description: AI-estimated years of relevant experience.
resume_ai_is_switching_profession:
type: boolean
nullable: true
description: Whether the candidate is switching profession.
processing_reason:
type: string
nullable: true
description: Reason the candidate failed screening, when applicable.
required:
- success
- decision_status
- recommendation
- profile_interview_id
- interview_result_pre_screening_id
- interview_file_id
- pre_screening_score
- resume_ai_analysis
- resume_ai_education
- resume_ai_technical_experience
- resume_ai_is_switching_profession
- processing_reason
description: Result of the pre-screening analysis. When the candidate is not in the pre-screening candidate_action state, a `{ decision_status, message }` object is returned instead.
PreScreeningResumeBinarySuccess:
type: object
properties:
success:
type: boolean
decision_status:
type: string
recommendation:
type: string
nullable: true
profile_interview_id:
type: string
interview_result_pre_screening_id:
type: string
interview_file_id:
type: string
pre_screening_score:
type: number
nullable: true
resume_ai_analysis:
type: string
nullable: true
resume_ai_education:
type: string
nullable: true
resume_ai_technical_experience:
type: number
nullable: true
resume_ai_is_switching_profession:
type: boolean
nullable: true
processing_reason:
type: string
nullable: true
required:
- success
- decision_status
- recommendation
- profile_interview_id
- interview_result_pre_screening_id
- interview_file_id
- pre_screening_score
- resume_ai_analysis
- resume_ai_education
- resume_ai_technical_experience
- resume_ai_is_switching_profession
- processing_reason
PreScreeningResumeBinaryEarlyExit:
type: object
properties:
decision_status:
type: string
message:
type: string
required:
- decision_status
- message
PreScreeningResumeBinaryResponse:
anyOf:
- $ref: '#/components/schemas/PreScreeningResumeBinarySuccess'
- $ref: '#/components/schemas/PreScreeningResumeBinaryEarlyExit'
description: Either the completed pre-screening result, or an early-exit status message when the candidate is not in the pre-screening candidate_action state.
PreScreeningAssessmentRule:
type: object
properties:
id:
type: string
description: 'Rule identifier. Standard form rules: form_nationality, form_residency, form_languages, form_education (or form_education_by_country), form_visa, form_age, form_gender, form_cover_letter, form_national_id. Standard resume rules: resume_match, resume_technical_experience, resume_education (or resume_education_by_country). Custom fields use custom_<timestamp>.'
example: form_education
type:
type: string
enum:
- optional
- required
- screening
description: How the field is treated. "screening" = the rule is enforced and can pass / mark-for-review / reject the candidate. "required" / "optional" only affect whether the candidate must fill the field — they are NOT enforced as screening. resume_match and resume_technical_experience ignore this key (always enforced).
example: screening
action:
type: string
enum:
- mark_for_review
- reject
description: Outcome applied when a screening rule is not satisfied. Defaults to "mark_for_review".
example: reject
country:
type: array
items:
type: string
description: 'form_nationality / form_residency: allowlist of ISO country codes. Combined with country_eu as a logical OR.'
example:
- US
- CA
country_eu:
type: boolean
description: 'form_nationality / form_residency: when true, any EU country is accepted (in addition to `country`).'
languages:
type: array
items:
type: object
properties:
code:
type: string
description: Platform language code.
example: en
level:
type: string
enum:
- beginner
- intermediate
- advanced
- fluent
description: Minimum required proficiency.
example: advanced
required:
- code
- level
description: 'form_languages: each listed language must be known at (at least) the given level.'
education:
type: string
description: 'form_education / resume_education: minimum acceptable education level. Standard keys: ["primary school","high school no graduation","high school","vocational","bachelors","masters","doctorate"]. Country-specific rules (form_education_by_country / resume_education_by_country) use the keys from that country''s list.'
example: bachelors
visa:
type: array
items:
type: string
description: 'form_visa: allowlist of visa keys. Keys are country-specific (only defined for some countries, e.g. KW, TR).'
age_min:
type: integer
description: 'form_age: minimum acceptable age (inclusive).'
example: 18
age_max:
type: integer
description: 'form_age: maximum acceptable age (inclusive).'
example: 60
gender:
type: array
items:
type: string
description: 'form_gender: allowlist of gender keys. Standard keys: male, female, other.'
example:
- female
technical_experience_years:
type: integer
description: 'resume_technical_experience: minimum years of relevant experience the AI must detect in the resume.'
example: 3
resume_score_reject:
type: integer
minimum: 0
maximum: 10
description: 'resume_match: reject the candidate when the AI match score (0-10) is below this value. 0 disables the reject threshold.'
example: 2
resume_score_accept:
type: integer
minimum: 0
maximum: 10
description: 'resume_match: below this value (but at/above resume_score_reject) the candidate is marked for review; at/above it the candidate passes.'
example: 7
required:
- id
description: A single pre-screening assessment rule. The keys used depend on `id` (see each key's description).
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: 'Supabase JWT — `Authorization: Bearer <token>`.'