Help center
https://api.certo.bio/v1openapi.json
Ask to read a passport
POST
/v1/access-requestsscope hiring:askAsks the holder of a published passport to let the organisation read it, by its public address. One credit is held (none for a subscribed or sponsored passport) and comes back if they decline or nobody answers in 30 days. Owners, admins and recruiters. Requires an Idempotency-Key; try it first with ?dry_run=true.
As an MCP tool: ask_to_read_passport
Query parameters
| Name | Type | Description |
|---|---|---|
| dry_run | boolean | true: the same checks and the same answer per row, and the credits it would take; nothing is written, sent or spent. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| passport | string | Yes | The passport's public address: certo.bio/<address>, https://certo.bio/<address> or <address>. |
| scope_types | array of one of employment, education, certification, role, language, right_to_work or null | What the candidate is asked to share: record types, or null (or every one) for all of them. | |
| include_contact | boolean | Ask for their contact details too. | |
| include_birth_date | boolean | Ask for their date of birth too (with birth_date_reason). | |
| birth_date_reason | string or null | Why the date of birth is needed, 3 to 280 characters; required with include_birth_date. The candidate reads it. | |
| requirements | object or null | What the candidate must have ready, docs/request-requirements.md: {"v": 1, "work_years", "education_min_level", "certifications", "right_to_work", "contact"}; null for the defaults. | |
| note | string or null | Why you ask, at most 280 characters: the holder reads it. | |
| position_id | uuid or null | An open position of the organisation it is for. | |
| custom_data | object or null | Your own data on the ask: any JSON object (an id from your ATS or HR system, a requisition number), at most 4096 bytes. certo stores it and answers it back on the reads and in your webhook events; it never reads it, and the candidate never sees it. The holder never sees it. |
Response fields
- dry_run
- boolean. Whether this was a dry run.
- access_request
- object. The ask (would be made, in a dry run).
- access_request.id
- uuid or null. The ask's id; null in a dry run (nothing was made).
- access_request.passport_url
- string. The passport asked about.
- access_request.status
- one of open. open once made.
- access_request.funding
- one of credit, free. credit: one credit held, back if it is not approved; free: the passport is subscribed or sponsored and reading it costs nothing.
- access_request.position_id
- uuid or null. The position it is under.
- access_request.custom_data
- object or null. Your own data on the ask, as you sent it; null for none.
- access_request.expires_at
- string or null. When it expires unanswered (30 days); null in a dry run.
- credits
- object. The credits.
- credits.needed
- integer. Credits the rows to send take: one per new invitation (or graduate), none for already.
- credits.spent
- integer. Credits spent now (0 in a dry run).
- credits.balance
- integer. The balance after the call (before it, in a dry run).
Errors
- 400 access_request.passport_invalid
- 404 access_request.not_askable
- 409 access_request.own_passport
- 409 access_request.already_open
- 409 access_request.already_granted
- 400 access_request.scope_invalid
- 400 access_request.reason_too_long
- 400 access_request.birth_date_reason_required
- 400 company_request.requirements_invalid
- 409 organisation.profile_incomplete
- 409 position.closed_or_missing
- 402 credit.none_left
- 400 request.unknown_field
- 400 api.idempotency_key_required
- 409 api.idempotency_mismatch
- 409 api.idempotency_in_progress
- 400 api.credential_in_url
- 401 api.key_invalid
- 401 api.key_expired
- 401 api.key_revoked
- 401 api.key_suspended
- 403 api.scope_missing
- 429 api.rate_limited