{"openapi":"3.1.0","info":{"title":"Kirnova Partner API","version":"1.0.0","summary":"Start assessments for your people, read their results, and run a job board, from your own system.","description":"Use this API from your server to put Kirnova assessments inside your product. You mint a launch token and show our assessment in a frame, or you drive a sitting yourself page by page. Afterwards you read the result, download the report, and get told when a person finishes. A second part of the API lets you list jobs, read applications to jobs your organisation posted, and invite people to apply.\n\nAuthenticate every server-to-server call with your secret API key in the Authorization header: `Authorization: Bearer bi_live_sk_...`. Keys come in a live form and a test form (`bi_test_sk_...`). Keep keys on your server and never ship them in a web page or an app.\n\nErrors always use one envelope: `{ \"error\": { \"code\", \"message\", \"field\"? } }`. Branch on `code`. Field names on the wire are snake_case.\n\nWhich instruments you can start is set on your account. Asking for one you do not hold is refused by name and never swapped for another. TIGER Light cannot be started by a partner: it is for a person to take for themselves."},"servers":[{"url":"https://api.kirnova.com","description":"Production"},{"url":"https://stage.kirnova.com","description":"Staging — test your integration here with a staging key"}],"tags":[{"name":"Launch","description":"Start an assessment inside a frame on your own page."},{"name":"Sessions","description":"Run a sitting yourself, page by page, without the frame."},{"name":"Results","description":"Read results and download reports for people you assessed."},{"name":"Account","description":"Usage for your partner account."},{"name":"Jobs","description":"Your job board: list jobs, read applications, invite people to apply."},{"name":"Webhooks","description":"Messages we send to your server when something happens."}],"security":[{"ApiKey":[]}],"paths":{"/api/v1/embed/launch-tokens":{"post":{"operationId":"createLaunchToken","tags":["Launch"],"summary":"Mint a launch token for one person","description":"Call this from your server when a person is about to take an assessment inside your product. It returns a one-time token and a ready-made `embed_url` to load in a frame on your page. Use it when you want Kirnova's own sitting screen, white-labelled to your frame. Tell us who the person is: a name and an email are required, so no result is ever left without an owner.\n\nThe token lives five minutes and works once. The person's session, once the frame loads, lasts 47 minutes. Answers already given are kept if the session ends.\n\nIf you name a `test` you do not hold, or one that cannot be shown in a frame, the call is refused with 403 and a code that says why. If you omit `test`, your account's default is used. The response always names the instrument that was chosen so you never need to guess. Needs a key that can write. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaunchTokenRequest"}}}},"responses":{"201":{"description":"Token created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaunchTokenResponse"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot write, the account is not active, or the instrument you asked for is not available to you in a frame. Codes: FORBIDDEN, TEST_NOT_ENTITLED, TEST_NONE_ENTITLED, TEST_NOT_EMBEDDABLE, TEST_NOT_SERVABLE, TEST_REQUIRED, TEST_PAYMENT_REQUIRED, TEST_NOT_CONFIGURED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"respondent_name or respondent_email is missing or invalid. Codes: VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The token could not be signed. Try again; if it persists, contact us. Codes: LAUNCH_TOKEN_FAILED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Assessment serving is switched off for the whole deployment. Nothing is wrong with your request; try again later. Codes: SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sessions":{"post":{"operationId":"createSession","tags":["Sessions"],"summary":"Start a sitting you will run yourself","description":"Call this when you want to show the questions in your own screens instead of our frame. It creates a session for one person and returns the first page of questions plus a `token` for that session. Use that session token, not your API key, to submit pages.\n\nYou must say who the person is: an email, a name, a phone number or your own `external_respondent_id`. A session with no identity is refused. The session expires 47 minutes after it is created.\n\nA new session counts against your account's daily session limit as well as the per-minute limit. Needs a key that can write.","security":[{"ApiKey":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionRequest"}}}},"responses":{"201":{"description":"Session created. The first page of questions is included.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionPage"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot write, or the instrument you asked for is not available to you through the API, or the key is being used on the wrong host. Codes: FORBIDDEN, TEST_NOT_ENTITLED, TEST_NONE_ENTITLED, TEST_NOT_EMBEDDABLE, TEST_NOT_SERVABLE, TEST_REQUIRED, TEST_PAYMENT_REQUIRED, TEST_NOT_CONFIGURED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A Kirnova assessment could not be started for this respondent right now, for example during a retake interval. No session is left open. Codes: SITTING_UNAVAILABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body is invalid, or no respondent identity was given. Codes: VALIDATION_ERROR, RESPONDENT_IDENTITY_REQUIRED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The session token could not be signed. Codes: SESSION_TOKEN_FAILED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"The chosen instrument has no page-by-page serving yet. Codes: TEST_NOT_SERVABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Serving is switched off, or the database is not available. Codes: SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sessions/{sessionId}/pages/{pageNumber}":{"post":{"operationId":"submitSessionPage","tags":["Sessions"],"summary":"Submit the answers for one page","description":"Send the respondent's answers for the page they are on. You get back the next page, or, after the last page, the finished result. Pages must be sent in order: the page number must be the current page. Each answer is a whole number from 1 to 5 (the page states its `scale`; Kirnova Cognitive uses the option number, 1 to 8), and every question on the page needs exactly one answer. A question already answered cannot be answered again.\n\nAuthenticate with the session `token` from the create-session call, not your API key. An API key here is refused. If saving the final page failed earlier but all answers are stored, send the final page number again to finish.\n\nIf the questions have been reworded since this person began, the sitting cannot continue and nothing is lost: start a new session for them. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"SessionToken":[]}],"parameters":[{"name":"sessionId","in":"path","required":true,"description":"The session_id returned when the session was created.","schema":{"type":"string"}},{"name":"pageNumber","in":"path","required":true,"description":"The current page, starting at 1.","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitPageRequest"}}}},"responses":{"200":{"description":"Answers accepted. Either the next page, or the completed result after the final page.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/SessionPage"},{"$ref":"#/components/schemas/SessionCompleted"}]}}}},"401":{"description":"The session token is missing, invalid or expired, or an API key was sent instead of a session token. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such session under your account. Codes: SESSION_NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The session is already finished, the page is not the current one, or the questions changed and the sitting cannot continue. Codes: SESSION_ALREADY_COMPLETE, SESSION_WRONG_PAGE, SESSION_CONTENT_CHANGED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The page number or the answers are invalid. Codes: VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The session could not be read, or the result could not be saved. Codes: SESSION_STATE_INVALID, SCORING_FAILED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database is not available. Codes: SERVICE_UNAVAILABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sessions/{sessionId}/results":{"get":{"operationId":"getSessionResult","tags":["Results"],"summary":"Read the result of one session","description":"Call this after a session finishes, or when you receive the `session.completed` webhook, to read the result for that person. The body depends on which instrument they sat: see `engine`.\n\nIf the person has not finished, you get 409 with `SCORING_PENDING` and a `progress` object showing how far along they are. Poll gently, or wait for the webhook.\n\nYour access to a result has a window. After it closes you get 403 `RESULT_ACCESS_EXPIRED` with the identity, instrument and dates, but never the reading. `access_until` tells you when the window ends. The person keeps their own result.\n\nAuthenticate with your API key, or with the session token for that session. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]},{"SessionToken":[]}],"parameters":[{"name":"sessionId","in":"path","required":true,"description":"The session_id returned when the session was created.","schema":{"type":"string"}}],"responses":{"200":{"description":"The result.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResult"}}}},"401":{"description":"Missing or invalid authorization. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your access window for this result has closed. Codes: RESULT_ACCESS_EXPIRED.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultAccessExpired"}}}},"404":{"description":"No such session under your account. Codes: SESSION_NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The person has not finished yet. Codes: SCORING_PENDING. A `progress` object says how far they are.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultPending"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The stored result could not be read. Codes: RESULT_UNREADABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sessions/{sessionId}/pdf":{"get":{"operationId":"downloadSessionPdf","tags":["Results"],"summary":"Download the PDF report for one session","description":"Returns the person's report as a PDF. Use it to attach the report to your own record or to show it to your user. If the report has not been made yet it is made on this call, which can take a moment. We also send a `pdf.ready` webhook when a report is ready.\n\nThe same access window as the result applies: after it closes you get 403 `RESULT_ACCESS_EXPIRED`. Depending on where the file is stored, the response is the PDF itself or a 302 redirect to a short-lived address.\n\nAuthenticate with your API key, the session token, or a signed download token in the `token` query parameter (used in links we email). Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]},{"SessionToken":[]},{}],"parameters":[{"name":"sessionId","in":"path","required":true,"description":"The session_id returned when the session was created.","schema":{"type":"string"}},{"name":"disposition","in":"query","required":false,"description":"`inline` to show in the browser. Anything else downloads the file.","schema":{"type":"string","enum":["inline","attachment"],"default":"attachment"}},{"name":"token","in":"query","required":false,"description":"A signed download token for this session, instead of an Authorization header.","schema":{"type":"string"}}],"responses":{"200":{"description":"The PDF.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"302":{"description":"The PDF is held elsewhere. Follow the Location header."},"401":{"description":"Missing or invalid authorization. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your access window for this result has closed. Codes: RESULT_ACCESS_EXPIRED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such session under your account. Codes: SESSION_NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The person has not finished yet. Codes: SCORING_PENDING.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The report could not be made or read. Codes: PDF_UNAVAILABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tenants/{tenantId}/results":{"get":{"operationId":"listTenantResults","tags":["Results"],"summary":"List finished results in a date range","description":"Use this to catch up or to reconcile: it lists every finished result for your account between two dates, newest first, with the same result body as the single-session call. Practice results (from test keys and practice sittings) are left out unless you ask for them with `environment=practice`.\n\nA result whose access window has closed is still listed, with `result_status` of `access_expired` and no reading. Filtering by `profile` only matches results you can still read. The key must belong to the account in the path. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"parameters":[{"name":"tenantId","in":"path","required":true,"description":"Your partner account id. It must be the account your API key belongs to.","schema":{"type":"string"}},{"name":"from","in":"query","required":true,"description":"Start of the range, an ISO 8601 date or date-time.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"End of the range. Defaults to now.","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"description":"Page size, 1 to 1000. Default 100.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"offset","in":"query","required":false,"description":"How many results to skip. Default 0.","schema":{"type":"integer","minimum":0,"default":0}},{"name":"profile","in":"query","required":false,"description":"Only results whose profile code matches (upper-cased for you).","schema":{"type":"string"}},{"name":"environment","in":"query","required":false,"description":"`live` (default) or `practice`.","schema":{"type":"string","enum":["live","practice"],"default":"live"}}],"responses":{"200":{"description":"A page of results.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantResultsList"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but not allowed to do this: the partner account is not active, the key is read-only for a write call, or it belongs to a different partner account. Codes: FORBIDDEN.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`from` or `to` is not a valid date, `from` is missing, or `environment` is not live or practice. Codes: VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tenants/{tenantId}/usage":{"get":{"operationId":"getTenantUsage","tags":["Account"],"summary":"See this month's usage","description":"Shows how many sessions your account started, finished and abandoned this calendar month (UTC), how many today, and your daily session limit. Practice sessions are not counted. Use it to watch your allowance. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"parameters":[{"name":"tenantId","in":"path","required":true,"description":"Your partner account id. It must be the account your API key belongs to.","schema":{"type":"string"}}],"responses":{"200":{"description":"Usage figures.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantUsage"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but not allowed to do this: the partner account is not active, the key is read-only for a write call, or it belongs to a different partner account. Codes: FORBIDDEN.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The account was not found. Codes: NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs":{"get":{"operationId":"listJobs","tags":["Jobs"],"summary":"List open jobs on your board","description":"Returns the open jobs on your job board, 'in your board's scope': either only jobs your organisation posted, or every job on Kirnova, as set on your account. Use it to build a job list in your own site. Each job has an `apply_url` (opens Kirnova, credited to you) and an `embed_url` (the job inside your frame).\n\nA page never carries a total, only `has_next_page`. If jobs are not switched on for your account the call is refused by name. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"parameters":[{"name":"q","in":"query","required":false,"description":"Words to search for in the title and description. Up to 80 characters.","schema":{"type":"string","maxLength":80}},{"name":"city","in":"query","required":false,"description":"City name. Up to 80 characters.","schema":{"type":"string","maxLength":80}},{"name":"remote","in":"query","required":false,"description":"`1` for remote jobs only.","schema":{"type":"string","enum":["1"]}},{"name":"type","in":"query","required":false,"description":"Employment type. Unknown values are ignored.","schema":{"type":"string","enum":["FULL_TIME","PART_TIME","CONTRACT","TEMPORARY","INTERNSHIP","APPRENTICESHIP","FREELANCE"]}},{"name":"experience","in":"query","required":false,"description":"Years of experience, 0 to 50.","schema":{"type":"integer","minimum":0,"maximum":50}},{"name":"salary","in":"query","required":false,"description":"Only jobs paying at least this amount.","schema":{"type":"integer","minimum":0}},{"name":"page","in":"query","required":false,"description":"Page number from 1. Bad values become 1.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1}}],"responses":{"200":{"description":"A page of jobs.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobList"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not read jobs, or jobs are not available on your account. Codes: FORBIDDEN, JOBS_NOT_ENABLED, TENANT_NOT_ACTIVE, NO_LINKED_ACCOUNT.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{slug}":{"get":{"operationId":"getJob","tags":["Jobs"],"summary":"Read one job","description":"Returns one job on your board. A job that has closed comes back with `open: false`, so you can take it down from your site instead of advertising it. A job outside your board's scope, a draft, or a job that does not exist all return 404. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"parameters":[{"name":"slug","in":"path","required":true,"description":"The job's slug, as returned in the jobs list.","schema":{"type":"string"}}],"responses":{"200":{"description":"The job.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobEnvelope"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not read jobs, or jobs are not available on your account. Codes: FORBIDDEN, JOBS_NOT_ENABLED, TENANT_NOT_ACTIVE, NO_LINKED_ACCOUNT.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such job on this board. Codes: NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{slug}/applications":{"get":{"operationId":"listJobApplications","tags":["Jobs"],"summary":"Read applications to a job you posted","description":"Returns the people who applied to a job that YOUR organisation posted. This is how you read applicants; the webhook only tells you that something happened. It works for your own jobs only, even if your board shows every job on Kirnova: a board must never learn who applied where.\n\nA person who withdrew their application is still listed, with `applicant` set to null, because withdrawing took back the permission that let you see their details. Filters and paging match the applicants screen. No total is returned. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"parameters":[{"name":"slug","in":"path","required":true,"description":"The job's slug, as returned in the jobs list.","schema":{"type":"string"}},{"name":"stage","in":"query","required":false,"description":"Only applications in this stage (case-insensitive).","schema":{"type":"string","enum":["applied","screening","core_sent","shortlisted","interview","offer","hired","not_selected","withdrawn"]}},{"name":"source","in":"query","required":false,"description":"How the person reached the job (case-insensitive).","schema":{"type":"string","enum":["self","recruiter","invited"]}},{"name":"core","in":"query","required":false,"description":"`sent` for applicants who were sent a TIGER Core, `none` for those who were not.","schema":{"type":"string","enum":["sent","none"]}},{"name":"expMin","in":"query","required":false,"description":"Minimum years of experience, 0 to 60.","schema":{"type":"integer","minimum":0,"maximum":60}},{"name":"expMax","in":"query","required":false,"description":"Maximum years of experience, 0 to 60.","schema":{"type":"integer","minimum":0,"maximum":60}},{"name":"city","in":"query","required":false,"description":"City contains this text. Up to 80 characters.","schema":{"type":"string","maxLength":80}},{"name":"notice","in":"query","required":false,"description":"Notice period of at most this many days, up to 365.","schema":{"type":"integer","minimum":0,"maximum":365}},{"name":"currentPay","in":"query","required":false,"description":"Current pay of at most this amount.","schema":{"type":"integer","minimum":0}},{"name":"expectedPay","in":"query","required":false,"description":"Expected pay of at most this amount.","schema":{"type":"integer","minimum":0}},{"name":"from","in":"query","required":false,"description":"Applied on or after this day (YYYY-MM-DD, UTC).","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"description":"Applied on or before this day (YYYY-MM-DD, UTC).","schema":{"type":"string","format":"date"}},{"name":"sort","in":"query","required":false,"description":"`newest` (default) or `oldest`.","schema":{"type":"string","enum":["newest","oldest"],"default":"newest"}},{"name":"page","in":"query","required":false,"description":"Page number from 1. A page holds up to 25 applications.","schema":{"type":"integer","minimum":1,"maximum":10000,"default":1}}],"responses":{"200":{"description":"A page of applications.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationList"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The job was not posted by your organisation, or jobs are not available on your account. Codes: FORBIDDEN, JOBS_NOT_ENABLED, TENANT_NOT_ACTIVE, NO_LINKED_ACCOUNT.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such job. Codes: NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{slug}/invitations":{"post":{"operationId":"inviteToApply","tags":["Jobs"],"summary":"Invite one person to apply to a job you posted","description":"Invites a person, by email, to apply to a job your organisation posted. The invitation is the same one the organisation's own screen sends. The person is still asked to sign in and complete their steps on Kirnova before applying: an invitation opens the door and does not walk anyone through it. The link records that they came through you.\n\nKirnova is for adults, so you must confirm the person is 18 or over by sending `confirm_adult: true`. Set `send_email` to false if you want to send the link yourself. The link expires when the job ends or after 30 days. Needs a key that can write. Counts against your account's per-minute request limit. The current limit and what is left are in the X-RateLimit-* response headers.","security":[{"ApiKey":[]}],"parameters":[{"name":"slug","in":"path","required":true,"description":"The job's slug, as returned in the jobs list.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationRequest"}}}},"responses":{"201":{"description":"Invitation created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"401":{"description":"The API key is missing, malformed, unknown, revoked or expired. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot write, the job was not posted by your organisation, your organisation is not approved by Kirnova yet, or jobs are not available on your account. Codes: FORBIDDEN, ACCOUNT_NOT_APPROVED, JOBS_NOT_ENABLED, TENANT_NOT_ACTIVE, NO_LINKED_ACCOUNT.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such job. Codes: NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"This person cannot be invited to this job right now (for example, the job has ended). Codes: NOT_INVITABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The email is missing or invalid, or the 18-or-over confirmation is missing. Codes: VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait the number of seconds in the Retry-After header, then try again. Codes: RATE_LIMIT_EXCEEDED (per-minute limit), TENANT_DAILY_LIMIT (daily session limit).","headers":{"Retry-After":{"description":"Seconds to wait before the next request.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"session.completed":{"post":{"operationId":"webhookSessionCompleted","summary":"A person finished an assessment","description":"Sent to your webhook address when a respondent finishes. Subscribe to this event in your partner settings. Use it to fetch the result and update your own record. We try to send it as soon as the result is saved, and failed deliveries are retried. See the `Signature` details under Webhooks in the API description.\n\nThe `data` object carries the person's identity and the instrument. For the original TIGER assessment it carries the reading directly. For the current instruments it carries a `result` object, and `test` names the instrument. If your access window for the result has already closed, the message is not sent.","security":[],"parameters":[{"name":"x-bi-webhook-event","in":"header","required":true,"description":"The event name, for example `session.completed`.","schema":{"type":"string"}},{"name":"x-bi-webhook-signature","in":"header","required":true,"description":"`sha256=` followed by the hex HMAC-SHA256 of the raw request body, keyed with your webhook secret. Compute it over the exact bytes you received, then compare in constant time.","schema":{"type":"string","pattern":"^sha256=[0-9a-f]{64}$"}},{"name":"x-bi-webhook-timestamp","in":"header","required":true,"description":"When this attempt was made, in seconds since 1970 (UTC). The same delivery carries a fresh timestamp on each retry. The signature does not cover it.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSessionCompleted"}}}},"responses":{"2XX":{"description":"Return any 2xx status to confirm. Anything else counts as a failure and is retried."}}}},"session.abandoned":{"post":{"operationId":"webhookSessionAbandoned","summary":"A person left without finishing","description":"Sent when a respondent abandons a session. Use it to offer them another go. It reports the last page they reached. Same headers, signature and retries as every webhook.","security":[],"parameters":[{"name":"x-bi-webhook-event","in":"header","required":true,"schema":{"type":"string"},"description":"The event name."},{"name":"x-bi-webhook-signature","in":"header","required":true,"schema":{"type":"string","pattern":"^sha256=[0-9a-f]{64}$"},"description":"`sha256=` plus the hex HMAC-SHA256 of the raw body."},{"name":"x-bi-webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Seconds since 1970 (UTC) of this attempt."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSessionAbandoned"}}}},"responses":{"2XX":{"description":"Return any 2xx status to confirm."}}}},"pdf.ready":{"post":{"operationId":"webhookPdfReady","summary":"A report is ready to download","description":"Sent when a person's PDF report has been made. It carries a link and the time the link stops working. If your access window for the result has closed, the message is not sent. You can also fetch the report any time with the PDF endpoint. Same headers, signature and retries as every webhook.","security":[],"parameters":[{"name":"x-bi-webhook-event","in":"header","required":true,"schema":{"type":"string"},"description":"The event name."},{"name":"x-bi-webhook-signature","in":"header","required":true,"schema":{"type":"string","pattern":"^sha256=[0-9a-f]{64}$"},"description":"`sha256=` plus the hex HMAC-SHA256 of the raw body."},{"name":"x-bi-webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Seconds since 1970 (UTC) of this attempt."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPdfReady"}}}},"responses":{"2XX":{"description":"Return any 2xx status to confirm."}}}},"job_application.submitted":{"post":{"operationId":"webhookJobApplicationSubmitted","summary":"Someone applied to a job you posted","description":"Sent when a person applies to a job your organisation posted. It is not sent to a board that only displayed the job. It carries no personal details: read the person with the applications endpoint, where your key and the person's permission are checked on every call. Same headers, signature and retries as every webhook.","security":[],"parameters":[{"name":"x-bi-webhook-event","in":"header","required":true,"schema":{"type":"string"},"description":"The event name."},{"name":"x-bi-webhook-signature","in":"header","required":true,"schema":{"type":"string","pattern":"^sha256=[0-9a-f]{64}$"},"description":"`sha256=` plus the hex HMAC-SHA256 of the raw body."},{"name":"x-bi-webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Seconds since 1970 (UTC) of this attempt."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookJobApplication"}}}},"responses":{"2XX":{"description":"Return any 2xx status to confirm."}}}},"job_application.stage_changed":{"post":{"operationId":"webhookJobApplicationStageChanged","summary":"An application moved to a new stage","description":"Sent when an application to a job your organisation posted changes stage. `previous_stage` says where it came from. Carries no personal details. Same headers, signature and retries as every webhook.","security":[],"parameters":[{"name":"x-bi-webhook-event","in":"header","required":true,"schema":{"type":"string"},"description":"The event name."},{"name":"x-bi-webhook-signature","in":"header","required":true,"schema":{"type":"string","pattern":"^sha256=[0-9a-f]{64}$"},"description":"`sha256=` plus the hex HMAC-SHA256 of the raw body."},{"name":"x-bi-webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Seconds since 1970 (UTC) of this attempt."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookJobApplication"}}}},"responses":{"2XX":{"description":"Return any 2xx status to confirm."}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","bearerFormat":"bi_(live|test)_sk_*","description":"Your secret API key, sent as `Authorization: Bearer bi_live_sk_...` (or `bi_test_sk_...` for a test key). A key is either read-only or able to write; calls that start things need a key that can write. Keep it on your server."},"SessionToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"The short-lived `token` returned when you create a session, sent as `Authorization: Bearer <token>`. It works only for that one session and lasts as long as the session (47 minutes)."}},"headers":{"X-RateLimit-Limit":{"description":"How many requests your account may make in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"How many requests are left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in seconds since 1970 (UTC).","schema":{"type":"integer"}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"A stable machine-readable code. Branch on this, never on the message."},"message":{"type":"string","description":"A short sentence in plain English. Its wording can change."},"field":{"type":"string","description":"The request field the problem is about, when there is one."}}}}},"LaunchTokenRequest":{"type":"object","required":["respondent_name","respondent_email"],"properties":{"respondent_name":{"type":"string","minLength":1,"description":"The person's name."},"respondent_email":{"type":"string","format":"email","description":"The person's email. Stored in lower case."},"respondent_phone":{"type":"string","description":"Optional phone number. Not checked for format."},"test":{"type":"string","description":"The instrument to give, by its short name. Omit it to use your account's default."},"external_respondent_id":{"type":"string","description":"Your own id for this person. Comes back on results and webhooks."},"metadata":{"type":"object","additionalProperties":true,"description":"Anything you want carried through to results and webhooks, for example your application id."},"callback_url":{"type":"string","format":"uri","description":"Where to send the person after they finish."},"embed_options":{"type":"object","properties":{"hide_results":{"type":"boolean","description":"Do not show the person their result at the end. The setting is signed into the token so it cannot be undone from the address bar."},"theme":{"type":"string","enum":["light","dark"]},"disable_pdf":{"type":"boolean","description":"Hide the PDF download inside the frame."}}}}},"LaunchTokenResponse":{"type":"object","required":["token","expires_at","embed_url","tenant_id","test"],"properties":{"token":{"type":"string","description":"The one-time launch token. Valid for five minutes."},"expires_at":{"type":"string","format":"date-time"},"embed_url":{"type":"string","format":"uri","description":"Load this in a frame on your page."},"tenant_id":{"type":"string","description":"Your partner account id."},"test":{"type":"string","description":"The instrument this launch will give, even if you named it yourself."}}},"CreateSessionRequest":{"type":"object","description":"At least one of respondent_email, respondent_phone, respondent_name or external_respondent_id must identify the person. The name \"Participant\" counts as no name.","properties":{"respondent_name":{"type":"string"},"respondent_email":{"type":"string","format":"email"},"respondent_phone":{"type":"string","description":"Accepted, but email is preferred. Not checked for format."},"test":{"type":"string","description":"The instrument to give. Omit it to use your account's default."},"external_respondent_id":{"type":"string","description":"Your own id for this person."},"metadata":{"type":"object","additionalProperties":true}}},"Question":{"type":"object","required":["position","id","text","page","position_on_page"],"properties":{"position":{"type":"integer","description":"Place in the whole sitting."},"id":{"type":"string","description":"Send this as `question_id` when answering."},"text":{"type":"string","description":"The statement to show the person, word for word."},"page":{"type":"integer"},"position_on_page":{"type":"integer"},"art":{"type":"object","description":"Kirnova Cognitive only. The puzzle picture and its eight options, as absolute addresses. `options` is one sheet (4 across, 2 down) for `png`, or a list of eight pictures for `svg`.","additionalProperties":true},"option_order":{"type":"array","items":{"type":"integer"},"description":"Kirnova Cognitive only. The order to show the eight options in. Send back the option number, never the position."}}},"SessionPage":{"type":"object","required":["session_id","status","current_page","total_pages","questions"],"properties":{"session_id":{"type":"string"},"token":{"type":"string","description":"Session token. Returned only when the session is created. Use it for page submissions."},"status":{"type":"string","enum":["initiated","in_progress"]},"respondent_id":{"type":"string","description":"Returned only when the session is created: your external id, else the email, else the session id."},"submitted_page":{"type":"integer","description":"The page just accepted. Absent on a new session."},"current_page":{"type":"integer","description":"The page to send next."},"total_pages":{"type":"integer"},"progress_pct":{"type":"number","description":"Share of pages submitted, 0 to 100. Present after the first submission."},"section_label":{"type":["string","null"],"description":"A heading for the page. Null for the current instruments, which have no sections."},"page_instruction":{"type":["string","null"],"description":"An instruction for the page. Null for the current instruments."},"questions":{"type":"array","items":{"$ref":"#/components/schemas/Question"}},"scale":{"type":"object","description":"Kirnova assessments only. The legal answer range: 1 to 5, 1 to 3 on the 180-activity Kirnova Calling length, 1 to 8 on Kirnova Cognitive.","additionalProperties":true},"response_labels":{"type":"array","items":{"type":"string"},"description":"Kirnova assessments with fixed anchors. Show them under the answer buttons."},"licence":{"type":"object","description":"Kirnova Calling only, on every page. The attribution, modification notice and warranty notice the licence requires wherever the questions are shown.","additionalProperties":true},"timing":{"type":"object","description":"Kirnova Cognitive only. The single clock over the whole test: `time_limit_seconds` and `deadline_epoch_ms` on Kirnova's clock.","additionalProperties":true}}},"SubmitPageRequest":{"type":"object","required":["responses"],"properties":{"responses":{"type":"array","description":"Exactly one answer for every question on the page.","items":{"type":"object","required":["question_id","value"],"properties":{"question_id":{"type":"string","minLength":1},"value":{"type":"integer","minimum":1,"maximum":8,"description":"1 to 5 for the questionnaires (1 to 3 on the 180-activity Kirnova Calling length). On Kirnova Cognitive, the option number, 1 to 8. The `scale` key on each page states the range."}}}}}},"SessionCompleted":{"type":"object","required":["session_id","status","submitted_page","results"],"properties":{"session_id":{"type":"string"},"status":{"type":"string","enum":["completed"]},"submitted_page":{"type":"integer"},"results":{"$ref":"#/components/schemas/ResultBody"}}},"ResultEngine":{"type":"string","enum":["legacy","tiger","kirnova-calling","kirnova-disc","kirnova-hexaco","kirnova-orvis","kirnova-cognitive"],"description":"Which instrument family produced the result, and so which shape `results` has. `legacy` is the original TIGER assessment, retired for new sittings but still readable. `tiger` covers TIGER Core and TIGER Student. The others are the standalone Kirnova instruments."},"ResultBody":{"type":"object","additionalProperties":true,"description":"The result. Its keys depend on the instrument (`engine`). Every shape is plain data meant to be shown or stored as is: scores on a 0 to 100 scale or named bands, descriptive text, and a `pdf` object. It never contains internal workings. Treat unknown keys as additions and ignore them. For `legacy`, the keys are `disc` and `tiger`. For the current instruments, the keys are described in your integration guide for that instrument. A TIGER Student result also carries `merged_reading` (empty for every other instrument, and empty when the sitting's purpose is hiring, promotion, other or not recorded; present for placement, development and self-discovery): the jobs built around how the person works, written to the person and marked `for_the_person`. It is for showing to the person you serve, not for choosing between people, and its `licence` lines must be shown beside it.","properties":{"pdf":{"$ref":"#/components/schemas/PdfBlock"},"engine":{"$ref":"#/components/schemas/ResultEngine"}}},"PdfBlock":{"type":"object","properties":{"available":{"type":"boolean"},"url":{"type":["string","null"],"format":"uri"},"expires_at":{"type":["string","null"],"format":"date-time"}}},"SessionResult":{"type":"object","required":["session_id","status","engine","result_status","results"],"properties":{"session_id":{"type":"string"},"status":{"type":"string","description":"The session status in lower case, for example `completed`."},"respondent_name":{"type":["string","null"]},"respondent_email":{"type":["string","null"]},"external_respondent_id":{"type":["string","null"]},"metadata":{"type":"object","additionalProperties":true},"completed_at":{"type":"string","format":"date-time"},"result_updated_at":{"type":"string","format":"date-time","description":"Changes if the result was later re-read against more of the person's information."},"ai_enrichment_status":{"type":["string","null"],"description":"Kept for older integrations. Expect null."},"engine":{"$ref":"#/components/schemas/ResultEngine"},"result_status":{"type":"string","enum":["available"]},"access_until":{"type":"string","format":"date-time","description":"The last moment you may read this result."},"results":{"$ref":"#/components/schemas/ResultBody"}}},"ResultAccessExpired":{"type":"object","description":"The result exists but your access to it has ended. No reading is returned.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["RESULT_ACCESS_EXPIRED"]},"message":{"type":"string"}}},"session_id":{"type":"string"},"status":{"type":"string"},"external_respondent_id":{"type":["string","null"]},"engine":{"oneOf":[{"$ref":"#/components/schemas/ResultEngine"},{"type":"null"}]},"completed_at":{"type":"string","format":"date-time"},"result_status":{"type":"string","enum":["access_expired"]},"access_expired_at":{"type":"string","format":"date-time"}}},"ResultPending":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["SCORING_PENDING"]},"message":{"type":"string"}}},"progress":{"type":["object","null"],"additionalProperties":true,"description":"How far through the person is. It never carries an answer or a score. Treat the exact keys as additive."}}},"TenantResultRow":{"type":"object","required":["session_id","completed_at","engine"],"properties":{"session_id":{"type":"string"},"respondent_name":{"type":["string","null"]},"external_respondent_id":{"type":["string","null"]},"completed_at":{"type":"string","format":"date-time"},"result_updated_at":{"type":"string","format":"date-time"},"profile":{"type":["string","null"],"description":"Profile code. Null once your access has closed."},"engine":{"oneOf":[{"$ref":"#/components/schemas/ResultEngine"},{"type":"null"}]},"result_status":{"type":"string","enum":["available","access_expired"]},"access_until":{"type":"string","format":"date-time"},"access_expired":{"type":"boolean","enum":[true],"description":"Present only when your access has closed."},"access_expired_at":{"type":"string","format":"date-time"},"unreadable":{"type":"boolean","enum":[true],"description":"Present only when this one stored result could not be read. The rest of the page is unaffected."},"disc":{"description":"Legacy results only."},"tiger":{"description":"Legacy results only."},"results":{"description":"The result body for the current instruments. Null when access has closed or the result is unreadable."}}},"TenantResultsList":{"type":"object","required":["count","limit","offset","environment","data"],"properties":{"count":{"type":"integer","description":"How many results match the range and filters, across all pages."},"limit":{"type":"integer"},"offset":{"type":"integer"},"environment":{"type":"string","enum":["live","practice"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/TenantResultRow"}}}},"TenantUsage":{"type":"object","required":["tenant_id","period","sessions","daily_limit","today_count"],"properties":{"tenant_id":{"type":"string"},"period":{"type":"string","description":"The calendar month, YYYY-MM (UTC)."},"sessions":{"type":"object","properties":{"total":{"type":"integer"},"completed":{"type":"integer"},"abandoned":{"type":"integer"},"completion_rate_pct":{"type":"number","description":"Completed as a share of total, one decimal place."}}},"daily_limit":{"type":["integer","null"],"description":"Sessions you may start per day. Null means no daily limit."},"today_count":{"type":"integer","description":"Sessions started today (UTC)."}}},"Job":{"type":"object","required":["slug","open","title","organisation","public_url","apply_url","embed_url"],"properties":{"slug":{"type":"string"},"open":{"type":"boolean","description":"False once the job has closed."},"title":{"type":"string"},"organisation":{"type":"string"},"organisation_line":{"type":"string","description":"How the organisation is described on the job page. Respects a hidden end company."},"description":{"type":"string"},"city":{"type":["string","null"]},"country_code":{"type":["string","null"]},"work_mode":{"type":["string","null"]},"employment_type":{"type":["string","null"]},"experience_min_years":{"type":["integer","null"]},"experience_max_years":{"type":["integer","null"]},"openings":{"type":"integer"},"salary":{"type":"object","properties":{"min":{"type":["number","null"]},"max":{"type":["number","null"]},"currency":{"type":["string","null"]},"period":{"type":["string","null"]},"text":{"type":["string","null"]}}},"skills":{"type":"array","items":{"type":"string"}},"closing_date":{"type":["string","null"],"format":"date"},"posted_at":{"type":["string","null"],"format":"date-time"},"valid_through":{"type":["string","null"],"format":"date-time"},"public_url":{"type":"string","format":"uri","description":"The job's public page."},"apply_url":{"type":"string","format":"uri","description":"Applying happens on Kirnova, credited to you. Open it in a new tab or window."},"embed_url":{"type":"string","format":"uri","description":"The job inside your own job-board frame."}}},"JobList":{"type":"object","required":["jobs","page","has_next_page"],"properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"page":{"type":"integer"},"has_next_page":{"type":"boolean"}}},"JobEnvelope":{"type":"object","required":["job"],"properties":{"job":{"$ref":"#/components/schemas/Job"}}},"Application":{"type":"object","required":["id","stage","applied_at","routes","core_sent","stage_history","applicant"],"properties":{"id":{"type":"string"},"stage":{"type":"string","description":"Lower case: applied, screening, core_sent, shortlisted, interview, offer, hired, not_selected or withdrawn."},"applied_at":{"type":"string","format":"date-time"},"routes":{"type":"array","items":{"type":"string"},"description":"How the person reached the job: self, recruiter, invited. Partner-site traffic is marked only when it was your own board."},"core_sent":{"type":"boolean","description":"Whether a TIGER Core was sent to this applicant."},"core_status":{"type":["string","null"]},"stage_history":{"type":"array","items":{"type":"object","properties":{"from":{"type":["string","null"]},"to":{"type":"string"},"at":{"type":"string","format":"date-time"}}}},"applicant":{"type":["object","null"],"description":"Null once the person withdrew.","properties":{"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"city":{"type":["string","null"]},"experience_years":{"type":["number","null"]},"notice_period_days":{"type":"integer"},"current_pay":{"type":["number","null"]},"expected_pay":{"type":["number","null"]},"pay_currency":{"type":["string","null"]}}}}},"ApplicationList":{"type":"object","required":["applications","page","has_next_page"],"properties":{"applications":{"type":"array","items":{"$ref":"#/components/schemas/Application"}},"page":{"type":"integer"},"has_next_page":{"type":"boolean"}}},"InvitationRequest":{"type":"object","required":["email","confirm_adult"],"properties":{"email":{"type":"string","format":"email","maxLength":320},"send_email":{"type":"boolean","default":true,"description":"False if you will send the link yourself. The invitation is the same either way."},"confirm_adult":{"type":"boolean","description":"Must be true: you confirm this person is 18 or over."}}},"InvitationResponse":{"type":"object","required":["invitation_url","expires_at","emailed"],"properties":{"invitation_url":{"type":"string","format":"uri","description":"The person's invitation link. Using it credits your board."},"expires_at":{"type":"string","format":"date-time"},"emailed":{"type":"boolean","description":"Whether we sent the email."}}},"WebhookEnvelope":{"type":"object","required":["event","timestamp","tenant_id","data"],"properties":{"event":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"tenant_id":{"type":"string","description":"Your partner account id."},"data":{"type":"object","additionalProperties":true}}},"WebhookSessionCompleted":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"event":{"type":"string","enum":["session.completed"]},"data":{"type":"object","additionalProperties":true,"properties":{"session_id":{"type":"string"},"respondent_name":{"type":["string","null"]},"respondent_email":{"type":["string","null"]},"external_respondent_id":{"type":["string","null"]},"metadata":{"type":"object","additionalProperties":true},"completed_at":{"type":"string","format":"date-time"},"test":{"type":"string","description":"The instrument, for the current instruments."},"result":{"type":"object","additionalProperties":true,"description":"The result body, for the current instruments."}}}}}]},"WebhookSessionAbandoned":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"event":{"type":"string","enum":["session.abandoned"]},"data":{"type":"object","properties":{"session_id":{"type":"string"},"respondent_name":{"type":["string","null"]},"external_respondent_id":{"type":["string","null"]},"metadata":{"type":"object","additionalProperties":true},"page_reached":{"type":["integer","null"]},"abandoned_at":{"type":"string","format":"date-time"}}}}}]},"WebhookPdfReady":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"event":{"type":"string","enum":["pdf.ready"]},"data":{"type":"object","properties":{"session_id":{"type":"string"},"respondent_name":{"type":["string","null"]},"external_respondent_id":{"type":["string","null"]},"metadata":{"type":"object","additionalProperties":true},"completed_at":{"type":"string","format":"date-time"},"pdf_url":{"type":"string","format":"uri"},"pdf_expires_at":{"type":["string","null"],"format":"date-time"}}}}}]},"WebhookJobApplication":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelope"},{"type":"object","properties":{"event":{"type":"string","enum":["job_application.submitted","job_application.stage_changed"]},"data":{"type":"object","description":"No personal details. Read the person through the applications endpoint.","properties":{"job_slug":{"type":"string"},"application_id":{"type":"string"},"stage":{"type":"string"},"previous_stage":{"type":["string","null"]},"at":{"type":"string","format":"date-time"}}}}}]}}},"x-webhook-delivery":{"description":"Webhooks are POSTed as JSON to the address set in your partner settings, for the events you subscribed to. Each request carries x-bi-webhook-event, x-bi-webhook-signature (`sha256=` plus the hex HMAC-SHA256 of the raw body, keyed with your webhook secret) and x-bi-webhook-timestamp. Answer with any 2xx status to confirm; each attempt times out after 30 seconds. A failed delivery is retried on this schedule, measured from the previous failure: straight away, then after 1 minute, 5 minutes, 30 minutes and 2 hours, five attempts in all. After the last attempt the delivery is marked failed; you can read missed results with the results endpoints. A daily sweep also picks up anything left unsent. Your server may see the same delivery more than once, so handle repeats.","attempts":5,"retryDelaysSeconds":[0,60,300,1800,7200],"signatureHeader":"x-bi-webhook-signature","signatureAlgorithm":"HMAC-SHA256"}}