Verify the account holder
Renting a phone line requires a verified account holder: a CNPJ or a CPF, a photo or PDF of the document that shows it, and a confirmed contact. It is asked for once per workspace, before the first purchase. These endpoints do it without the dashboard — and it is the same verification as the dashboard’s (Phone lines → Buy phone lines): whichever you use, the other sees the result.Requires a tenant-scoped key, like every
/v1/phone-lines endpoint. With an OAuth / MCP token, only the workspace Owner may verify — the permission is phone_lines:purchase, the same as buying — and any other role gets 403 PERMISSION_DENIED. The state is sent with Cache-Control: no-store.How it works
1
Submit the data and the document
POST /v1/phone-lines/verification with the CNPJ or CPF, a contact e-mail, a WhatsApp number and the document file. We check the number (check digits; a CNPJ must also be found and active in the public registry), send the contact codes, store the file privately and read the number printed on it.2
Confirm the contact codes
Each channel in
contact.requiredChannels receives a 6-digit code. Confirm each one with POST …/contact/{channel}/confirm.3
Read the decision
As soon as the last code is confirmed and the document is in, the verification is decided — in that same response:
APPROVED, or IN_REVIEW when a person needs to look at it. Both let you buy (canPurchase: true). The decision is also delivered as the phone_line.verification_updated webhook event.EMAIL is always required, sent to contactEmail. WHATSAPP is required too whenever Pilot Status is sending WhatsApp codes — then both are. contact.requiredChannels says exactly which, and the list is frozen when the verification starts.
The decision. APPROVED when the number read from the document is the number you submitted and the public registry could be consulted (a CPF has no registry: the document decides). Otherwise it waits for a person — IN_REVIEW — who approves it, rejects it, or asks for another document (NEEDS_DOCUMENT).
Submit
POST https://pilotstatus.com.br/v1/phone-lines/verification — answers 201 with the state, in PENDING_CONTACT: the codes are on their way.
string
required
Any string up to 255 characters with no control characters — a UUID per submission. Missing or blank → 400
IDEMPOTENCY_KEY_REQUIRED; longer, or with a control character → 400 IDEMPOTENCY_KEY_INVALID. See Idempotency.string
required
"CNPJ" or "CPF".string
required
The CNPJ or CPF, with or without punctuation (
11.222.333/0001-81 or 11222333000181). Alphanumeric CNPJs are accepted.string
required
The account holder’s contact e-mail, up to 254 characters. It receives a code.
string
required
The account holder’s WhatsApp number in international format — country code, area code and number, e.g.
+55 11 90000-0000; punctuation is ignored. It receives a code when WHATSAPP is required.string
required
The document file as a base64 data URI:
data:<type>;base64,<content>. Types: application/pdf, image/jpeg, image/png, image/webp; at most 10 MB of file (about 14 MB once encoded). For a CNPJ: the CNPJ card (Cartão CNPJ), the articles of incorporation or the registration proof. For a CPF: an ID card (RG), a driver license (CNH) or the CPF registration proof, with the number readable.UNKNOWN_FIELDS. The file is checked before anything starts: a wrong type or size sends no code and creates nothing.
base64 -w0 on Linux, base64 -i file on macOS).
Submitting again. While the verification is still PENDING_CONTACT, a new submit replaces it — use it to fix a typo: the codes already sent stop working and new ones go out. Once it moved on, a submit answers 409 VERIFICATION_WRONG_STATE (IN_REVIEW or APPROVED — you can already buy — or NEEDS_DOCUMENT — send the document instead) or 403 VERIFICATION_REJECTED (talk to support).
Confirm a code
POST https://pilotstatus.com.br/v1/phone-lines/verification/contact/{channel}/confirm — {channel} is whatsapp or email, lowercase (anything else → 400 INVALID_CONTACT_CHANNEL).
string
required
The 6-digit code received on that channel.
200 with the state. When it was the last required code and the document is in, the state already carries the decision.
- A code expires 10 minutes after it was sent (400
CONTACT_CODE_EXPIRED). - A wrong code is 400
CONTACT_CODE_INVALIDand counts as an attempt; after 5 wrong attempts the code is locked (429CONTACT_CODE_TOO_MANY_ATTEMPTS) — send a new one. - A code is used once. Retrying a confirm that already succeeded answers
CONTACT_CODE_INVALID: read the state withGETinstead. - A channel not in
requiredChannelsis 400CONTACT_CODE_NOT_REQUIRED. No verification yet is 404VERIFICATION_NOT_FOUND.
Send a new code
POST https://pilotstatus.com.br/v1/phone-lines/verification/contact/{channel}/send — no body. Sends a new code on a required channel and answers 200 with the state. The new code replaces the previous one.
The first codes are sent by the submit itself: call this for a code that expired, was locked, or did not arrive.
- At most one code per channel every 60 seconds, counting the one the submit sent (429
CONTACT_CODE_COOLDOWN). - Only while the verification is
PENDING_CONTACT(409VERIFICATION_WRONG_STATEafterwards), and only on a channel inrequiredChannels(400CONTACT_CODE_NOT_REQUIRED). - 503
SUPPLIER_UNAVAILABLE: the code could not be queued for sending. Nothing counts against the cooldown — try again.
Send a new document
POST https://pilotstatus.com.br/v1/phone-lines/verification/document with { "document": "data:…;base64,…" } — the same file rules as the submit, and it counts against the same submission limit. It replaces the document of the current verification and answers 200 with the state.
It is accepted in two states:
PENDING_CONTACT— to replace the document you sent. Do it whendocument.statusisUNREADABLEorUNCLEAR: we could not read a number we trust from the file, so send a sharper photo or the PDF. If you confirm the last code without replacing it, the verification goes to manual review (IN_REVIEW,reviewReason: "DOCUMENT_CHECK").NEEDS_DOCUMENT— the reviewer asked for another document (reviewNotemay say which). The contact codes were already confirmed, so the new document takes the verification straight back to a decision, in this same response.
VERIFICATION_WRONG_STATE; with no verification, 404 VERIFICATION_NOT_FOUND.
Read the state
GET https://pilotstatus.com.br/v1/phone-lines/verification answers 200 with the state of the workspace’s current verification. Before any submit:
The state object
Every endpoint on this page answers with it.string
NONE, PENDING_CONTACT, IN_REVIEW, APPROVED, NEEDS_DOCUMENT or REJECTED — see the table below. The phone_line.verification_updated webhook uses the same values for the last four.boolean
Whether
POST /v1/phone-lines is allowed now: true for APPROVED and IN_REVIEW.object | null
null for NONE.string
Identifies the verification — the
verificationId of the webhook event, and what support will ask for.string
CNPJ or CPF.string
Masked:
**.***.333/0001-** (CNPJ) or ***.456.789-** (CPF). The full number is never returned.string | null
The company name from the public registry (CNPJ).
null for a CPF, and when the registry could not be consulted.string
The contact e-mail, masked (
a***@example.com).string | null
The contact WhatsApp, masked (
+5511****0000).string[]
WHATSAPP and/or EMAIL: the codes this verification needs.string[]
The required channels already confirmed.
string[]
The required channels still waiting for their code.
string
RECEIVED, UNREADABLE, UNCLEAR, REQUESTED or MISSING — see the table below.string | null
Only while
IN_REVIEW: REGISTRY_UNAVAILABLE (the CNPJ registry could not be consulted) or DOCUMENT_CHECK (the document needs a look). null otherwise.string | null
The reviewer’s message, on
REJECTED and NEEDS_DOCUMENT only. null otherwise, and it may be null there too.string
ISO 8601 — when this verification was submitted.
string
ISO 8601 — its last change.
Idempotency
The submit requires anIdempotency-Key, because it has effects a retry must not repeat blindly: it sends the codes, and a second submit replaces the first (the codes just sent stop working). For 24 hours:
- Same key, same request, first one finished →
200(not201) with the workspace’s current state and the headerIdempotent-Replayed: true. Nothing is sent or read again. - Same key while the first is still running → 409
IDEMPOTENCY_KEY_IN_USE. Wait, then send it again to get its result. - Same key, different request (any field, the file included) → 409
IDEMPOTENCY_KEY_REUSED. Use a new key for new data. - A request refused as a whole (any
4xxor5xxanswer) keeps nothing: the same key can be sent again, and runs.
Limits
- 10 submissions per hour per workspace, the submit and the document together → 429
RATE_LIMITED, with aRetry-Afterheader (seconds) andretryAfterSecondsin the body. A request refused by validation before it runs — a wrong field, type or file — does not count. - Contact codes: valid 10 minutes, 5 wrong attempts each, one per channel every 60 seconds.
- The file: PDF, JPEG, PNG or WEBP, 10 MB at most.
Privacy
The document is personal data. It is stored in a private bucket, only Pilot Status reviewers can open it (every opening is logged), and no endpoint returns it — nor the full document number, nor what was read from the file. The responses mask the document number and the contacts, and never return a CPF holder’s name.Errors
The envelope is the one of every phone lines error:{ "error": "English. | Português.", "code": "…" } — branch on code. Validation errors may add details (unknownFields, maxLength).
Plus the errors of every phone lines endpoint:
401, 403 NUMBER_SCOPE_NOT_ALLOWED, 403 PERMISSION_DENIED, 403 WORKSPACE_MEMBERSHIP_REQUIRED and 500 INTERNAL_ERROR.