Number provisioning & KYC
Complete lifecycle of provisioning a phone number: KYC documents, document sets, number orders, assignment, routing, and release.
This document describes the complete lifecycle of provisioning a phone number through the Audact platform, from Know-Your-Customer (KYC) document submission through placing orders with the carrier and managing numbers in the pool.
The Audact platform manages numbers on behalf of tenants using the Telnyx carrier. Numbers are ordered, stored, and assigned to agents through a self-serve KYC-on-behalf-of-tenant model: the tenant uploads regulatory compliance documents (e.g. business registration, proof of address), Audact encrypts them at rest and submits them to Telnyx for approval, and once approved, the tenant can order numbers in any country/type combination covered by that approval. Numbers are billed through Audact's platform Telnyx account; connection and billing setup are attached automatically by the platform and never supplied by the client.
The process unfolds in five logical steps:
- KYC Documents — Upload, encrypt, and track regulatory compliance documents
- Document Sets — Group documents into requirement sets matched against Telnyx regulatory requirements
- Number Orders — Place carrier orders against approved sets; receive DIDs into the pool
- Assignment & Routing — Assign numbers to agents and manage suspend/resume
- Release & Deletion — Return numbers to pool or permanently delete (and release at carrier)
Step 1: KYC Documents
POST /v1/kyc/documents — Upload a compliance document (e.g. business registration, proof of address).
The request must be multipart/form-data with:
document(file field, required) — the raw document bytes (PDF, image, etc.; max 15 MiB per document)doc_type(form field, required) — the Telnyx regulatory requirement type this document satisfies (e.g."business_registration","certificate_of_authority")filename(form field, optional) — human-readable filename; defaults to the upload filename if not provided
The response includes:
id(string) — unique document UUIDdoc_type(string) — the type as suppliedfilename(string) — the filenamecontent_type(string) — MIME type (e.g."application/pdf")size_bytes(integer) — uncompressed size of the original filesha256(string) — lowercase hex SHA-256 digest of the original bytes (immutable, for integrity checking)telnyx_document_id(string, nullable) — the Telnyx-assigned ID if this document has been sent to Telnyxcreated_at(ISO-8601 timestamp) — server-generated upload timeupdated_at(ISO-8601 timestamp) — last modification time
Important: Document bytes are NEVER returned by the API — only metadata. All documents are encrypted at rest under the tenant's KYC Document Encryption Key (DEK), with encryption keyed to the tenant and document ID so that ciphertexts cannot be replayed under a different identity.
GET /v1/kyc/documents — List all KYC documents for the tenant.
Returns a paginated list of document metadata (all fields from the POST response, minus the file bytes).
GET /v1/kyc/documents/{id} — Retrieve a single document's metadata.
PUT /v1/kyc/documents/{id} — Replace a document (e.g. re-upload a corrected or updated version).
The request body is again multipart/form-data with document and optionally filename and doc_type. Replacement will:
- Re-encrypt the new bytes under the current DEK version
- If this document backs an already-submitted or approved document set, re-verify the entire set with Telnyx (sending updated metadata to the requirement group)
- 409 Conflict if an in-flight number order currently references this document set
DELETE /v1/kyc/documents/{id} — Delete a document.
Returns 409 Conflict if a number order is currently referencing the document set that contains this document (the in-flight deletion guard prevents orphaning an order). Otherwise succeeds and cascades to any document sets that used this document.
Step 2: Document Sets & Requirements Coverage
GET /v1/kyc/requirements — Look up Telnyx's live regulatory requirements for a country and number type.
Query parameters:
country(required, 2-letter ISO 3166-1 alpha-2, e.g."US")number_type(required, e.g."local"or"mobile")
Response:
json
{
"requirements": [
{
"requirement_id": "string",
"doc_type": "string",
"description": "string"
}
]
}Coverage validation: There are three possible outcomes:
- Telnyx has requirements. The response lists each required
doc_type. Your document set must include at least one document matching each type. - Telnyx fetch failed. If the platform cannot reach Telnyx to look up requirements, the response is 503 Service Unavailable (fail-closed). Retry the endpoint once Telnyx is reachable.
- No requirements. Some country/type combinations require no documents (e.g. a reseller's existing DIDs). The response is 200 with an empty
requirementsarray; this is a legitimate outcome, not an error.
POST /v1/kyc/document-sets — Create a document set.
Request body:
json
{
"country": "US",
"number_type": "local",
"documents": [
{
"requirement_id": "reg-001",
"document_id": "uuid-of-uploaded-document"
},
{
"requirement_id": "reg-002",
"document_id": "uuid-of-another-document"
}
]
}The platform validates:
- Each
requirement_idis listed in the current Telnyx requirements for(country, number_type) - Each
document_idreferences an existing document belonging to this tenant - Every Telnyx requirement for the pair is satisfied by at least one document in the set (no gaps)
- The set is not empty
If validation succeeds, the set is submitted to Telnyx as a "requirement group" and responds with:
id(string) — set UUIDcountry(string) — country codenumber_type(string) — the typestatus(string) — always"submitted"on creationtelnyx_requirement_group_id(string) — Telnyx's ID for this groupdocuments(array) — the mapped documents with metadatacreated_at(ISO-8601 timestamp)updated_at(ISO-8601 timestamp)
Failure modes:
- 400 Bad Request if the document/requirement types are invalid or coverage is incomplete
- 422 Unprocessable Entity if Telnyx rejects the submission (e.g. document quality issues)
- 503 Service Unavailable if Telnyx is unreachable (fail-closed; retry later)
Async approval: After creation, Telnyx will verify the documents asynchronously. When verification completes (within hours or days), Telnyx delivers a signed webhook with event_type: "requirement_group.verified", and the platform atomically updates the set's status:
"approved"— Telnyx accepted all documents; the set is now usable for number orders"rejected"— Telnyx rejected one or more documents; the set cannot be used; inspect the rejection reason andPUTto re-submit documents
GET /v1/kyc/document-sets — List all document sets for the tenant.
Returns a paginated list including status ("submitted", "approved", or "rejected"), country, number_type, and the mapped documents for each.
GET /v1/kyc/document-sets/{id} — Retrieve a single set's details.
Includes the full list of requirement slots and which document is mapped to each slot.
DELETE /v1/kyc/document-sets/{id} — Delete a document set.
Returns 409 Conflict if a number order is currently referencing this set (the in-flight deletion guard). Otherwise succeeds and cascades to release the Telnyx requirement group.
Step 3: Number Orders
GET /v1/numbers/available — Query available numbers for ordering.
Query parameters:
country(required, e.g."US")number_type(required, e.g."local")limit(optional, default 10)area_code(optional, for US numbers; filters to a specific NPA)
The response returns a list of DIDs available for immediate provisioning:
json
{
"available_numbers": [
{
"number": "+1415...",
"region": "San Francisco, CA",
"features": ["sms", "voice", "mms"]
}
]
}Numbers are fetched live from Telnyx on each query and will not appear again once any order references them.
POST /v1/numbers/orders — Place a number order.
Request body:
json
{
"document_set_id": "uuid-of-approved-set",
"country": "US",
"number_type": "local",
"quantity": 5,
"area_code": "415"
}Parameters:
document_set_id— UUID of an approved (status ="approved") document setcountryandnumber_type— must match the set's country and type (validation error if mismatched)quantity— number of DIDs to order (typically 1–10)area_code— optional; US-specific filter
Idempotency: Pass the Idempotency-Key header (any string value); if a prior request with the same key succeeded, the identical response is replayed. This ensures orders placed over a flaky network do not result in duplicate orders.
On success, the response:
json
{
"order_id": "uuid",
"status": "pending",
"country": "US",
"number_type": "local",
"quantity": 5,
"requested_numbers": [],
"ordered_numbers": [],
"created_at": "2025-01-20T...",
"updated_at": "2025-01-20T..."
}The order starts in the "pending" status. The platform immediately submits the order to Telnyx's API and transitions the status through the following lifecycle, driven by Telnyx webhook delivery:
| Status | Meaning |
|---|---|
pending | Order accepted locally; awaiting Telnyx processing |
requirement_review | Telnyx is reviewing the document set coverage for this order's country/type (rare) |
ordering | Telnyx is acquiring the numbers from inventory (active) |
success | All requested numbers were allocated; numbers are now in the pool |
failure | Telnyx could not allocate all or some numbers (e.g. area code exhausted); no numbers were created |
Failure modes:
- 400 Bad Request if the country/number_type are invalid
- 404 Not Found if the document set does not exist
- 409 Conflict if the document set is not approved (status !=
"approved") - 422 Unprocessable Entity if the coverage validation fails (e.g. missing requirement documents)
- 503 Service Unavailable if Telnyx is unreachable (fail-closed; the order is not created)
Coverage re-validation: Before placing the order, the platform re-validates that the document set still covers all current Telnyx regulatory requirements for (country, number_type). This ensures that regulatory changes between set approval and order time are caught. If coverage is now incomplete (a new requirement was added by Telnyx after the set was approved), the order fails with 422.
Automatic connection and billing: Once the order reaches "success" status and the numbers are inserted into the pool, the Audact platform automatically:
- Configures the trunk connection (Telnyx SIP trunk) for this tenant
- Attaches billing to the tenant's Audact-managed Telnyx account
- Sets routing defaults (initially, all DIDs are inactive until explicitly assigned to an agent)
The client does not supply connection parameters or billing details; the platform handles this transparently.
GET /v1/numbers/orders — List all orders for the tenant.
Returns a paginated list with status, country, type, quantity, and timestamps.
GET /v1/numbers/orders/{id} — Retrieve a single order's status.
Includes the full lifecycle status and current ordered_numbers list (populated once status reaches "success").
Step 4: Assignment & Routing
Once a number reaches the pool (status = "success" in its order), it is not yet routing calls. Assignment makes a number active for a specific agent.
GET /v1/numbers — List all numbers in the pool for the tenant.
Query parameters:
status(optional, filter by state:"available","assigned","suspended")
Response includes:
id— number UUIDnumber— the E.164-formatted DID (e.g."+1415...")country,number_type— originstatus— one of"available","assigned", or"suspended"assigned_agent_id(nullable) — the agent UUID if assignedcreated_at,updated_at
POST /v1/numbers/{id}/assign — Assign a number to an agent.
Request body:
json
{
"agent_id": "uuid-of-agent"
}Response 200 if successful, 404 if the number or agent does not exist, 409 if the number is already assigned to a different agent.
Once assigned, calls inbound to the number will be routed to that agent.
DELETE /v1/numbers/{id}/assign — Unassign a number from an agent.
The number transitions back to "available" and calls are no longer routed (still in the pool, but inactive).
POST /v1/numbers/{id}/suspend — Suspend a number's routing.
The number transitions to "suspended" status; calls to it are rejected (no longer routed to its agent, if assigned). Routing can be resumed without losing the assignment.
POST /v1/numbers/{id}/resume — Resume a suspended number.
The number transitions back to its prior status ("assigned" if assigned, or "available" if unassigned) and routing is restored.
Step 5: Release & Deletion
Two distinct removal operations exist:
DELETE /v1/numbers/{id}/release — Return the number to the pool (soft delete).
- Clears the agent assignment (if any)
- Transitions to
"available"status - Does NOT permanently delete the number or release the DID at Telnyx
- The number remains usable for future assignment
This operation is used when an agent no longer needs the number but you want to keep it in your pool for reassignment or for planning.
DELETE /v1/numbers/{id} — Permanently delete the number (hard delete).
- Removes the number from the pool entirely
- Releases the DID back to Telnyx's inventory (irreversible)
- Stops billing for this number immediately
- The DID will never be callable again
This operation is irreversible; use release if you want to keep the number callable for future reassignment. Once deleted, the number is gone from the platform and the carrier's pool.
Error Semantics & Idempotency
Fail-Closed & Fail-Open
All KYC and ordering operations are fail-closed: if the platform cannot reach Telnyx (network error, timeout, Telnyx outage), the request fails with 503 Service Unavailable. No partial state is created; a retry once Telnyx is reachable will not result in duplicates (thanks to Idempotency-Key support on state-changing endpoints).
Idempotency-Key
Pass the Idempotency-Key header on:
POST /v1/kyc/documents— upload a documentPUT /v1/kyc/documents/{id}— replace a documentPOST /v1/numbers/orders— place an order
Any two requests with the same key within 24 hours (or your configured idempotency window) will result in the same response being returned. This allows safe replay of requests across network failures without creating duplicates.
In-Use Conflict (409)
Document sets and documents carry guards against being deleted while referenced by an in-flight order:
DELETE /v1/kyc/documents/{id}returns 409 if any of its sets have an active orderDELETE /v1/kyc/document-sets/{id}returns 409 if an active order references the set
An "active" order is one with status in {pending, requirement_review, ordering}; once an order reaches terminal status (success or failure), the set is no longer "in-flight" and can be safely deleted.
Coverage Scope (422)
POST /v1/kyc/document-setsreturns 422 if coverage is incomplete (a Telnyx requirement has no matching document)POST /v1/numbers/ordersreturns 422 if coverage has changed since the set was approved (Telnyx added a new requirement)
Both are modeled as unprocessable entities to distinguish them from 400 (bad request parameters) and 503 (backend unavailable).
Unknown Type (400)
POST /v1/kyc/documentswith an invaliddoc_typereturns 400 Bad RequestPOST /v1/kyc/document-setswith an invalid requirement ID or country/type combo returns 400
These are malformed requests, not coverage issues.
Telnyx Unreachable (503)
Whenever Telnyx is unreachable:
POST /v1/kyc/documentsupload (before encryption) returns 503 if the subsequent Telnyx metadata fetch failsPUT /v1/kyc/documents/{id}(during re-verification) returns 503 if Telnyx is unreachableGET /v1/kyc/requirementsreturns 503 if Telnyx cannot be queriedPOST /v1/kyc/document-setsreturns 503 if Telnyx submission failsPOST /v1/numbers/ordersreturns 503 if Telnyx order submission fails
In all cases, the platform does not enter a partial state; retry once Telnyx is reachable.
