{
  "openapi": "3.0.3",
  "info": {
    "title": "JobAffinity REST API",
    "version": "1.20",
    "description": "JobAffinity REST API for managing jobs, candidates, applications, and recruitment processes.\n\n## Authentication\nAll endpoints require HTTP Basic Authentication.\n\n## Conventions\n- **Validation errors are answered with HTTP 200** and `{\"_status\": \"INVALID\", \"invalid\": {...}}` (see the `InvalidResponse` schema and the Responses guide), not with an HTTP error code.\n- **Pagination**: list endpoints return pages of a fixed 100 items. Follow `_next`; it is an empty string on the last page.\n- **Unknown query parameters**: list endpoints answer `_status: INVALID`; detail endpoints ignore them.\n- **Dates** are `YYYY-MM-DD`. **Timestamps** are `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time), unless stated otherwise.\n- `_self`, `_schema`, `_next` and the other resource links are root-relative paths (`/restapi/v1/...`).\n- Empty text fields are returned as `\"\"`; empty email fields as `null`.\n\n## Versioning\nCurrent version: 1.20\n\nSee `/restapi/changelog` for version history and changes.\n",
    "contact": {
      "name": "JobAffinity API Support"
    }
  },
  "servers": [
    {
      "url": "https://jobaffinity.fr/restapi/v1",
      "description": "REST API v1"
    },
    {
      "url": "https://jobaffinity.fr/restapi",
      "description": "REST API root (for changelog)"
    }
  ],
  "security": [
    {
      "basicAuth": []
    }
  ],
  "tags": [
    {
      "name": "API Info",
      "description": "API metadata and changelog"
    },
    {
      "name": "Account",
      "description": "Authenticated user identity and organisation membership"
    },
    {
      "name": "Jobs",
      "description": "Job listings and details"
    },
    {
      "name": "Candidates",
      "description": "Candidate information"
    },
    {
      "name": "Applications",
      "description": "Job applications"
    },
    {
      "name": "Processes",
      "description": "Recruitment processes and steps"
    },
    {
      "name": "Notes",
      "description": "Candidate notes"
    },
    {
      "name": "Templates",
      "description": "Recorded email templates"
    },
    {
      "name": "Questionnaires",
      "description": "Questionnaire answers"
    },
    {
      "name": "Webhooks",
      "description": "Webhook tasks"
    },
    {
      "name": "Job Application Submission",
      "description": "Submit candidate applications to a job posting via the apply URL provided in the job feed. The authentication token is embedded in the URL — no additional credentials are required."
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "API Info"
        ],
        "summary": "Get API index",
        "description": "Returns API metadata, version information, and main entry points",
        "operationId": "getApiIndex",
        "responses": {
          "200": {
            "description": "API index information",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_status": {
                      "type": "string",
                      "enum": [
                        "OK"
                      ]
                    },
                    "_version": {
                      "type": "string",
                      "example": "1.20"
                    },
                    "_expiration": {
                      "type": "string",
                      "nullable": true
                    },
                    "_last_version_url": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "_self": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "_changelog": {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        }
                      }
                    },
                    "candidates": {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "description": {
                          "type": "string"
                        }
                      }
                    },
                    "jobs": {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "description": {
                          "type": "string"
                        }
                      }
                    },
                    "steps": {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "description": {
                          "type": "string"
                        }
                      }
                    },
                    "me": {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "description": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/changelog": {
      "servers": [
        {
          "url": "/restapi"
        }
      ],
      "get": {
        "tags": [
          "API Info"
        ],
        "summary": "Get API changelog",
        "description": "Returns version history and changes for each API version",
        "operationId": "getChangelog",
        "responses": {
          "200": {
            "description": "API changelog by version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "1.20": [
                      "/restapi/v1/step: add `organisation` (`id`, `name`, `external_id`), the organisation of the job the step change belongs to.",
                      "`job` sub-object everywhere (/restapi/v1/job, /job/{id}/applications, /candidate/{id}/applications, /application/{id}, /step): add `client_external_id`, the identifier of the job client, null when there is none. Purely additive."
                    ],
                    "1.19": [
                      "/restapi/v1/step/{change_id}/cancel(POST): new. Cancels the latest step change of an application.",
                      "/restapi/v1/step(POST), /application/{id}/advance-step and /application/{id}/reject: add `change_id`, the identifier of the step change they created."
                    ],
                    "1.18": [
                      "/restapi/v1/me/validation-inbox: new."
                    ],
                    "1.17": [
                      "/restapi/v1/process and /restapi/v1/process/{id}: add `companion`, the companion app configuration of the process.",
                      "/restapi/v1/application/{id}/reject(POST): answers 400 instead of 404 when no enabled one-click reject configuration exists for the application."
                    ],
                    "1.16": [
                      "/restapi/v1/job/{id}: add `organisation.external_id` and `client_external_id`, the identifiers of the organisation and of the job client. Both are null when there is none. Purely additive."
                    ],
                    "1.15": [
                      "/restapi/v1/step?user_id=42: new. Filter the feed to the changes authored by one user.",
                      "/restapi/v1/step?since=2026-09-01: new. Filter the feed to changes whose step started on or after this date (day granularity)."
                    ],
                    "1.14": [
                      "/restapi/v1/me: `organisations` entries gain `role_label`, the canonical English name of the numeric `role`. `role` stays the numeric identifier. Purely additive."
                    ],
                    "1.13": [
                      "/restapi/v1/me: new. Identity of the authenticated user.",
                      "/restapi/v1/application/{id}/steps: new.",
                      "/restapi/v1/job/{id}/stats: new."
                    ],
                    "1.12": [
                      "/restapi/v1/application/{id}/reject(POST): new. One-click reject an application."
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/process/": {
      "get": {
        "tags": [
          "Processes"
        ],
        "summary": "List all processes",
        "description": "Returns every visible recruitment process. Not paginated.",
        "operationId": "listProcesses",
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "description": "List the processes of this organisation; also narrows each process's `organisations` to it.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "step_status",
            "in": "query",
            "description": "Narrow the embedded `steps` to this status. Does not filter processes: a process without a matching step comes back with empty `steps`.",
            "schema": {
              "type": "string",
              "enum": [
                "START",
                "CURRENT",
                "END"
              ]
            }
          },
          {
            "name": "step_name",
            "in": "query",
            "description": "Narrow the embedded `steps` to this exact name. Does not filter processes: a process without a matching step comes back with empty `steps`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of processes",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Process"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/process/{processid}": {
      "get": {
        "tags": [
          "Processes"
        ],
        "summary": "Get process by ID",
        "description": "Returns details of a specific recruitment process",
        "operationId": "getProcess",
        "parameters": [
          {
            "name": "processid",
            "in": "path",
            "required": true,
            "description": "Process ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Process details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/job": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "List all jobs",
        "description": "Returns the jobs whose team includes the authenticated user. Use `alljobs=1` to return every job visible to the user. Default jobs (spontaneous applications) are never listed.",
        "operationId": "listJobs",
        "parameters": [
          {
            "name": "alljobs",
            "in": "query",
            "description": "Set to `1` to return every job visible to the user, not only the jobs whose team includes the user.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Job statuses to list, repeatable (`?status=OPEN&status=DRAFT`). Defaults to OPEN and CLOSED. Case-sensitive.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "ARCHIVED",
                  "CLOSED",
                  "DRAFT",
                  "OPEN",
                  "PENDING_VALIDATION",
                  "REJECTED",
                  "VALIDATED"
                ]
              },
              "default": [
                "OPEN",
                "CLOSED"
              ]
            },
            "style": "form",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of jobs",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/JobListItem"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/job/{jobid}": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "Get job by ID",
        "description": "Returns detailed information about a specific job",
        "operationId": "getJob",
        "parameters": [
          {
            "name": "jobid",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/job/{jobid}/team": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "Get job team",
        "description": "Returns the visible members of the job team, ordered by last name, first name. Not paginated.",
        "operationId": "getJobTeam",
        "parameters": [
          {
            "name": "jobid",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job team information",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_self": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "_schema": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "_status": {
                      "type": "string",
                      "enum": [
                        "OK"
                      ]
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/job/{jobid}/application": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "List applications for a job",
        "description": "Returns all applications submitted for a specific job. Applications on default jobs (spontaneous applications) are not listed.",
        "operationId": "listJobApplications",
        "parameters": [
          {
            "name": "jobid",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "step_name",
            "in": "query",
            "description": "Filter applications by the name of their current step. Valid step names can be found in the `process` field of the job details.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "step_status",
            "in": "query",
            "description": "Filter applications by their current step status.",
            "schema": {
              "type": "string",
              "enum": [
                "START",
                "CURRENT",
                "END"
              ]
            }
          },
          {
            "name": "step",
            "in": "query",
            "description": "Filter applications by the id of their current step. Valid step ids can be found in the `process` field of the job details.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "last_step_since",
            "in": "query",
            "description": "Filter applications whose last step change occurred on or after this date (inclusive). ISO 8601 format: YYYY-MM-DD. Compared with the date the application entered its current step.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "last_step_until",
            "in": "query",
            "description": "Filter applications whose last step change occurred on or before this date (inclusive). ISO 8601 format: YYYY-MM-DD. Compared with the date the application entered its current step.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "last_name",
            "in": "query",
            "description": "Filter applications by the candidate's last name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "first_name",
            "in": "query",
            "description": "Filter applications by the candidate's first name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "description": "Filter applications by the candidate's email address. Must be a valid address, otherwise `_status: INVALID`. Exact match.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of applications",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ApplicationListItem"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/application/{applicationid}": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Get application by ID",
        "description": "Returns detailed information about a specific application",
        "operationId": "getApplication",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Application details",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Application"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/application/{applicationid}/latest_cv": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Download latest CV for application",
        "description": "Returns the latest CV document of this application (no fallback to the candidate's other applications). Depending on the storage backend it either redirects (302) to a presigned URL valid one hour, or streams the file (200).\n\n**IMPORTANT:** Do not build this URL manually. Use the `latest_cv_url` field from application objects.",
        "operationId": "getApplicationLatestCv",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Application not visible, or no CV."
          },
          "302": {
            "description": "Redirect to a temporary download URL (object storage backend).",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "200": {
            "description": "The CV file (filesystem backend).",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/application/{applicationid}/profile": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Get candidate profile for an application",
        "description": "Returns the candidate's structured profile (identity, employments, educations, languages, skills, hobbies) as extracted from their CV. Same data as the profile tab in the UI.",
        "operationId": "getApplicationProfile",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Candidate profile",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ApplicationProfile"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/application/{applicationid}/reject": {
      "post": {
        "tags": [
          "Applications"
        ],
        "summary": "Reject an application (one-click)",
        "description": "Moves the application to the configured rejection step and optionally sends a rejection email. Requires a OneClickRejectConfig to be enabled for the application's process.\n\nThe rejection step and email template are determined by the organisation's one-click reject configuration.",
        "operationId": "rejectApplication",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Application rejected successfully",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "application_id": {
                          "type": "integer",
                          "description": "ID of the rejected application"
                        },
                        "step_id": {
                          "type": "integer",
                          "description": "ID of the rejection step"
                        },
                        "step_name": {
                          "type": "string",
                          "description": "Name of the rejection step"
                        },
                        "email_sent": {
                          "type": "boolean",
                          "description": "Whether a rejection email was sent or scheduled"
                        },
                        "change_id": {
                          "type": "integer",
                          "description": "Identifier of the step change created."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Application not found, or not editable by the caller."
          },
          "409": {
            "description": "Application is already on an END step (already rejected or archived)"
          },
          "400": {
            "description": "No enabled one-click reject configuration for this application."
          }
        }
      }
    },
    "/candidate": {
      "get": {
        "tags": [
          "Candidates"
        ],
        "summary": "List all candidates",
        "description": "Returns a paginated list of all visible candidates (CVtheque). By default, all candidates visible to the user are listed.",
        "operationId": "listCandidates",
        "parameters": [
          {
            "name": "first_name",
            "in": "query",
            "description": "Search candidates by first name. Exact match.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "last_name",
            "in": "query",
            "description": "Search candidates by last name. Exact match.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "description": "Search candidates by email address. Searches both primary email and secondary email. Must be a valid address, otherwise `_status: INVALID`. Exact match.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "pool_name",
            "in": "query",
            "description": "Search candidates by pool (vivier) name. Can be specified multiple times for AND logic (candidate must be in all specified pools). Use the pipe character `|` within a single value for OR logic (e.g. `pool_name=ios|android`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of candidates",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CandidateListItem"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/candidate/{candidateid}": {
      "get": {
        "tags": [
          "Candidates"
        ],
        "summary": "Get candidate by ID",
        "description": "Returns detailed information about a specific candidate",
        "operationId": "getCandidate",
        "parameters": [
          {
            "name": "candidateid",
            "in": "path",
            "required": true,
            "description": "Candidate ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Candidate details",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Candidate"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/candidate/{candidateid}/application": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "List applications for a candidate",
        "description": "Returns all applications submitted by a specific candidate. Applications on default jobs (spontaneous applications) are not listed.",
        "operationId": "listCandidateApplications",
        "parameters": [
          {
            "name": "candidateid",
            "in": "path",
            "required": true,
            "description": "Candidate ID",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "step_name",
            "in": "query",
            "description": "Filter applications by the name of their current step. Valid step names can be found in the `process` field of the job details.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "step_status",
            "in": "query",
            "description": "Filter applications by their current step status.",
            "schema": {
              "type": "string",
              "enum": [
                "START",
                "CURRENT",
                "END"
              ]
            }
          },
          {
            "name": "step",
            "in": "query",
            "description": "Filter applications by the id of their current step. Valid step ids can be found in the `process` field of the job details.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "last_step_since",
            "in": "query",
            "description": "Filter applications whose last step change occurred on or after this date (inclusive). ISO 8601 format: YYYY-MM-DD. Compared with the date the application entered its current step.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "last_step_until",
            "in": "query",
            "description": "Filter applications whose last step change occurred on or before this date (inclusive). ISO 8601 format: YYYY-MM-DD. Compared with the date the application entered its current step.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "job_title",
            "in": "query",
            "description": "Filter applications by the job title.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "job_status",
            "in": "query",
            "description": "Filter applications by the job status.",
            "schema": {
              "type": "string",
              "enum": [
                "ARCHIVED",
                "CLOSED",
                "DRAFT",
                "OPEN",
                "PENDING_VALIDATION",
                "REJECTED",
                "VALIDATED"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of applications",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ApplicationListItem"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/note": {
      "get": {
        "tags": [
          "Notes"
        ],
        "summary": "List all notes",
        "description": "Returns a paginated list of candidate notes, from most recently added to oldest, across all candidates and organisations. Use `candidate_id` or `organisation_id` to filter. You can use the URI provided by the `.notes._self` field from the application detail resource.",
        "operationId": "listNotes",
        "parameters": [
          {
            "name": "candidate_id",
            "in": "query",
            "description": "Filter notes for a specific candidate. This ID is provided by the `.id` field of the candidate detail resource.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "organisation_id",
            "in": "query",
            "description": "Filter notes for a specific organisation.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of notes",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Note"
                          }
                        },
                        "_actions": {
                          "type": "array",
                          "description": "`[{\"_self\": \"/restapi/v1/note\", \"doc\": \"Add a new note\", \"method\": \"POST\"}]`",
                          "items": {
                            "type": "object",
                            "properties": {
                              "_self": {
                                "type": "string"
                              },
                              "doc": {
                                "type": "string"
                              },
                              "method": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      },
      "post": {
        "tags": [
          "Notes"
        ],
        "summary": "Add a new note",
        "description": "Creates a new note for a candidate",
        "operationId": "addNote",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoteInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Note created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/step": {
      "get": {
        "tags": [
          "Processes"
        ],
        "summary": "List all process step changes",
        "description": "Returns a paginated list of all process step changes, most recent first (ordered by descending `change_id`). Pages hold a fixed 100 items; there is no page-size parameter. Follow `_next` to read the next page; it is an empty string on the last page. Invalid query parameters (e.g. a `since` that is not a date) are answered with `_status: INVALID` and the validation errors in the body, not with an HTTP error code. Applications on default jobs are never listed.",
        "operationId": "listApplicationProcesses",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "description": "Filter to changes whose step started on or after this date (day granularity). This is a date, not a change_id.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-01-31"
            }
          },
          {
            "$ref": "#/components/parameters/organisationFilterParam"
          },
          {
            "name": "user_id",
            "in": "query",
            "description": "Filter to the changes authored by this user",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "candidate_email",
            "in": "query",
            "description": "Exact match on the candidate's primary email (no format check).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "job_reference",
            "in": "query",
            "description": "Filter by job reference",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "step_status",
            "in": "query",
            "description": "Filter by step status",
            "schema": {
              "type": "string",
              "enum": [
                "START",
                "CURRENT",
                "END"
              ]
            }
          },
          {
            "name": "step_name",
            "in": "query",
            "description": "Filter by step name",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of step changes",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ApplicationProcess"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "description": "The caller is not a member of the organisation given in organisation_id"
          }
        }
      },
      "post": {
        "tags": [
          "Processes"
        ],
        "summary": "Add a new process step",
        "description": "Adds a new process step change to an application",
        "operationId": "addApplicationProcess",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApplicationProcessInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Process step added successfully",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "change_id": {
                          "type": "integer",
                          "description": "Identifier of the step change created (use it to cancel)."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/application/{applicationid}/questionnaire/{questionnaireid}": {
      "get": {
        "tags": [
          "Questionnaires"
        ],
        "summary": "Get questionnaire answers",
        "description": "Returns answers to a specific questionnaire for an application",
        "operationId": "getQuestionnaireAnswers",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "questionnaireid",
            "in": "path",
            "required": true,
            "description": "Questionnaire ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Questionnaire answers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_self": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "_schema": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "_status": {
                      "type": "string",
                      "enum": [
                        "OK"
                      ]
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QuestionnaireSubmission"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError",
            "description": "Application not visible, or questionnaire not in one of the caller's organisations."
          }
        }
      }
    },
    "/application/{appid}/questionnaire": {
      "post": {
        "tags": [
          "Questionnaires"
        ],
        "summary": "Submit questionnaire answers",
        "description": "Submits answers to a questionnaire for an application",
        "operationId": "postQuestionnaireAnswers",
        "parameters": [
          {
            "name": "appid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuestionnaireAnswersInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Questionnaire answers submitted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_self": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "The answers of this questionnaire."
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Get the authenticated user",
        "description": "Returns the identity of the authenticated user, one entry per membership in `organisations`, and `organisation_ids`.",
        "operationId": "getMe",
        "responses": {
          "200": {
            "description": "Authenticated user identity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/organisation/{organisationid}/users": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "List an organisation's members",
        "description": "Returns the members of an organisation, one entry per user, ordered by user id. The role kept is the first of administrator, recruiter, manager, operational, then the lowest other role id; members whose only membership is disabled (10) or deleted (11) are still listed. The caller must be an administrator, recruiter or manager of the organisation (or a superadmin, who gets empty `items` for an unknown organisation).",
        "operationId": "listOrganisationUsers",
        "parameters": [
          {
            "name": "organisationid",
            "in": "path",
            "required": true,
            "description": "Organisation ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organisation members",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_status": {
                      "type": "string",
                      "enum": [
                        "OK"
                      ]
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OrganisationMember"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "description": "The caller is not an administrator, recruiter or manager of this organisation."
          }
        }
      }
    },
    "/candidate/lookup": {
      "get": {
        "tags": [
          "Candidates"
        ],
        "summary": "Look up candidates by email or phone",
        "description": "Finds visible candidates by normalized email or phone. The email is lowercased and trimmed; the phone is parsed to E.164. At least one criterion must survive normalization, otherwise the answer is 400. Criteria that could not be normalized are listed in `rejected_criteria`, and each item reports which criteria it matched in `matched_on`.",
        "operationId": "lookupCandidates",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "description": "Email to look up. Only trimmed and lowercased (no format check); a blank value is rejected.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "phone",
            "in": "query",
            "description": "Phone to look up. Parsed to E.164 before matching.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching candidates (up to 50)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_status": {
                      "type": "string",
                      "enum": [
                        "OK"
                      ]
                    },
                    "_self": {
                      "type": "string",
                      "format": "uri-reference"
                    },
                    "normalized_email": {
                      "type": "string",
                      "nullable": true,
                      "description": "The email after normalization, or null when it could not be normalized."
                    },
                    "normalized_phone": {
                      "type": "string",
                      "nullable": true,
                      "description": "The phone after E.164 parsing, or null when it could not be parsed."
                    },
                    "rejected_criteria": {
                      "type": "array",
                      "description": "Criteria that were supplied but could not be normalized.",
                      "items": {
                        "type": "string",
                        "enum": [
                          "email",
                          "phone"
                        ]
                      }
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CandidateLookupItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No criterion given, or none survived normalization."
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/application/{applicationid}/email/{emailid}": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Get one archived email of an application",
        "description": "Returns the body, recipients and attachments of a single archived email attached to the application.",
        "operationId": "getApplicationEmail",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "emailid",
            "in": "path",
            "required": true,
            "description": "Archived email ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Archived email detail",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ApplicationEmail"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "No such email on this application"
          }
        }
      }
    },
    "/message-template": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "List an organisation's email templates",
        "description": "Returns the email templates (not SMS) of one organisation, ordered by name. The caller must belong to the organisation.",
        "operationId": "listMessageTemplates",
        "parameters": [
          {
            "name": "organisation_id",
            "in": "query",
            "required": true,
            "description": "Organisation whose templates are listed.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Optional category filter. An empty value means no filter.",
            "schema": {
              "type": "string",
              "enum": [
                "gdpr",
                "acknowledgement",
                "validation",
                "validation_multiple"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Email templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_status": {
                      "type": "string",
                      "enum": [
                        "OK"
                      ]
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MessageTemplate"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "organisation_id is missing or not an integer."
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "description": "The caller does not belong to this organisation"
          }
        }
      }
    },
    "/application/{applicationid}/send-email": {
      "post": {
        "tags": [
          "Applications"
        ],
        "summary": "Send a recorded template on an application",
        "description": "Sends a recorded email template on the application, as the recruiter making the call. Optional `subject` and `message` replace the template's subject and body; keywords already rendered stay as sent. A template of another organisation answers 404. The email is queued, not sent synchronously.",
        "operationId": "sendApplicationEmail",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "template_id"
                ],
                "properties": {
                  "template_id": {
                    "type": "integer",
                    "description": "Template to send. Must belong to the application's organisation."
                  },
                  "subject": {
                    "type": "string",
                    "description": "Optional subject override."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional HTML body override."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email sent",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "application_id": {
                          "type": "integer",
                          "description": "ID of the application written to."
                        },
                        "template_id": {
                          "type": "integer",
                          "description": "ID of the template that was sent."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Body is not valid JSON. A missing `template_id` answers 200 `_status: INVALID`."
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Application not visible or not editable (default jobs included), or the template is unknown, of another organisation, or not an email template."
          }
        }
      }
    },
    "/application/{applicationid}/email-preview": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Preview a recorded template on an application",
        "description": "Renders a recorded template on the application — subject and HTML body with the real keywords, exactly as send-email would produce them — without sending anything. Same access rules as send-email. A missing `template_id` answers 200 `_status: INVALID`.",
        "operationId": "previewApplicationEmail",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "template_id",
            "in": "query",
            "required": true,
            "description": "Template to render.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rendered template",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "application_id": {
                          "type": "integer",
                          "description": "ID of the application previewed."
                        },
                        "template_id": {
                          "type": "integer",
                          "description": "ID of the rendered template."
                        },
                        "subject": {
                          "type": "string",
                          "description": "Subject with keywords expanded."
                        },
                        "body": {
                          "type": "string",
                          "description": "HTML body with keywords expanded."
                        },
                        "from_email": {
                          "type": "string",
                          "description": "Address the email would be sent as."
                        },
                        "to_email": {
                          "type": "string",
                          "description": "Candidate address the email would go to."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Application not visible or not editable (default jobs included), or the template is unknown, of another organisation, or not an email template."
          }
        }
      }
    },
    "/validation/{validationid}/remind": {
      "post": {
        "tags": [
          "Applications"
        ],
        "summary": "Remind a pending validation",
        "description": "Re-sends a pending CV validation request to its recorded manager, reusing the organisation's validation template (subject prefixed with 'Relance : ') and the same magic answer link as the original email. Acts as the recruiter making the call.",
        "operationId": "remindValidation",
        "parameters": [
          {
            "name": "validationid",
            "in": "path",
            "required": true,
            "description": "Validation ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reminder sent",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "validation_id": {
                          "type": "integer",
                          "description": "ID of the reminded validation."
                        },
                        "application_id": {
                          "type": "integer",
                          "description": "ID of the application under review."
                        },
                        "sent_to": {
                          "type": "string",
                          "description": "Address the reminder was sent to."
                        },
                        "request_date": {
                          "type": "string",
                          "format": "date",
                          "description": "New request date (today)."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Validation not found, or its application is not editable by the caller."
          },
          "409": {
            "description": "Validation is already answered, archived, or has no recorded recipient"
          }
        }
      }
    },
    "/application/{applicationid}/steps": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Get the process steps of an application",
        "description": "Returns the visible steps of the application's process, its current step, and the deduced next step of the normal flow (first visible step after the current one by rank, never an END step nor an operational step).",
        "operationId": "getApplicationSteps",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Process steps",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ApplicationSteps"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/application/{applicationid}/advance-step": {
      "post": {
        "tags": [
          "Applications"
        ],
        "summary": "Advance an application to its next step",
        "description": "Moves the application, as the recruiter making the call, to `target_step_id` or to the deduced next step when omitted. Requires `expected_current_step_id`: a 409 answers when the application moved in the meantime, so two recruiters cannot silently race. END and operational steps are refused. Parameters may also be sent in the query string, which wins over the body. An explicit target is not checked to be after the current step.",
        "operationId": "advanceApplicationStep",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "expected_current_step_id"
                ],
                "properties": {
                  "expected_current_step_id": {
                    "type": "integer",
                    "description": "The step the caller believes is current. A mismatch answers 409."
                  },
                  "target_step_id": {
                    "type": "integer",
                    "description": "Explicit step to move to. Omit to use the deduced next step."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Application moved",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "application_id": {
                          "type": "integer",
                          "description": "ID of the application moved."
                        },
                        "step_id": {
                          "type": "integer",
                          "description": "ID of the step the application is now on."
                        },
                        "step_name": {
                          "type": "string",
                          "description": "Name of that step."
                        },
                        "rank": {
                          "type": "integer",
                          "description": "Rank of that step."
                        },
                        "change_id": {
                          "type": "integer",
                          "description": "Identifier of the step change created."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Body is not valid JSON. A missing or non-integer `expected_current_step_id` answers 200 `_status: INVALID`."
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Application not visible or not editable, or the target step is not a visible step of the application's process."
          },
          "409": {
            "description": "The application has no current step, moved in the meantime, has no next step to deduce, or the target is an END, validation, approved or renewal step."
          }
        }
      }
    },
    "/application/{applicationid}/pending": {
      "get": {
        "tags": [
          "Applications"
        ],
        "summary": "Get what an application is waiting on",
        "description": "Returns what the application is waiting on: the open validation request and its manager, the latest archived email and its direction, and the last action time.",
        "operationId": "getApplicationPending",
        "parameters": [
          {
            "name": "applicationid",
            "in": "path",
            "required": true,
            "description": "Application ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pending state",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ApplicationPending"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/job/{jobid}/stats": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "Get the recruitment funnel of a job",
        "description": "Returns the recruitment funnel of one job: total applications, per channel, per week over the last eight weeks, the average of open jobs on the same contract type, and the first active publication date.",
        "operationId": "getJobStats",
        "parameters": [
          {
            "name": "jobid",
            "in": "path",
            "required": true,
            "description": "Job ID",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recruitment funnel",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/JobStats"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        }
      }
    },
    "/webhook/task": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook tasks",
        "description": "Returns the sent webhook tasks on applications visible to the user (default jobs excluded), newest first.",
        "operationId": "listWebhookTasks",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Paginated list of webhook tasks",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookTask"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/posting/apply/{publication_token}": {
      "servers": [
        {
          "url": "https://jobaffinity.fr",
          "description": "JobAffinity apply endpoint"
        }
      ],
      "post": {
        "tags": [
          "Job Application Submission"
        ],
        "summary": "Submit a job application",
        "description": "Submits a candidate application for a job posting. The full URL is the `apply_url` of the job feed (`https://jobaffinity.fr/posting/apply/{publication_token}`); do not build it manually. Requests carrying an `X-Indeed-Signature` header are handled in the Indeed Apply format.\n\nThe publication must be active and its job open or closed.\n\nYou can use the [validation form](https://jobaffinity.fr/apply/api/#validation-form) to check your data; note that it validates more strictly (field lengths) than this endpoint.",
        "operationId": "submitApplication",
        "parameters": [
          {
            "name": "publication_token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Publication token embedded in the `apply_url` of the job feed."
          },
          {
            "name": "src",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Sub-source name (added by Easyposting). `turnover-it` switches to the Turnover-IT format."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApplicationSubmissionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Application submitted.",
            "headers": {
              "Location": {
                "description": "Recruiter URL of the created application.",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string",
                      "enum": [
                        "1.0"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (JSON below), or a body that is not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string",
                      "enum": [
                        "1.0"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "validation"
                      ]
                    },
                    "_diagnostic": {
                      "type": "string",
                      "description": "One `field: message` per line."
                    }
                  }
                }
              }
            }
          },
          "405": {
            "description": "Method other than POST or HEAD."
          },
          "410": {
            "description": "Unknown or inactive publication."
          },
          "500": {
            "description": "Unexpected error; job boards should retry."
          }
        },
        "security": []
      },
      "head": {
        "tags": [
          "Job Application Submission"
        ],
        "summary": "Check apply endpoint availability",
        "description": "Answers 200 without checking the token: use it to check that the endpoint is reachable, not that a publication is active.",
        "operationId": "checkApplyEndpoint",
        "parameters": [
          {
            "name": "publication_token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Publication token embedded in the `apply_url` of the job feed."
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint reachable."
          }
        },
        "security": []
      }
    },
    "/me/validation-inbox": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "List the applications waiting for manager validation",
        "description": "Applications sitting on the manager validation step of their process companion configuration, newest application first. By default only jobs whose team includes the caller; any non-empty `alljobs` lists every visible job.",
        "operationId": "getValidationInbox",
        "parameters": [
          {
            "name": "alljobs",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Any non-empty value (including `0`) lists every visible job."
          },
          {
            "name": "job_id",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Only this job."
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only applications that entered their current step on or after this date."
          }
        ],
        "responses": {
          "200": {
            "description": "Validation inbox",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_schema": {
                          "type": "string",
                          "format": "uri-reference"
                        },
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "_next": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "Next page; empty string on the last page."
                        },
                        "count": {
                          "type": "integer",
                          "description": "Total across all pages."
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ValidationInboxItem"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/step/{change_id}/cancel": {
      "post": {
        "tags": [
          "Processes"
        ],
        "summary": "Cancel the latest step change of an application",
        "description": "Cancels the latest step change of an application, which goes back to its previous step. Only the author of the change can cancel it.",
        "operationId": "cancelStepChange",
        "parameters": [
          {
            "name": "change_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "`change_id` returned by the step change."
          }
        ],
        "responses": {
          "200": {
            "description": "Step change cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "_status": {
                          "type": "string",
                          "enum": [
                            "OK"
                          ]
                        },
                        "application_id": {
                          "type": "integer"
                        },
                        "step_id": {
                          "type": "integer"
                        },
                        "step_name": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/InvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The first step of an application cannot be cancelled."
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "description": "The caller is not the author of the step change."
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "409": {
            "description": "`not_last`: a later change exists; `email_sent`: the one-click rejection email is sent or being sent."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic Authentication with API credentials"
      }
    },
    "parameters": {
      "organisationFilterParam": {
        "name": "organisation_id",
        "in": "query",
        "description": "Narrow the list to one organisation.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "UnauthorizedError": {
        "description": "Authentication information is missing or invalid",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "NotFoundError": {
        "description": "The resource does not exist or is not visible to the caller."
      },
      "BadRequestError": {
        "description": "Malformed request, e.g. a body that is not valid JSON. Field validation errors are answered with HTTP 200 `_status: INVALID` instead."
      }
    },
    "schemas": {
      "Organisation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Organisation identifier"
          },
          "name": {
            "type": "string",
            "description": "Name of the organisation"
          }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "nullable": true,
            "description": "User identifier; null for an event made by the system (names are then empty strings)."
          },
          "first_name": {
            "type": "string",
            "description": "First name of the user"
          },
          "last_name": {
            "type": "string",
            "description": "Last name of the user"
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true,
            "description": "Email of the user; null when the user has none."
          }
        }
      },
      "PostalAddress": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "The street address"
          },
          "postalcode": {
            "type": "string",
            "description": "The postal code"
          },
          "town": {
            "type": "string",
            "description": "Town in which the street address is"
          },
          "country": {
            "type": "string",
            "description": "Country (ISO 3166-1 alpha-2 country code)"
          }
        }
      },
      "ContractType": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Long name of the contract type"
          },
          "abbreviation": {
            "type": "string",
            "description": "Short name of the contract type"
          }
        }
      },
      "JobTerm": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "description": "Term count of the job"
          },
          "unit": {
            "type": "string",
            "description": "Term unit of the job",
            "enum": [
              "DAY",
              "MONTH",
              "UNSET",
              "YEAR"
            ]
          }
        }
      },
      "ProcessStep": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Step identifier"
          },
          "name": {
            "type": "string",
            "description": "Step name"
          },
          "status": {
            "type": "string",
            "enum": [
              "START",
              "CURRENT",
              "END"
            ],
            "description": "Step status in the process"
          }
        }
      },
      "Process": {
        "type": "object",
        "properties": {
          "_self": {
            "type": "string",
            "format": "uri-reference",
            "description": "URL of the resource"
          },
          "id": {
            "type": "integer",
            "description": "Process identifier"
          },
          "name": {
            "type": "string",
            "description": "Process name"
          },
          "organisations": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Organisation"
                }
              }
            }
          },
          "steps": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ProcessStep"
                }
              }
            }
          },
          "companion": {
            "$ref": "#/components/schemas/ProcessCompanion"
          }
        }
      },
      "JobLite": {
        "type": "object",
        "properties": {
          "_self": {
            "type": "string",
            "format": "uri-reference",
            "description": "URL of the job resource"
          },
          "id": {
            "type": "integer",
            "description": "Job identifier"
          },
          "title": {
            "type": "string",
            "description": "Job title"
          },
          "status": {
            "type": "string",
            "description": "Job status",
            "enum": [
              "ARCHIVED",
              "CLOSED",
              "DRAFT",
              "OPEN",
              "PENDING_VALIDATION",
              "REJECTED",
              "VALIDATED"
            ]
          },
          "applications": {
            "type": "object",
            "properties": {
              "_self": {
                "type": "string",
                "format": "uri-reference",
                "description": "URL to list applications for this job"
              }
            }
          },
          "creation_date": {
            "type": "string",
            "format": "date",
            "description": "Job creation date."
          },
          "last_status_modification_date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
            "nullable": true,
            "description": "Last change of the job status. Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
          },
          "client_external_id": {
            "type": "string",
            "nullable": true,
            "description": "Identifier of the job client. Null when the job has no client or the client has no identifier."
          }
        }
      },
      "JobListItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/JobLite"
          },
          {
            "type": "object",
            "properties": {
              "start_applications_count": {
                "type": "integer",
                "description": "Number of applications on a step with START status."
              },
              "current_applications_count": {
                "type": "integer",
                "description": "Number of applications on a step with CURRENT status."
              },
              "end_applications_count": {
                "type": "integer",
                "description": "Number of applications on a step with END status."
              }
            }
          }
        ]
      },
      "Job": {
        "allOf": [
          {
            "$ref": "#/components/schemas/JobLite"
          },
          {
            "type": "object",
            "properties": {
              "_schema": {
                "type": "string",
                "format": "uri-reference"
              },
              "_status": {
                "type": "string",
                "enum": [
                  "OK"
                ]
              },
              "reference": {
                "type": "string",
                "description": "Job reference code"
              },
              "client": {
                "type": "string",
                "description": "Job client"
              },
              "location": {
                "type": "object",
                "description": "Job location with postal address and geographic coordinates",
                "properties": {
                  "address": {
                    "type": "string",
                    "description": "Street address"
                  },
                  "postalcode": {
                    "type": "string",
                    "description": "Postal code"
                  },
                  "town": {
                    "type": "string",
                    "description": "Town"
                  },
                  "country": {
                    "type": "string",
                    "description": "Country (ISO 3166-1 alpha-2)"
                  },
                  "longitude": {
                    "type": "number",
                    "description": "Geographic longitude",
                    "nullable": true
                  },
                  "latitude": {
                    "type": "number",
                    "description": "Geographic latitude",
                    "nullable": true
                  }
                }
              },
              "employer_description": {
                "type": "string",
                "description": "Employer description (HTML, returned as stored)."
              },
              "position_description": {
                "type": "string",
                "description": "Position description (HTML, returned as stored)."
              },
              "profile_description": {
                "type": "string",
                "description": "Required profile description (HTML, returned as stored)."
              },
              "creation_date": {
                "type": "string",
                "format": "date",
                "description": "Job creation date (YYYY-MM-DD)"
              },
              "owner": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/User"
                  }
                ],
                "description": "User responsible for the job"
              },
              "contract_type": {
                "$ref": "#/components/schemas/ContractType"
              },
              "term": {
                "$ref": "#/components/schemas/JobTerm"
              },
              "organisation": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Organisation"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "external_id": {
                        "type": "string",
                        "nullable": true,
                        "description": "Identifier of the organisation. Null when it has none."
                      }
                    }
                  }
                ],
                "description": "Organisation of the job."
              },
              "attributes": {
                "type": "object",
                "description": "Custom job attributes",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "Display name of the attribute"
                        },
                        "api_name": {
                          "type": "string",
                          "description": "API name of the attribute"
                        },
                        "value": {
                          "type": "string",
                          "description": "Value of the attribute"
                        }
                      }
                    }
                  }
                }
              },
              "questionnaire": {
                "$ref": "#/components/schemas/JobQuestionnaire"
              },
              "process": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  },
                  "id": {
                    "type": "integer",
                    "description": "Process identifier"
                  }
                }
              },
              "team": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  }
                }
              },
              "publication": {
                "type": "object",
                "description": "Span of the active publication schedules (always an object).",
                "properties": {
                  "start_date": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                    "nullable": true,
                    "description": "Earliest schedule start. Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
                  },
                  "end_date": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                    "nullable": true,
                    "description": "Latest schedule end. Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
                  }
                }
              }
            }
          }
        ]
      },
      "Candidate": {
        "type": "object",
        "properties": {
          "_self": {
            "type": "string",
            "format": "uri-reference",
            "description": "URL of the candidate resource"
          },
          "_schema": {
            "type": "string",
            "format": "uri-reference"
          },
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "id": {
            "type": "integer",
            "description": "Candidate identifier"
          },
          "title": {
            "type": "string",
            "enum": [
              "UNKNOWN",
              "MR",
              "MS",
              "MRS",
              "MISS"
            ],
            "description": "Candidate title"
          },
          "first_name": {
            "type": "string",
            "description": "First name"
          },
          "last_name": {
            "type": "string",
            "description": "Last name"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Primary email address",
            "nullable": true
          },
          "email_secondary": {
            "type": "string",
            "format": "email",
            "description": "Secondary email address",
            "nullable": true
          },
          "birthdate": {
            "type": "string",
            "format": "date",
            "description": "Date of birth (YYYY-MM-DD)",
            "nullable": true
          },
          "home_phone": {
            "type": "string",
            "description": "Home phone number Empty string when unknown."
          },
          "mobile_phone": {
            "type": "string",
            "description": "Mobile phone number Empty string when unknown."
          },
          "office_phone": {
            "type": "string",
            "description": "Office phone number Empty string when unknown."
          },
          "location": {
            "$ref": "#/components/schemas/PostalAddress",
            "description": "Candidate postal address"
          },
          "position": {
            "type": "string",
            "description": "Current position/job title Empty string when unknown."
          },
          "position_company": {
            "type": "string",
            "description": "Current company Empty string when unknown."
          },
          "experience_start": {
            "type": "string",
            "format": "date",
            "description": "Start of professional experience (YYYY-MM-DD)",
            "nullable": true
          },
          "linkedin": {
            "type": "string",
            "format": "uri",
            "description": "LinkedIn profile URL",
            "nullable": true
          },
          "instagram": {
            "type": "string",
            "format": "uri",
            "description": "Instagram profile URL; null when none.",
            "nullable": true
          },
          "applications": {
            "type": "object",
            "properties": {
              "_self": {
                "type": "string",
                "format": "uri-reference"
              }
            }
          },
          "pools": {
            "type": "object",
            "description": "Candidate pools (viviers)",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Pool name"
                    },
                    "candidates": {
                      "type": "object",
                      "properties": {
                        "_self": {
                          "type": "string",
                          "format": "uri-reference",
                          "description": "URL to list candidates in this pool"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Application": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApplicationListItem"
          },
          {
            "type": "object",
            "properties": {
              "_schema": {
                "type": "string",
                "format": "uri-reference"
              },
              "_status": {
                "type": "string",
                "enum": [
                  "OK"
                ]
              },
              "profile": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  }
                }
              },
              "steps": {
                "type": "object",
                "description": "Full step history of the application (available in application detail, not in collection listings)",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "START",
                            "CURRENT",
                            "END"
                          ]
                        },
                        "date": {
                          "type": "string",
                          "format": "date"
                        },
                        "user": {
                          "$ref": "#/components/schemas/User"
                        }
                      }
                    }
                  }
                }
              },
              "history": {
                "type": "object",
                "description": "Events recorded on the application, most recent first (available in application detail, not in collection listings). Capped at the 200 latest.",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ApplicationHistoryEvent"
                    }
                  }
                }
              },
              "emails": {
                "type": "object",
                "description": "Archived incoming and outgoing emails of the application, most recent first (available in application detail, not in collection listings). Capped at the 100 latest.",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ApplicationEmailSummary"
                    }
                  }
                }
              },
              "notes": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  }
                }
              }
            }
          }
        ],
        "description": "Application detail. Applications on a default job answer 404."
      },
      "ApplicationProfile": {
        "type": "object",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "_schema": {
            "type": "string",
            "format": "uri-reference"
          },
          "identity": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileIdentity"
            },
            "description": "Zero or one entry."
          },
          "employments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileEmployment"
            }
          },
          "educations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileEducation"
            }
          },
          "languages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileLanguage"
            }
          },
          "skills": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileSkill"
            }
          },
          "hobbies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProfileHobby"
            }
          },
          "avatar_url": {
            "type": "string",
            "description": "Presigned URL to the candidate avatar image, or empty string if none. Short-lived (~1 hour)."
          },
          "cv_url": {
            "type": "string",
            "description": "Presigned URL to a CV document, or empty string if none. Falls back to the candidate's latest CV when the application's own document is missing. Short-lived (~1 hour)."
          },
          "cv_unreachable": {
            "type": "boolean",
            "description": "True when a CV is attached but its file cannot be served."
          },
          "scoring": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Scoring"
              }
            ],
            "description": "Matching evaluation. When never scored: `{score: 0, label: \"\", evaluated: false, strong_points: [], weak_points: []}`."
          }
        }
      },
      "ProfileIdentity": {
        "type": "object",
        "properties": {
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "phone": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "zip_code": {
            "type": "string"
          },
          "town": {
            "type": "string"
          },
          "country": {
            "type": "string"
          }
        }
      },
      "ProfileEmployment": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "description": "Start date (YYYY-MM-DD or empty)"
          },
          "end": {
            "type": "string",
            "description": "End date (YYYY-MM-DD or empty)"
          },
          "readable_start": {
            "type": "string",
            "description": "Localized start date"
          },
          "readable_end": {
            "type": "string",
            "description": "Localized end date"
          },
          "title": {
            "type": "string"
          },
          "employer": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "ProfileEducation": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          },
          "readable_start": {
            "type": "string"
          },
          "readable_end": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "institution": {
            "type": "string"
          },
          "level": {
            "type": "integer",
            "description": "Education level index; 0 when unknown."
          },
          "level_text": {
            "type": "string",
            "description": "Education level label (e.g. Bac+5)"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "ProfileLanguage": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Language name"
          },
          "level": {
            "type": "integer",
            "description": "Proficiency level index; 0 when unknown."
          },
          "level_name": {
            "type": "string",
            "description": "Proficiency level label (e.g. C1)"
          }
        }
      },
      "ProfileSkill": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "level": {
            "type": "integer",
            "description": "Skill level index; 0 when unknown."
          },
          "level_name": {
            "type": "string",
            "description": "Skill level label (e.g. Advanced)"
          }
        }
      },
      "ProfileHobby": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          }
        }
      },
      "ApplicationProcess": {
        "type": "object",
        "description": "A step change entry in the process history",
        "properties": {
          "change_id": {
            "type": "integer",
            "description": "Unique identifier for this step change, assigned in increasing order. Concurrent changes can become visible slightly out of order, so deduplicate on it rather than using it as a strict watermark."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Date of step change"
          },
          "id": {
            "type": "integer",
            "description": "Step identifier"
          },
          "name": {
            "type": "string",
            "description": "Step name"
          },
          "status": {
            "type": "string",
            "enum": [
              "START",
              "CURRENT",
              "END"
            ],
            "description": "Step status"
          },
          "user": {
            "$ref": "#/components/schemas/User",
            "description": "User who made the step change"
          },
          "organisation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Organisation"
              },
              {
                "type": "object",
                "properties": {
                  "external_id": {
                    "type": "string",
                    "nullable": true,
                    "description": "Identifier of the organisation. Null when the organisation has no identifier."
                  }
                }
              }
            ],
            "description": "Organisation of the job the step change belongs to"
          },
          "application": {
            "type": "object",
            "description": "Full application details including candidate, job, source, and current step",
            "properties": {
              "_self": {
                "type": "string",
                "format": "uri-reference"
              },
              "id": {
                "type": "integer"
              },
              "route": {
                "type": "string",
                "enum": [
                  "UNSPECIFIED",
                  "MANUAL",
                  "LINK",
                  "EMAIL",
                  "ASSIGN",
                  "IMPORT",
                  "LINKEDIN_PROFILE",
                  "API_APPLY"
                ]
              },
              "date": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
              },
              "candidate": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  },
                  "applications": {
                    "type": "object",
                    "properties": {
                      "_self": {
                        "type": "string",
                        "format": "uri-reference"
                      }
                    }
                  },
                  "id": {
                    "type": "integer"
                  },
                  "title": {
                    "type": "string",
                    "enum": [
                      "UNKNOWN",
                      "MR",
                      "MS",
                      "MRS",
                      "MISS"
                    ]
                  },
                  "first_name": {
                    "type": "string"
                  },
                  "last_name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "nullable": true
                  }
                }
              },
              "job": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  },
                  "applications": {
                    "type": "object",
                    "properties": {
                      "_self": {
                        "type": "string",
                        "format": "uri-reference"
                      }
                    }
                  },
                  "id": {
                    "type": "integer"
                  },
                  "title": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ARCHIVED",
                      "CLOSED",
                      "DRAFT",
                      "OPEN",
                      "PENDING_VALIDATION",
                      "REJECTED",
                      "VALIDATED"
                    ]
                  },
                  "creation_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "last_status_modification_date": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                    "nullable": true,
                    "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
                  },
                  "client_external_id": {
                    "type": "string",
                    "nullable": true,
                    "description": "Identifier of the job client. Null when the job has no client or the client has no identifier."
                  }
                }
              },
              "source": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  },
                  "posting_service": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer",
                        "nullable": true
                      },
                      "name": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "step": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "START",
                      "CURRENT",
                      "END",
                      ""
                    ]
                  },
                  "id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  }
                }
              },
              "latest_cv_url": {
                "type": "string",
                "format": "uri-reference",
                "description": "Always set."
              },
              "questionnaire_score": {
                "type": "integer",
                "nullable": true
              },
              "score": {
                "type": "integer",
                "nullable": true
              },
              "last_action_time": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                "nullable": true,
                "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
              },
              "candidate_last_action": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                "nullable": true,
                "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
              }
            }
          }
        }
      },
      "ApplicationProcessInput": {
        "type": "object",
        "required": [
          "application_id",
          "process_step_id"
        ],
        "properties": {
          "application_id": {
            "type": "integer",
            "description": "Identifier of the application to modify. Provided by the `.id` field of the application detail resource."
          },
          "process_step_id": {
            "type": "integer",
            "description": "Identifier of the target step (will become the current step of the application). Provided by the `.steps.items[].id` field of the process detail resource."
          }
        },
        "additionalProperties": false
      },
      "Note": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Note identifier"
          },
          "datetime": {
            "type": "string",
            "description": "Note creation date and time Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time).",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$"
          },
          "text": {
            "type": "string",
            "description": "Note content (HTML format)"
          },
          "organisation": {
            "$ref": "#/components/schemas/Organisation",
            "description": "Organisation that can see this note"
          },
          "candidate": {
            "type": "object",
            "description": "Candidate this note is about",
            "properties": {
              "id": {
                "type": "integer"
              },
              "_self": {
                "type": "string",
                "format": "uri-reference"
              },
              "title": {
                "type": "string",
                "enum": [
                  "UNKNOWN",
                  "MR",
                  "MS",
                  "MRS",
                  "MISS"
                ]
              },
              "first_name": {
                "type": "string"
              },
              "last_name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email",
                "nullable": true
              },
              "applications": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  }
                }
              }
            }
          },
          "user": {
            "$ref": "#/components/schemas/User"
          }
        }
      },
      "NoteInput": {
        "type": "object",
        "required": [
          "candidate_id",
          "organisation_id",
          "text"
        ],
        "properties": {
          "candidate_id": {
            "type": "integer",
            "description": "Identifier of the candidate. Provided by the `.id` field of the candidate detail resource."
          },
          "organisation_id": {
            "type": "integer",
            "description": "Identifier of the organisation for note visibility. Only users in this organisation will be able to see the note. Provided by the `.organisation.id` field of the job detail resource."
          },
          "text": {
            "type": "string",
            "description": "Note content. Can be HTML formatted (basic formatting tags only; script tags and style attributes are stripped). Length is counted after sanitizing.",
            "minLength": 1,
            "maxLength": 60000
          }
        },
        "additionalProperties": false
      },
      "QuestionnaireAnswer": {
        "type": "object",
        "properties": {
          "question": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "label": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "enum": [
                  "TEXT",
                  "LONG_TEXT",
                  "SELECT",
                  "CHECK",
                  "RADIO",
                  "SCORE",
                  "DOCUMENT"
                ]
              }
            }
          },
          "choice": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "nullable": true
              },
              "value": {
                "type": "string",
                "description": "Empty string when none."
              }
            }
          },
          "answer": {
            "type": "string",
            "description": "Empty string when none."
          },
          "score": {
            "type": "integer",
            "nullable": true
          },
          "document": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "nullable": true
              },
              "name": {
                "type": "string"
              },
              "mime_type": {
                "type": "string"
              }
            }
          }
        }
      },
      "QuestionnaireAnswersInput": {
        "type": "object",
        "required": [
          "questionnaire_id",
          "answers"
        ],
        "properties": {
          "questionnaire_id": {
            "type": "integer",
            "description": "Questionnaire identifier."
          },
          "answers": {
            "type": "object",
            "description": "Keyed by question id (as a string): `{\"<question_id>\": {\"answer\": value}}`. Value per question type: TEXT string (max 255), LONG_TEXT string (max 4096), SCORE integer, DOCUMENT data URI `data:<mime>;base64,<content>`, SELECT/CHECK/RADIO array of choice ids. Required questions must be answered; unknown question ids are rejected.",
            "additionalProperties": {
              "type": "object",
              "required": [
                "answer"
              ],
              "properties": {
                "answer": {}
              }
            }
          }
        },
        "additionalProperties": false
      },
      "DocumentInput": {
        "type": "object",
        "description": "A file document encoded as Base64",
        "properties": {
          "name": {
            "type": "string",
            "description": "File name",
            "example": "cv.txt"
          },
          "mime_type": {
            "type": "string",
            "description": "Media type of the file. The key is required but may be null or empty: it is then guessed from `name`. When set, it must match the detected content type, otherwise 400 `Inconsistent Mime Type.`",
            "example": "text/plain",
            "nullable": true
          },
          "content": {
            "type": "string",
            "description": "Base64-encoded content of the file.",
            "example": "RG9jdW1lbnQgY29udGVudA==\n"
          }
        },
        "required": [
          "name",
          "mime_type",
          "content"
        ],
        "additionalProperties": false
      },
      "ApplicationSubmissionInput": {
        "type": "object",
        "description": "Candidate application payload (version 1.x). Unknown fields are rejected (400), except `application_id` and `status_url`, which are kept as the external reference of the application.",
        "required": [
          "version",
          "first_name",
          "last_name",
          "email",
          "country"
        ],
        "properties": {
          "version": {
            "type": "string",
            "description": "Schema version, e.g. `\"1.2\"`. Any non-empty value is accepted.",
            "example": "1.2"
          },
          "first_name": {
            "type": "string",
            "description": "Candidate's first name.",
            "example": "John"
          },
          "last_name": {
            "type": "string",
            "description": "Candidate's last name.",
            "example": "Doe"
          },
          "title": {
            "type": "string",
            "description": "Candidate's title (case-sensitive). Defaults to `UNKNOWN`.",
            "enum": [
              "UNKNOWN",
              "MR",
              "MS",
              "MRS",
              "MISS"
            ],
            "example": "MR"
          },
          "email": {
            "type": "string",
            "description": "Candidate's email address.",
            "format": "email",
            "example": "john@doe.site"
          },
          "office_phone": {
            "type": "string",
            "description": "Candidate's office phone number.",
            "example": "+33 1 55-55-55-55"
          },
          "mobile_phone": {
            "type": "string",
            "description": "Candidate's mobile phone number.",
            "example": "+33 6 55-55-55-55"
          },
          "home_phone": {
            "type": "string",
            "description": "Candidate's home phone number.",
            "example": "+33 9 55-55-55-55"
          },
          "address": {
            "type": "string",
            "description": "Candidate's street address.",
            "example": "1 rue Champs Bon"
          },
          "zip_code": {
            "type": "string",
            "description": "Candidate's postal code.",
            "example": "75001"
          },
          "town": {
            "type": "string",
            "description": "Town where the candidate lives.",
            "example": "Paris"
          },
          "country": {
            "type": "string",
            "description": "Two-character country code (ISO 3166-1 alpha-2 expected; not checked against the list).",
            "maxLength": 2,
            "example": "FR",
            "minLength": 2
          },
          "availability": {
            "type": "string",
            "maxLength": 4,
            "description": "When the candidate is available. Known values: `now`, `15d`, `1m`, `2m`, `3m`, `>3m`, `ope`, `na`, `date` (then send `availability_date`); other values of up to 4 characters are accepted and ignored.",
            "example": "now"
          },
          "availability_date": {
            "type": "string",
            "description": "Required when `availability` is `\"date\"`. Not checked to be in the future.",
            "format": "date",
            "example": "2030-01-10"
          },
          "cv": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DocumentInput"
              }
            ],
            "description": "CV document of the candidate. Optional."
          },
          "cover": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DocumentInput"
              }
            ],
            "description": "Cover letter document of the candidate. Optional. Can also carry any raw candidate information such as assessments."
          },
          "linkedin_public_url": {
            "type": "string",
            "format": "uri",
            "description": "URL of the candidate's LinkedIn profile; `http://` is added when the scheme is missing.",
            "example": "https://www.linkedin.com/in/username/"
          },
          "experience": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Years of experience."
          }
        },
        "example": {
          "version": "1.2",
          "first_name": "John",
          "last_name": "Doe",
          "title": "MR",
          "email": "john@doe.email",
          "mobile_phone": "+33 6 55-55-55-55",
          "address": "1 rue Champs Bon",
          "zip_code": "75001",
          "town": "Paris",
          "country": "FR",
          "cv": {
            "name": "cv.txt",
            "mime_type": "text/plain",
            "content": "RG9jdW1lbnQgY29udGVudA==\n"
          }
        }
      },
      "Me": {
        "type": "object",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "_self": {
            "type": "string",
            "format": "uri-reference"
          },
          "id": {
            "type": "integer",
            "description": "User identifier"
          },
          "login": {
            "type": "string",
            "description": "User login"
          },
          "email": {
            "type": "string",
            "description": "User email; may be empty."
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "superadmin": {
            "type": "boolean",
            "description": "Whether the user is a superadmin"
          },
          "domain_id": {
            "type": "integer",
            "description": "Identifier of the user's domain"
          },
          "organisation_ids": {
            "type": "array",
            "description": "Sorted ids of the organisations where the user is administrator, recruiter or manager (operational, disabled and deleted memberships are left out, unlike `organisations`).",
            "items": {
              "type": "integer"
            }
          },
          "organisations": {
            "type": "array",
            "description": "One entry per membership.",
            "items": {
              "$ref": "#/components/schemas/OrganisationMembership"
            }
          }
        }
      },
      "OrganisationMembership": {
        "type": "object",
        "description": "A user's membership of one organisation.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Organisation identifier"
          },
          "role": {
            "type": "integer",
            "enum": [
              1,
              2,
              3,
              5,
              10,
              11
            ],
            "description": "1 Administrator, 2 Recruiter, 3 Operational, 5 Manager, 10 Disabled, 11 Deleted user."
          },
          "role_label": {
            "type": "string",
            "nullable": true,
            "description": "Canonical English name of the role: Administrator, Recruiter, Operational, Manager, Disabled, Deleted user; null for an unmapped role."
          },
          "name": {
            "type": "string",
            "description": "Organisation label, empty when the organisation is unavailable."
          }
        }
      },
      "OrganisationMember": {
        "type": "object",
        "description": "A member of an organisation, one entry per user.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "User identifier"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "May be empty."
          },
          "role": {
            "type": "integer",
            "enum": [
              1,
              2,
              3,
              5,
              10,
              11
            ]
          }
        }
      },
      "CandidateLookupItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Candidate identifier"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "Empty string when unknown."
          },
          "email_secondary": {
            "type": "string",
            "description": "Empty string when unknown."
          },
          "office_phone": {
            "type": "string",
            "description": "Empty string when unknown."
          },
          "mobile_phone": {
            "type": "string",
            "description": "Empty string when unknown."
          },
          "home_phone": {
            "type": "string",
            "description": "Empty string when unknown."
          },
          "matched_on": {
            "type": "array",
            "description": "Which supplied criteria this candidate matched.",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "phone"
              ]
            }
          }
        }
      },
      "ApplicationHistoryEvent": {
        "type": "object",
        "description": "An event recorded on the application.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Event identifier"
          },
          "date": {
            "type": "string",
            "description": "When the event happened Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time).",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$"
          },
          "action": {
            "type": "string",
            "description": "Event type"
          },
          "text": {
            "type": "string",
            "description": "Human-readable event description"
          },
          "user": {
            "allOf": [
              {
                "$ref": "#/components/schemas/User"
              }
            ],
            "description": "Always an object; `id` is null for a system event."
          }
        }
      },
      "ApplicationEmailSummary": {
        "type": "object",
        "description": "A summary of one archived email of the application.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Archived email identifier"
          },
          "date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
            "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
          },
          "subject": {
            "type": "string",
            "description": "Empty string when none."
          },
          "sender": {
            "type": "string"
          },
          "recipient": {
            "type": "string"
          },
          "received": {
            "type": "boolean",
            "description": "True when the email has a delivered-to address (came in)."
          }
        }
      },
      "ApplicationEmail": {
        "type": "object",
        "description": "Full detail of one archived email of the application.",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "_schema": {
            "type": "string",
            "format": "uri-reference"
          },
          "id": {
            "type": "integer",
            "description": "Archived email identifier"
          },
          "date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
            "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
          },
          "subject": {
            "type": "string"
          },
          "sender": {
            "type": "string",
            "description": "The From address."
          },
          "recipient": {
            "type": "string",
            "description": "The delivered-to address, empty for an outgoing email."
          },
          "received": {
            "type": "boolean",
            "description": "True when the email came from the candidate."
          },
          "to": {
            "type": "string",
            "description": "Received email: the delivered-to address. Sent email: the To addresses, comma-separated."
          },
          "cc": {
            "type": "string",
            "description": "Comma-separated Cc addresses."
          },
          "content": {
            "type": "string",
            "description": "Sanitized HTML or plain-text body, empty when unreadable."
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "size": {
                  "type": "string",
                  "description": "Human-readable size such as `512 B` or `1.2 MB`; empty when unknown."
                }
              }
            }
          }
        }
      },
      "MessageTemplate": {
        "type": "object",
        "description": "A recorded email template of an organisation.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Template identifier"
          },
          "name": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "",
              "gdpr",
              "acknowledgement",
              "validation",
              "validation_multiple"
            ]
          },
          "from_email": {
            "type": "string",
            "description": "Sender address, possibly a keyword such as `[emailutilisateur]`; may be empty."
          }
        }
      },
      "ScoringPoint": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Always empty; kept for compatibility."
          }
        }
      },
      "Scoring": {
        "type": "object",
        "description": "Matching evaluation of an application.",
        "properties": {
          "score": {
            "type": "integer",
            "description": "Final matching score."
          },
          "label": {
            "type": "string",
            "description": "Score tier label."
          },
          "evaluated": {
            "type": "boolean",
            "description": "False when the application was never scored (score 0, empty label and lists)."
          },
          "strong_points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScoringPoint"
            }
          },
          "weak_points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScoringPoint"
            }
          }
        }
      },
      "ApplicationSteps": {
        "type": "object",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "application_id": {
            "type": "integer"
          },
          "current_step_id": {
            "type": "integer",
            "nullable": true,
            "description": "Current step, null before any step."
          },
          "steps": {
            "type": "array",
            "description": "Visible steps of the process, ordered by rank.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "rank": {
                  "type": "integer",
                  "description": "Rank of the step in the process."
                },
                "status": {
                  "type": "integer",
                  "description": "Step status: 1 start, 2 current, 3 end."
                }
              }
            }
          },
          "next_step_id": {
            "type": "integer",
            "nullable": true,
            "description": "Deduced next step, null at the end of the flow."
          },
          "next_step_name": {
            "type": "string",
            "description": "Empty string when there is no next step."
          },
          "next_step_rank": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "ApplicationPending": {
        "type": "object",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "application_id": {
            "type": "integer"
          },
          "validation_id": {
            "type": "integer",
            "nullable": true,
            "description": "Open validation request, null when none."
          },
          "validation_requested_at": {
            "type": "string",
            "description": "Request date `YYYY-MM-DD`; empty string when none."
          },
          "validation_manager_label": {
            "type": "string",
            "description": "Manager the validation waits on; empty string when none."
          },
          "last_email_at": {
            "type": "string",
            "description": "Latest archived email (`YYYY-MM-DDTHH:MM:SS`, no timezone); empty string when none."
          },
          "last_email_received": {
            "type": "boolean",
            "description": "True when that email came in; false when there is no email."
          },
          "last_action_time": {
            "type": "string",
            "description": "Last action (`YYYY-MM-DDTHH:MM:SS`, no timezone); empty string when none."
          }
        }
      },
      "JobStats": {
        "type": "object",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "OK"
            ]
          },
          "job_id": {
            "type": "integer"
          },
          "job_title": {
            "type": "string"
          },
          "applications_total": {
            "type": "integer",
            "description": "Applications received over the job's life."
          },
          "by_channel": {
            "type": "array",
            "description": "Applications per publication source, biggest first.",
            "items": {
              "type": "object",
              "properties": {
                "channel": {
                  "type": "string",
                  "description": "Publication source name."
                },
                "count": {
                  "type": "integer"
                },
                "last_date": {
                  "type": "string",
                  "format": "date"
                }
              }
            }
          },
          "by_week": {
            "type": "array",
            "description": "Applications per week over the last eight weeks.",
            "items": {
              "type": "object",
              "properties": {
                "week_start": {
                  "type": "string",
                  "format": "date",
                  "description": "First day of a rolling 7-day window ending today (not an ISO week); 8 windows, oldest first."
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          },
          "peer_average": {
            "type": "number",
            "nullable": true,
            "description": "Average applications of OPEN, non-default jobs of the same organisation and contract type (this job included), rounded to 1 decimal; null below three such jobs."
          },
          "published_since": {
            "type": "string",
            "description": "First active publication date `YYYY-MM-DD`; empty string when never published."
          }
        }
      },
      "WebhookTask": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Webhook task identifier"
          },
          "event": {
            "type": "string",
            "enum": [
              "ProcessStepChange"
            ]
          },
          "datetime": {
            "type": "string",
            "description": "Webhook call date and time Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time).",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$"
          },
          "response_body": {
            "type": "string",
            "maxLength": 1024,
            "description": "First 1024 characters of the response body, or the error text when the call failed; empty string when none."
          },
          "response_status": {
            "type": "integer",
            "nullable": true,
            "description": "HTTP status returned by the endpoint; 0 when the call failed (network error)."
          },
          "application": {
            "type": "object",
            "properties": {
              "_self": {
                "type": "string",
                "format": "uri-reference"
              }
            }
          }
        }
      },
      "InvalidResponse": {
        "type": "object",
        "description": "Answered with HTTP 200 when parameters or body fail validation.",
        "properties": {
          "_status": {
            "type": "string",
            "enum": [
              "INVALID"
            ]
          },
          "invalid": {
            "type": "object",
            "description": "One entry per invalid field; the key `\"\"` holds errors not tied to a field (e.g. an unexpected parameter). `message` is an object or an array for nested fields.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "nullable": true,
                  "description": "Always null."
                },
                "message": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "object"
                    },
                    {
                      "type": "array",
                      "items": {}
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "ProcessCompanion": {
        "type": "object",
        "nullable": true,
        "description": "Companion app configuration of the process; null when the process is not configured.",
        "properties": {
          "manager_validation_step_id": {
            "type": "integer"
          },
          "accept_step_id": {
            "type": "integer"
          },
          "reject": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "oneclick",
                  "step"
                ]
              },
              "step_id": {
                "type": "integer",
                "nullable": true,
                "description": "Set only in `step` mode."
              }
            }
          }
        }
      },
      "ProcessDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Process"
          },
          {
            "type": "object",
            "properties": {
              "_schema": {
                "type": "string",
                "format": "uri-reference"
              },
              "_status": {
                "type": "string",
                "enum": [
                  "OK"
                ]
              }
            }
          }
        ],
        "description": "Organisations are limited to those the caller belongs to; steps are every step of the process, ordered by status (START, CURRENT, END) then rank."
      },
      "JobQuestionnaire": {
        "type": "object",
        "nullable": true,
        "description": "Questionnaire of the job, null when none.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "questions": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "label": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "TEXT",
                        "LONG_TEXT",
                        "SELECT",
                        "CHECK",
                        "RADIO",
                        "SCORE",
                        "DOCUMENT"
                      ]
                    },
                    "required": {
                      "type": "boolean"
                    },
                    "choices": {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "value": {
                                "type": "string"
                              }
                            }
                          },
                          "description": "Empty for question types without choices."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ApplicationListItem": {
        "type": "object",
        "properties": {
          "_self": {
            "type": "string",
            "format": "uri-reference",
            "description": "URL of the application resource"
          },
          "id": {
            "type": "integer",
            "description": "Application identifier"
          },
          "date": {
            "type": "string",
            "description": "Application submission date Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time).",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$"
          },
          "route": {
            "type": "string",
            "enum": [
              "UNSPECIFIED",
              "MANUAL",
              "LINK",
              "EMAIL",
              "ASSIGN",
              "IMPORT",
              "LINKEDIN_PROFILE",
              "API_APPLY"
            ],
            "description": "How the candidate applied."
          },
          "questionnaire_score": {
            "type": "integer",
            "nullable": true,
            "description": "Questionnaire score, null when the candidate never answered a scoring questionnaire."
          },
          "score": {
            "type": "integer",
            "nullable": true,
            "description": "Matching score, null when never evaluated."
          },
          "last_action_time": {
            "type": "string",
            "nullable": true,
            "description": "Timestamp of the last action recorded on the application. Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time).",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$"
          },
          "candidate_last_action": {
            "type": "string",
            "nullable": true,
            "description": "Timestamp of the last action recorded on the candidate. Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time).",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$"
          },
          "latest_cv_url": {
            "type": "string",
            "format": "uri-reference",
            "description": "Link to the latest CV (redirects or streams it). Always set; answers 404 when the application has no CV."
          },
          "candidate": {
            "type": "object",
            "description": "Inline candidate summary",
            "properties": {
              "id": {
                "type": "integer"
              },
              "_self": {
                "type": "string",
                "format": "uri-reference"
              },
              "applications": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  }
                }
              },
              "title": {
                "type": "string",
                "enum": [
                  "UNKNOWN",
                  "MR",
                  "MS",
                  "MRS",
                  "MISS"
                ]
              },
              "first_name": {
                "type": "string"
              },
              "last_name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email",
                "nullable": true
              }
            }
          },
          "source": {
            "type": "object",
            "description": "Source of the application",
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "posting_service": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "name": {
                    "type": "string",
                    "description": "Empty string when none."
                  }
                }
              }
            }
          },
          "job": {
            "type": "object",
            "description": "Inline job summary",
            "properties": {
              "_self": {
                "type": "string",
                "format": "uri-reference"
              },
              "id": {
                "type": "integer"
              },
              "title": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "ARCHIVED",
                  "CLOSED",
                  "DRAFT",
                  "OPEN",
                  "PENDING_VALIDATION",
                  "REJECTED",
                  "VALIDATED"
                ]
              },
              "applications": {
                "type": "object",
                "properties": {
                  "_self": {
                    "type": "string",
                    "format": "uri-reference"
                  }
                }
              },
              "creation_date": {
                "type": "string",
                "format": "date"
              },
              "last_status_modification_date": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
                "nullable": true,
                "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
              },
              "client_external_id": {
                "type": "string",
                "nullable": true,
                "description": "Identifier of the job client. Null when the job has no client or the client has no identifier."
              }
            }
          },
          "step": {
            "type": "object",
            "description": "Current step of the application",
            "properties": {
              "id": {
                "type": "integer",
                "nullable": true
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "START",
                  "CURRENT",
                  "END",
                  ""
                ]
              },
              "date": {
                "type": "string",
                "format": "date",
                "nullable": true,
                "description": "Date when this step was set"
              }
            }
          }
        }
      },
      "CandidateListItem": {
        "type": "object",
        "properties": {
          "_self": {
            "type": "string",
            "format": "uri-reference"
          },
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string",
            "enum": [
              "UNKNOWN",
              "MR",
              "MS",
              "MRS",
              "MISS"
            ]
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "applications": {
            "type": "object",
            "properties": {
              "_self": {
                "type": "string",
                "format": "uri-reference"
              }
            }
          }
        },
        "description": "Use GET /candidate/{id} for phones, address, birthdate and pools."
      },
      "QuestionnaireSubmission": {
        "type": "object",
        "description": "One submission of the questionnaire, newest first.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "time_created": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$",
            "description": "Format `YYYY-MM-DD HH:MM:SS` (space separator, no timezone, server local time)."
          },
          "answers": {
            "type": "array",
            "description": "Ordered by question rank; one entry per chosen choice.",
            "items": {
              "$ref": "#/components/schemas/QuestionnaireAnswer"
            }
          }
        }
      },
      "ValidationInboxItem": {
        "type": "object",
        "properties": {
          "application_id": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "manager_validation"
            ]
          },
          "job": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "title": {
                "type": "string"
              }
            }
          },
          "candidate": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "first_name": {
                "type": "string"
              },
              "last_name": {
                "type": "string"
              }
            }
          },
          "step": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
