Skip to main content
Audact
See the offer

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:

  1. KYC Documents — Upload, encrypt, and track regulatory compliance documents
  2. Document Sets — Group documents into requirement sets matched against Telnyx regulatory requirements
  3. Number Orders — Place carrier orders against approved sets; receive DIDs into the pool
  4. Assignment & Routing — Assign numbers to agents and manage suspend/resume
  5. 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 UUID
  • doc_type (string) — the type as supplied
  • filename (string) — the filename
  • content_type (string) — MIME type (e.g. "application/pdf")
  • size_bytes (integer) — uncompressed size of the original file
  • sha256 (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 Telnyx
  • created_at (ISO-8601 timestamp) — server-generated upload time
  • updated_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:

  1. Telnyx has requirements. The response lists each required doc_type. Your document set must include at least one document matching each type.
  2. 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.
  3. No requirements. Some country/type combinations require no documents (e.g. a reseller's existing DIDs). The response is 200 with an empty requirements array; 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:

  1. Each requirement_id is listed in the current Telnyx requirements for (country, number_type)
  2. Each document_id references an existing document belonging to this tenant
  3. Every Telnyx requirement for the pair is satisfied by at least one document in the set (no gaps)
  4. The set is not empty

If validation succeeds, the set is submitted to Telnyx as a "requirement group" and responds with:

  • id (string) — set UUID
  • country (string) — country code
  • number_type (string) — the type
  • status (string) — always "submitted" on creation
  • telnyx_requirement_group_id (string) — Telnyx's ID for this group
  • documents (array) — the mapped documents with metadata
  • created_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 and PUT to 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 set
  • country and number_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:

StatusMeaning
pendingOrder accepted locally; awaiting Telnyx processing
requirement_reviewTelnyx is reviewing the document set coverage for this order's country/type (rare)
orderingTelnyx is acquiring the numbers from inventory (active)
successAll requested numbers were allocated; numbers are now in the pool
failureTelnyx 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 UUID
  • number — the E.164-formatted DID (e.g. "+1415...")
  • country, number_type — origin
  • status — one of "available", "assigned", or "suspended"
  • assigned_agent_id (nullable) — the agent UUID if assigned
  • created_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 document
  • PUT /v1/kyc/documents/{id} — replace a document
  • POST /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 order
  • DELETE /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-sets returns 422 if coverage is incomplete (a Telnyx requirement has no matching document)
  • POST /v1/numbers/orders returns 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/documents with an invalid doc_type returns 400 Bad Request
  • POST /v1/kyc/document-sets with 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/documents upload (before encryption) returns 503 if the subsequent Telnyx metadata fetch fails
  • PUT /v1/kyc/documents/{id} (during re-verification) returns 503 if Telnyx is unreachable
  • GET /v1/kyc/requirements returns 503 if Telnyx cannot be queried
  • POST /v1/kyc/document-sets returns 503 if Telnyx submission fails
  • POST /v1/numbers/orders returns 503 if Telnyx order submission fails

In all cases, the platform does not enter a partial state; retry once Telnyx is reachable.

Source updated: October 19, 2026