Phone, SMS & voice

KYC for regulated countries

Submit the identity details a regulated country requires, hand the form to your customer with a branded link, and fix a declined number.

Many countries regulate who can hold a phone number. Before a number in those countries is ordered, the registrant (you, or your customer) proves who they are and where they are. Pozy collects the details, files them with the carrier and tracks the review. Numbers usually activate 1 to 3 business days after you submit.

You hit this flow when GET /v1/phone-numbers/countries shows needsKyc: true, or when a purchase answers 202 with status: "kyc_required".

#Two ways to collect the details

Hosted link API
Who fills it in Your customer, on a page with your logo and colour Your own UI
Build effort One call A form per country, document upload, address checks
Best for Platforms whose customers hold the legal details Teams that already collect business documents

#Hand the form to your customer

Not available yet

Hosted KYC links are not available yet: POST /v1/phone-numbers/kyc/share answers 403 with code: "feature_not_available". Collect the details in your own UI and submit them through the API until it opens.

POST /v1/phone-numbers/kyc/share returns a single-use page, valid for 7 days, that walks your customer through the country's requirements. It needs no Pozy login.

curl -X POST https://api.pozy.ai/v1/phone-numbers/kyc/share \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "6650c1f0a2b3c4d5e6f70812",
    "country": "GB",
    "language": "en",
    "branding": { "companyName": "Acme", "logoUrl": "https://acme.example.com/logo.png", "brandColor": "#1a73e8" },
    "redirect_url": "https://acme.example.com/numbers/done"
  }'
  • branding puts your company name, logo and colour on the page.
  • language sets the page language: en, es or pt-BR. Without it, the page follows the browser and falls back to English.
  • redirect_url sends the customer back to your app afterwards, with kyc=submitted and country=<code> added to the query string.
  • areaCode holds the order to one area.

When your customer submits, the number enters review under your workspace and whatsapp.number.kyc_submitted fires.

#Submit through the API

#Check stock and the address rule

GET /v1/phone-numbers/availability?country=GB says whether numbers are in stock and which address the registrant needs (addressConstraint: geo, country or none). Checking first avoids a filing that passes review but can never get a number because the address is in the wrong area.

#Get the form for the country

GET /v1/phone-numbers/kyc?country=GB returns the fields the country requires. Add numberType if you want a type other than the default, and send the same value when you submit.

Response
{
  "country": "GB",
  "numberType": "local",
  "fields": [
    { "requirementId": "d7e8f9a0-b1c2-4d3e-9f4a-5b6c7d8e9f0a", "label": "Business name", "kind": "text", "example": "Acme Ltd" },
    { "requirementId": "e8f9a0b1-c2d3-4e4f-8a5b-6c7d8e9f0a1b", "label": "Proof of identity", "kind": "file" },
    { "requirementId": "f9a0b1c2-d3e4-4f5a-9b6c-7d8e9f0a1b2c", "label": "Business address", "kind": "address", "localTo": "GB" }
  ],
  "reusable": null,
  "pendingReview": false
}
kind What to send
text A string in values, keyed by requirementId
date An ISO date in values
address The address object
file An entry in documents, either inline base64 or a documentId from POST /v1/phone-numbers/kyc/upload-document (about 4 MB per document). Base64 makes a file about a third larger and the whole request can carry about 4 MB, so keep inline files to about 3 MB in total per submission
action An identity check the end user completes after the order, through a link. Send endUserFirstName and endUserLastName with the submit.

#Check the packet (optional)

POST /v1/phone-numbers/kyc/validate-address checks an address before you upload anything. POST /v1/phone-numbers/kyc/review-packet reviews the whole packet for likely decline reasons.

#Submit

Submit KYC
curl -X POST https://api.pozy.ai/v1/phone-numbers/kyc \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "6650c1f0a2b3c4d5e6f70812",
    "country": "GB",
    "submissionId": "acme-gb-0001",
    "values": { "d7e8f9a0-b1c2-4d3e-9f4a-5b6c7d8e9f0a": "Acme Ltd" },
    "documents": [
      { "requirementId": "e8f9a0b1-c2d3-4e4f-8a5b-6c7d8e9f0a1b", "filename": "passport-both-sides.pdf", "base64": "JVBERi0xLjQK..." }
    ],
    "address": {
      "requirementId": "f9a0b1c2-d3e4-4f5a-9b6c-7d8e9f0a1b2c",
      "country_code": "GB",
      "street_address": "221 Baker Street",
      "locality": "London",
      "administrative_area": "London",
      "postal_code": "NW1 6XE"
    }
  }'

The response has status: "kyc_submitted" and the new number in pending_regulatory.

ID cards need both sides

Carriers reject one-sided ID cards often. Combine front and back into one file before you upload it.

Submit field What it does
submissionId Your key for this attempt. A retry with the same value returns the same number instead of ordering another.
quantity 1 to 5 numbers in the same country from one verification (a pre-order is always 1)
wantsSms Order from SMS-capable stock and turn SMS on at activation
reuse, reuseOptionId Reuse an approved verification you already hold for this country. The form response lists it in reusable. No documents needed, and the order is placed at once.
areaCode, preOrder Order in one area, and accept a sourced number when it is out of stock

Document bytes go straight to the carrier with the request. Pozy does not keep a copy.

#Out of stock: pre-order

When available is false but preOrderable is true, submit the same form with preOrder: true. Pozy takes the next number that comes back in stock, or one the carrier sources for this registration.

  • Sourcing usually takes 2 to 4 weeks and is not guaranteed.
  • The number stays pending_regulatory until sourced. Nothing is billed until it is active.
  • If the carrier cannot source it, the number is declined and nothing is charged.
  • To cancel, release the pending number with DELETE /v1/phone-numbers/{id}.

In geo countries the area comes from the address. If that area has no stock, the first submit returns 409 with code: "area_pre_order_available" and details (areaCode, areaName, estimatedWeeks, billedWhenActive). The error text is ready to show your customer. Once they agree to wait, resend the same body with preOrder: true.

Some countries (Australian mobiles, for example) need the end user to pass a live identity check after the order is placed. When the form had an action field:

  1. Poll GET /v1/phone-numbers/{id} after submitting. The check link appears on the number in onfidoVerificationUrl once the order exists.
  2. Or listen for whatsapp.number.verification_required, which carries the same link in verificationUrl.
  3. Send the link to the person whose ID is on file. The review waits until they finish.

#Fix a declined number

A declined number stays in your list with status: "regulatory_declined" and a regulatoryDeclineReason. You do not start over.

  1. GET /v1/phone-numbers/{id}/remediate returns only the requirements the reviewer flagged.
  2. POST /v1/phone-numbers/{id}/remediate with corrected values, documents or address.

The number goes back to pending_regulatory on the same record and is not billed again.

#Answer the reviewer

Sometimes the reviewer asks a question rather than flagging a field ("is this a personal or a business line?"). POST /v1/phone-numbers/{id}/remediate/respond sends a message, plus any corrected documents or address and up to 5 attachments (PDF, JPG, PNG or WebP, sent as base64, about 3 MB in total per request). The response status is replied for a message alone and resubmitted when corrections went with it.

Reply to the reviewer
curl -X POST https://api.pozy.ai/v1/phone-numbers/6650d2e1b3c4d5e6f7081923/remediate/respond \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "This is a business line for Acme Ltd. The utility bill is in the company name." }'

#Statuses and events

What happened Number status Event
Customer submitted a hosted link pending_regulatory whatsapp.number.kyc_submitted
Reviewer needs more information stays pending_regulatory whatsapp.number.action_required (with reason and per-requirement requirements)
End user must pass an ID check stays pending_regulatory whatsapp.number.verification_required
Approved and live active whatsapp.number.activated
Declined regulatory_declined whatsapp.number.declined

These events carry a whatsapp. prefix for historical reasons and fire for every number. No event fires when you submit through the API, because you already know.

Note

verification.approved and verification.failed are not KYC events. They belong to the one-time code API.

#Limits

Limit Value
Review time Usually 1 to 3 business days
Numbers per verification 1 to 5, same country
Hosted link Single use, valid 7 days
Reviewer attachments Up to 5 files, about 3 MB in total per request (they travel as base64)

#Errors

Status Code Cause Fix
400 A value failed validation, often an address outside the country or an oversized file Run validate-address first
409 area_pre_order_available The address area is out of stock but can be pre-ordered Resend with preOrder: true once the customer agrees
409 area_code_unavailable The area has no stock and cannot be pre-ordered Pick another area, or create a stock watch
409 country_out_of_stock Nothing deliverable in the country and type Create a stock watch
409 reuse: true with no approved verification on file Submit the full form

#Endpoints

Endpoint What it does
GET /v1/phone-numbers/kyc Get KYC form spec
POST /v1/phone-numbers/kyc Submit KYC
POST /v1/phone-numbers/kyc/upload-document Upload a KYC document
GET /v1/phone-numbers/kyc/document/{documentId} View a KYC document on file
POST /v1/phone-numbers/kyc/validate-address Pre-validate KYC address
POST /v1/phone-numbers/kyc/review-packet Pre-review a KYC packet
POST /v1/phone-numbers/kyc/share Create a hosted KYC link (not available yet)
GET /v1/phone-numbers/{id}/remediate Get declined requirements
POST /v1/phone-numbers/{id}/remediate Resubmit a declined number
POST /v1/phone-numbers/{id}/remediate/reply Reply to the regulatory reviewer
POST /v1/phone-numbers/{id}/remediate/respond Respond to the regulatory reviewer (message + corrections)

#Webhook events

Event When it fires
whatsapp.number.activated WhatsApp number activated.
whatsapp.number.declined WhatsApp number declined.
whatsapp.number.action_required WhatsApp number action required.
whatsapp.number.verification_required WhatsApp number verification-required.
whatsapp.number.kyc_submitted WhatsApp number KYC submitted.