60db Logo
  • Guides

Capture phone numbers without losing country codes or leading zeros

Capture phone numbers as text. Preserve country context, leading zeroes, extensions, and current confirmation with a tested reference.

Editorial date
Reading time
26 min
A plus sign and grouped digit tiles retain the leading zero and a separate extension

TL;DR: Capture a telephone number as literal text with explicit dialing context, separate extension, and versioned confirmation. Preserve leading zeroes until a numbering-plan adapter has enough authority to interpret them; format acceptance, reachability, caller identity, and permission to act remain separate evidence.


A caller gives a grouped phone number. The recognizer returns all the expected digits, but a later integer conversion removes its leading zeroes. Another service adds a country prefix from the caller's language, and the confirmation voice reads a plausible destination that the caller never supplied.

Keep the literal capture before normalization. The workflow enforcement guide explains why an approved action needs current values; a phone destination needs the same discipline before it reaches a dialing or lookup interface.

This guide provides an executable capture and revision contract. It deliberately stops before worldwide numbering-plan validation. Its synthetic fixtures contain incomplete fragments, never dial a number, and establish no carrier, ownership, or speech-recognition result.

The public libphonenumber FAQ explains why national prefixes, significant zeroes, and international dialing prefixes need context. A parser that extracts a plausible telephone number is useful, but its interpretation must not erase what the caller actually said.

Table of contents

  1. Keep the telephone identifier as text
  2. Separate country context from caller identity
  3. Preserve zeroes and prefix evidence
  4. Design a repeat-back the caller can repair
  5. Run the bounded capture reference
  6. Pass a confirmed snapshot to the numbering adapter
  7. Keep validation claims separate
  8. Trace a corrected destination safely
  9. Evaluate the whole capture path
  10. Replay a lost-zero incident across interfaces
  11. Specify the adapter acceptance report
  12. Frequently asked questions
  13. Start with one explicit destination contract

Keep the telephone identifier as text

A telephone number identifies a destination under a numbering and dialing context. It is not a quantity on which the application should perform arithmetic. Converting its digits into an integer discards distinctions that can matter before a plan-aware adapter has interpreted the input.

Retain the original groups, a joined digit string, any literal plus sign, the explicitly supplied context, and the extension separately. The joined string is useful for a later adapter, while the groups help the caller repair a particular section. Neither should silently overwrite the other.

A display form and a spoken form can differ from the canonical destination. A screen may add spaces, and a voice may pronounce each character separately. Those presentation choices do not authorize adding a country prefix, removing a significant zero, or merging an extension into the main number.

The DTMF and voice identifier guide addresses input ownership when speech and keypad events compete. This guide assumes that boundary already produced one literal candidate. A numbering-plan contract should not attempt to solve modality races by choosing whichever resulting string looks more plausible.

JSON has distinct string and number types. RFC 8259 also defines a number grammar that excludes arbitrary leading-zero spellings. If an upstream service sends a numeric value after removing zeroes, a downstream formatter cannot reliably recover the original literal.

Treat that loss as a clarification or unsupported-input event. Padding the remaining digits to a familiar length invents evidence. The postal-code preservation guide describes the same information-loss problem in another structured identifier, where numeric-looking content still requires textual storage.

Keep type assertions at every serialization boundary. A database text column does not help if the application casts the value before insertion, and a string-valued API does not help if an export tool infers a numeric column. Test the exact literal after each supported handoff rather than comparing only its numeric value.

The local example accepts ASCII digit groups as a deliberate teaching subset. A broader input path can support additional decimal digit characters through an explicit mapping policy, but it should preserve the original representation and record the mapping. A general Unicode normalization call is not a worldwide telephone parser.

Separate country context from caller identity

Country calling codes and ISO country or region identifiers serve different purposes. A numeric calling code can serve more than one region, and a caller's current location can differ from the numbering context of their phone. Keep the exact kind of context named in the application data model.

The public libphonenumber falsehoods document describes several unsafe assumptions about telephone numbers. Use it to challenge shortcuts such as inferring preferred language or physical location from a number. Those shortcuts can also cause an application to disclose information to the wrong person.

Ask a bounded question when context is missing. For a national-form destination, the application may need the relevant region or an explicitly global representation. A caller saying that the destination belongs to a certain country supplies useful context, but it still does not prove that they own that destination.

The shared-phone identity guide explains why a familiar number is insufficient evidence about the person currently speaking. A shared handset, reassignment, forwarding, or another respondent can break the assumption. Destination capture and authentication therefore need separate workflow gates.

Do not select a numbering plan from recognition language. A bilingual caller can speak one language while giving a number from another region. The Hindi-English language-policy guide concerns recognizer configuration; that metadata does not grant authority to add a telephone prefix.

Store context provenance beside its value. A caller-supplied region, an application policy for a particular form, and a result from a trusted numbering adapter are different sources. If the application uses a configured default, expose that choice in confirmation rather than presenting it as though the caller supplied it.

A previously remembered context can also expire or become irrelevant. The repeat-call memory guide separates durable memory from current consent and identity. A stored region should be reused only under a documented policy and current destination confirmation.

The reference's context field is intentionally opaque. It retains a label plus provenance and leaves validation of that label to the next adapter. Its synthetic region label is not an ISO assignment. The artifact demonstrates preservation and confirmation boundaries without pretending to ship a complete numbering metadata database.

Literal digit groups, explicit country context, and extension remain distinct before the numbering adapter

Preserve zeroes and prefix evidence

A leading zero can be a national dialing prefix, a significant part of the destination, or part of an incomplete or special input. Do not remove it with a generic trim operation. The correct interpretation depends on the supported numbering context and the adapter's actual contract.

The libphonenumber FAQ documents why some zeroes are retained, including significant zeroes needed when dialing some destinations from abroad. That behavior is sufficient reason to avoid a blanket zero-deletion rule. An application should not recreate country-specific numbering logic from a few familiar examples.

A literal plus sign and the digits zero zero are different capture evidence. The FAQ explicitly explains that 00 is not the international dialing prefix in every originating region. Rewriting it universally to plus can change the interpretation of a valid literal before the application knows the relevant dialing context.

RFC 3966 distinguishes global telephone identifiers from local ones and defines context for local numbers. It also represents an extension as a separate parameter. The capture model follows that separation without claiming that its simple fields constitute a complete tel URI parser.

Keep the plus sign as a boolean only because this teaching contract accepts at most one explicitly supplied leading plus. It is not a license to extract every plus-like symbol from arbitrary text. The speech or form adapter must identify that supported token and report ambiguity instead of guessing.

The main digit string and extension should remain separate through repeat-back and lookup. Concatenating them can create a different destination, and feeding extension digits into a numbering-plan validator can produce a misleading format result. If the downstream API has a specific extension representation, adapt it explicitly.

A zero is retained until the numbering context supports its interpretation

Raw input preservation is useful even when a mature parser is available. The libphonenumber FAQ explains that parsing can extract telephone content and handle vanity letters or apparent mistakes. That is useful behavior under a documented product policy, but it differs from a strict promise to preserve the caller's literal identifier unchanged.

Compare parser output with the retained capture and identify material changes before confirmation. If a parser converts letters or removes a prefix, record why that transformation is permitted. A silent successful parse should not erase an unresolved character or context question.

Design a repeat-back the caller can repair

Repeat the destination in the same meaningful groups the caller supplied, with the country context and extension made explicit. The purpose is to establish which current candidate they are confirming. A fluent reading that hides a missing prefix can make a wrong value sound more trustworthy than it is.

The confirmation prosody guide describes how grouping and emphasis help distinguish values. Those are presentation tools. They should operate on a preserved candidate rather than replacing it with a prettier but semantically different number.

Let the caller repair one group. A correction should create a new revision, invalidate the previous approval, and trigger a current repeat-back. The spoken self-correction guide explains why the intended final value is different from merely accumulating all recognized fragments.

A partial transcript is not a confirmed destination. The partial-transcript revision guide shows why later recognition can revise an earlier segment. Keep the capture controller's revision distinct from the recognizer's own segment identifiers so an adapter can explain how one becomes the other.

An answer such as yes followed by a correction should not preserve approval for the previous number. The confirmation repair guide addresses that exact interaction. The material value needs another current confirmation before a downstream action can rely on it.

A correction advances the candidate revision and invalidates the old approval

The synchronous reference below approves a current revision inside one local owner. It does not implement transport-level confirmation request delivery. If a real integration retries asynchronous confirmation work, a new request after repair needs a distinct request identity, while an exact retry retains its original immutable payload and timestamp.

Do not renew an expired capture merely because a new correction arrives. Check the existing deadline before replacement admission. If the operation expired, allocate a new attempt under an explicit recovery policy instead of granting the old identity a fresh lifetime and calling the result a harmless revision.

Run the bounded capture reference

The complete Python example uses only the standard library and runs on Python 3.11 or newer. It preserves supported groups, context, and extension in owned immutable values. It checks deadlines during proposals, confirmation, and readiness queries, so missing callbacks do not leave a stale approved capture operational indefinitely.

Its validation boundary is deliberately narrow. It checks supported text shape and revision behavior, not worldwide phone-number lengths or current assignments. The Python re documentation describes explicit ASCII matching options; this example uses direct ASCII character checks to make the supported subset visible.

from dataclasses import dataclass, replace, FrozenInstanceError
import json


def digits(value):
    return type(value) is str and bool(value) and all('0' <= c <= '9' for c in value)


@dataclass(frozen=True)
class PhoneInput:
    groups: tuple
    literal_plus: bool
    country_context: tuple | None
    extension: str

    @property
    def digit_string(self):
        return ''.join(self.groups)


def capture(groups, literal_plus=False, country_context=None, extension=''):
    if (type(groups) not in (list, tuple) or not 1 <= len(groups) <= 12
            or any(not digits(g) or len(g) > 20 for g in groups)
            or type(literal_plus) is not bool):
        raise ValueError('unsupported literal capture')
    if type(extension) is not str or (extension and not digits(extension)) or len(extension) > 12:
        raise ValueError('unsupported extension')
    if country_context is not None:
        if (type(country_context) not in (list, tuple) or len(country_context) != 2
                or any(type(x) is not str or not x or len(x) > 64 for x in country_context)
                or country_context[1] not in ('caller-supplied', 'application-policy')):
            raise ValueError('explicit context and provenance required')
        country_context = tuple(country_context)
    return PhoneInput(tuple(groups), literal_plus, country_context, extension)


@dataclass(frozen=True)
class Attempt:
    deadline: int
    revision: int = -1
    candidate: PhoneInput | None = None
    approved_revision: int | None = None
    status: str = 'active'

    def __post_init__(self):
        if type(self.deadline) is not int or self.deadline < 0:
            raise ValueError('nonnegative local integer deadline required')


def advance(state, now):
    if type(now) is not int or now < 0:
        raise ValueError('nonnegative local integer clock required')
    if state.status == 'active' and now >= state.deadline:
        return replace(state, status='expired', approved_revision=None)
    return state


def propose(state, revision, candidate, now):
    state = advance(state, now)
    if state.status != 'active':
        return state, state.status
    if type(revision) is not int or revision < 0 or type(candidate) is not PhoneInput:
        raise ValueError('validated capture and revision required')
    candidate = capture(candidate.groups, candidate.literal_plus,
                        candidate.country_context, candidate.extension)
    if revision < state.revision:
        return state, 'stale'
    if revision == state.revision:
        return state, 'duplicate' if candidate == state.candidate else 'conflict'
    return replace(state, revision=revision, candidate=candidate,
                   approved_revision=None), 'awaiting-repeat-back'


def confirm(state, revision, now):
    state = advance(state, now)
    if state.status != 'active':
        return state, state.status
    if type(revision) is not int or revision != state.revision or state.candidate is None:
        return state, 'stale-confirmation'
    return replace(state, approved_revision=revision), 'confirmed-capture'


def readiness(state, now):
    state = advance(state, now)
    if state.status != 'active':
        return state, state.status
    if state.candidate is None or state.approved_revision != state.revision:
        return state, 'awaiting-repeat-back'
    c = state.candidate
    if not c.literal_plus and c.country_context is None:
        return state, 'clarify-country-context'
    return state, 'ready-for-numbering-plan-adapter'


def fixtures():
    # Synthetic, deliberately incomplete capture fragments. Never dialed.
    groups = ['0012', '034']
    context = ['synthetic-region', 'caller-supplied']
    snapshot = capture(groups, country_context=context, extension='00')
    groups[0] = '9'; context[0] = 'changed'
    assert snapshot.groups == ('0012', '034')
    assert snapshot.digit_string == '0012034' and snapshot.extension == '00'
    assert snapshot.country_context[0] == 'synthetic-region'
    try:
        snapshot.groups = ('9',)
    except FrozenInstanceError:
        pass
    else:
        raise AssertionError('mutable snapshot')
    try:
        snapshot.groups[0] = '9'
    except TypeError:
        pass
    else:
        raise AssertionError('mutable item')
    state, _ = propose(Attempt(100), 0, snapshot, 1)
    state, _ = confirm(state, 0, 2)
    assert readiness(state, 3)[1] == 'ready-for-numbering-plan-adapter'
    repaired = capture(['0012', '035'], country_context=('synthetic-region', 'caller-supplied'))
    assert propose(state, 0, repaired, 3)[1] == 'conflict'
    changed, _ = propose(state, 1, repaired, 3)
    assert changed.approved_revision is None
    assert confirm(changed, 0, 4)[1] == 'stale-confirmation'
    assert propose(changed, 0, snapshot, 4)[1] == 'stale'
    assert propose(changed, 1, repaired, 4)[1] == 'duplicate'
    for plus in (False, True):
        no_country = capture(['00', '012'], literal_plus=plus)
        check, _ = propose(Attempt(100), 0, no_country, 1)
        check, _ = confirm(check, 0, 2)
        expected = 'ready-for-numbering-plan-adapter' if plus else 'clarify-country-context'
        assert readiness(check, 3)[1] == expected
        assert check.candidate.digit_string == '00012'
    expired, disposition = readiness(state, 100)
    assert disposition == 'expired' and expired.approved_revision is None
    assert propose(state, 2, repaired, 100)[1] == 'expired'
    assert confirm(expired, 0, 2)[1] == 'expired'
    assert readiness(replace(state, status='canceled'), 3)[1] == 'canceled'
    for bad in ([123], ['12'], [''], '0012'):
        try:
            capture(bad)
        except ValueError:
            pass
        else:
            raise AssertionError('invalid literal accepted')
    for bad_deadline in (-1, True, '100'):
        try:
            Attempt(bad_deadline)
        except ValueError:
            pass
        else:
            raise AssertionError('invalid deadline accepted')
    print(json.dumps({'fixture_class':'Synthetic', 'preserved_groups':snapshot.groups,
                      'digit_string':snapshot.digit_string, 'extension':snapshot.extension,
                      'result':'capture checks passed; numbering plan and ownership untested'}))

if __name__ == '__main__':
    fixtures()

The program emits a JSON report containing the synthetic preserved groups, joined string, extension, and the explicitly untested boundaries. It also runs its negative fixtures. A success message alone would not fulfill a promise to provide reusable capture evidence, so inspect the actual emitted fields when adapting the program.

The capture function copies lists into tuples. The proposal path accepts only a validated PhoneInput and keeps its immutable value shape. Strings and tuples prevent a caller from rewriting an approved candidate by mutating the original list or rebinding a returned property.

The fixtures distinguish strictly older revisions, exact current duplicates, and changed content under a reused current revision. They also demonstrate that a material new revision clears approval, an old confirmation is stale, and a readiness query records expiry. These are local controller guarantees rather than evidence that an external action was canceled.

The Python Unicode database documentation is useful when expanding the digit policy. Decimal, digit, and numeric character properties are different concepts. Record the supported runtime and mapping policy before accepting characters that an ASCII-only downstream service cannot interpret directly.

Pass a confirmed snapshot to the numbering adapter

Readiness means the local capture is approved and has enough explicit context for the next adapter. It does not mean the number is valid, reachable, or owned. The output name ready-for-numbering-plan-adapter is intentionally specific so a caller cannot mistake the local result for business authorization.

Pass the original capture and the adapter configuration together. The adapter should know whether the plus sign was literal, which region context was supplied, and whether an extension exists. A bare joined digit string loses information that can affect parsing and confirmation.

Keep adapter output as a separate versioned value. A canonical global form, a formatted display form, and a plan-validation result can coexist with the original input. If the canonical result changes a material part of the intended destination, confirm that result according to the application policy rather than replacing the source record silently.

The order-ID and email capture guide develops the corresponding rule for characters and punctuation. A normalization that is harmless for one field can be destructive for another. Reusing one generic cleanup function across phone numbers, email addresses, and account IDs creates hidden assumptions.

Check what the library's possible and valid methods actually establish. A possible number can pass a structural possibility check without belonging to a documented valid range, and a valid range does not establish assignment to the intended person. The exact implementation version and metadata influence those results.

Record the metadata version when plan validation is material to an experiment or deployment decision. Numbering plans change, and public metadata can lag an official change. An old validation result should not be presented as timeless authority about a destination's current assignment or reachability.

The cache-key guide explains why configuration and policy belong in cache identity. A cached normalization under one region assumption must not be reused after the caller supplies different context. Retain the literal input and the interpretation version together.

If the adapter is unavailable, expose that outcome. Do not replace unavailable validation with a permissive regex and report the destination as validated. The capture can remain preserved while the workflow requests clarification, retries through an authorized route, or stops according to its actual requirements.

Keep validation claims separate

The criteria table uses ✓ to mean that the method can establish the named property within its declared boundary. A ✗ means it cannot establish that property by itself. These are evidence distinctions, not accuracy scores or rankings of telephone providers.

Method Preserve supplied zeroes Interpret numbering context Establish current reachability Establish caller identity
Literal capture reference ✓ ✗ ✗ ✗
Versioned plan-aware parser ✓ only with retained source ✓ within supported metadata ✗ ✗
Authorized reachability check ✗ by itself ✗ by itself ✓ at observed boundary ✗ by itself
Separate identity workflow ✗ by itself ✗ by itself ✗ by itself ✓ only under its own evidence policy

Literal capture, plan validation, reachability, and authorization require separate evidence

A telephone connection does not prove that the intended person answered. The ringback and early-media guide separates signaling acceptance, media, classification, and task outcome. A destination can be reachable while the required identity or consent remains unverified.

A spoken readback also does not establish that the caller heard or understood it. The playback acknowledgment guide explains delivery boundaries. Local writer acceptance can support a technical trace without proving acoustic comprehension or approval.

If the application uses speech synthesis markup to read characters, use the documented engine subset. W3C SSML 1.1 defines interpretation mechanisms, but a tag does not establish that a particular voice pronounces every digit or extension correctly. Test the selected output path with appropriate listening evidence.

Keep a business action separate from capture confirmation. A caller approving a callback destination has not necessarily authorized a purchase, account change, or disclosure. The workflow must bind its approval to the complete current proposal, including the intended action and any other material fields.

Trace a corrected destination safely

A useful trace records the attempt identity, candidate revision, context provenance, confirmation disposition, and adapter version. It does not need to include a complete actual destination in every routine log. Synthetic fragments can reproduce most type and ownership defects without retaining a customer's phone number.

The observability guide explains how to connect those events without turning diagnostics into a content archive. Keep raw sensitive content under its own justified access and retention rules. A trace identifier is not a reason to copy every value into a shared incident report.

Distinguish parser rejection from caller uncertainty. A missing region, an unresolved character, an unsupported digit script, and an adapter outage require different recovery actions. Returning a single empty string for all four makes the next step guess what happened.

The progressive no-input guide is relevant when the caller pauses while finding a number. A silence timeout and an invalid destination are different observations. Preserve the current attempt's deadline and offer a supported recovery path without inventing digits or extending approval indefinitely.

If the caller cancels or the session closes, keep that terminal disposition. The shutdown semantics guide describes why a late successful callback must not reopen operational authority. A retained historical candidate can remain available for audit while being ineligible for another action.

When an adapter performs asynchronous work, carry the expected attempt and revision through the wait. Recheck current ownership and expiry before accepting its result. A successful normalization for an older capture should not overwrite the caller's later correction simply because it completed last.

Evaluate the whole capture path

Begin with deterministic synthetic fixtures. Test group preservation, leading zeroes, literal plus evidence, separate extensions, explicit missing context, numeric input rejection, mutable source aliases, revision conflicts, and expiry before correction. Run the complete published fence rather than a simplified copy of its happy path.

The regression testing guide provides stable fixture and expected-disposition practices. Include the serialization and export boundaries actually used by the application. A pure capture test does not prove that a spreadsheet, database adapter, or provider request retains the same literal.

Synthetic preservation fixtures distinguish missing context and separate extensions without real destinations

For speech evaluation, use consented task material with reviewed labels. The multilingual benchmark guide explains why language conditions and critical-field outcomes need explicit cohorts. A parser accepting synthetic digits does not establish that the recognizer heard those digits correctly.

Count the complete attempt population. Clarification, cancellation, expiry, unsupported input, adapter failure, and confirmed capture should remain visible. The outcome metrics guide explains why reporting only successfully parsed destinations can conceal the actual interaction cost.

For an application using 60db Agent, apply this contract at the documented integration boundary you own. It does not describe private numbering logic or an internal recognition architecture. The useful result is a preserved, current, explicitly contextualized candidate that a supported downstream adapter can interpret honestly.

Replay a lost-zero incident across interfaces

Begin with a synthetic source value whose first group contains zeroes. Record the exact group tuple before any parser runs. Send that value through the same JSON and persistence boundaries used by the application, then compare the returned literal character sequence with the source. The expected result is exact textual preservation, not equality after numeric conversion.

Include a negative fixture that supplies a number instead of a string. The correct outcome is an explicit rejection at the supported boundary because the original spelling is unavailable. A formatter that pads that number may produce a destination-shaped string, but the test should reject the invented reconstruction even if a later parser accepts its shape.

Next separate the main destination and extension. Send an empty extension, an extension containing leading zeroes, and an unsupported extension character through the adapter. Check that the first remains empty, the second remains literal text, and the third receives a declared unsupported disposition. Do not let a missing extension become a default extension chosen by a serializer.

Test the literal plus evidence independently from the digit groups. A countryless national capture should remain unresolved after a successful text-preservation check. A capture with an explicit plus can be ready for the next adapter while still being invalid under that adapter's numbering rules. Those outcomes belong to different stages and should remain distinguishable in the report.

Now change the country context while preserving every digit. That is a material interpretation change even though a character comparison on the digit string passes. Create a new candidate revision, clear approval, and repeat the destination with the new context. The old confirmation must not authorize the newly interpreted destination.

A similar test changes only the context provenance. An application default and a caller-supplied region can have the same label but different evidence. Keeping provenance in the candidate snapshot makes that difference available for confirmation and audit. A deployment can define whether this change requires another confirmation, but it should not disappear through an unversioned side field.

Finally, let an asynchronous plan-validation result for the old revision arrive after the correction. It can remain useful historical evidence, but it cannot replace the current candidate. The application should compare the expected attempt and revision before admitting the result, and it should preserve the actual adapter disposition rather than changing it into a current approval.

Keep the minimal failing source in the regression suite. If the defect occurred in a JSON boundary, the fixture should include the exact JSON type and supported payload shape. If an export caused the loss, exercise that export boundary. A local dataclass test alone cannot establish that an unrelated spreadsheet or database driver preserves the value.

Retain the supported attempt deadline independently of input arrival. A quiet caller, a delayed recognition final, and a slow numbering adapter can all leave the controller without new callbacks. Readiness queries still need to advance expiry. A correction received at the deadline cannot reset the old operation's lifetime before expiry is recorded.

Use a new attempt identity when the recovery flow starts another capture. The small reference operates on one caller-owned Attempt value, so the surrounding application must route events to the correct instance. Reusing a global field without an attempt identity makes a late old result indistinguishable from a current one. That routing responsibility is separate from the revision ordering demonstrated locally.

Test that separation with a synthetic replay. Preserve an old normalization result, begin a new attempt, and then deliver the old result through the real adapter. The new attempt should remain unchanged. Report the old result as stale historical evidence rather than a current invalid-number error, because the caller may have supplied an entirely different valid candidate in the new attempt.

Specify the adapter acceptance report

An adapter report should identify its supported input contract, interpretation policy, library version, metadata version when available, and the exact output representation. It should retain a clear association with the capture revision. That association lets a reviewer distinguish an old successful normalization from the result applicable to the current destination.

Avoid a single boolean called valid when several checks are performed. A supported input shape, a possible numbering pattern, a documented valid range, and a successful reachability observation are different results. Name each property, represent unavailable checks explicitly, and let the workflow decide which evidence is required for the intended action.

The report should also say what was not attempted. The reference emits that numbering-plan and ownership checks were untested because neither belongs to its local promise. A production integration can add stronger evidence, but the evidence must come from the relevant adapter or authorized workflow rather than inheriting a convenient success flag from capture.

If a normalization changes the displayed grouping while preserving the interpreted destination, keep both representations. The original groups help reproduce caller repair; the normalized display can help a user recognize a familiar format. A comparison rule may treat them as equivalent for a particular purpose without declaring the original source unnecessary.

If a transformation changes digits or prefix interpretation, record that as a material operation. The product may permit a known vanity-number conversion or context-specific national-prefix removal, but it must state that policy and confirm the intended result. Parser convenience is not sufficient evidence that the caller meant the transformed destination.

Choose a stable error vocabulary. Missing context can request clarification, unsupported characters can request a different capture route, and an unavailable adapter can trigger a bounded retry. An invalid number under current metadata may need another question. Returning the same empty string for all these outcomes deprives the application of the information needed to recover safely.

Keep the operational boundary narrow when sharing examples. A public fixture can show literal preservation and parser interpretation without initiating a call or revealing an actual destination. A later live check needs explicit authorization, permitted endpoints, and its own stop conditions. Do not use a supposedly harmless formatting diagnostic to initiate unplanned outbound traffic.

A release decision should name which source interfaces have been tested. A voice adapter, a keypad adapter, a web form, and an imported contact file can supply different evidence about grouping and country context. Passing one path does not establish all four. Expand support only when each path can produce the same documented candidate contract or a clear unsupported disposition.


Frequently asked questions

Why are phone numbers strings?

Their digits identify a destination rather than a quantity. Integer conversion can remove leading zeroes before the application has authority to interpret them. Preserve literal groups and digits, then let a supported numbering-plan adapter produce a separately recorded interpretation.

Does a calling code equal an ISO country code?

No. Numeric calling codes and ISO region identifiers are different namespaces and can have different relationships. Name the field explicitly. Do not put a calling-code integer into a region field and assume a parser will infer the intended context correctly.

Can one calling code serve multiple regions?

Yes. A calling code is insufficient as a universal one-to-one geographic identifier. It also does not establish the caller's current location, language, or citizenship. Ask for the specific context the supported adapter needs instead of deriving unrelated facts from the prefix.

When is a leading zero significant?

Its meaning depends on the numbering context. It can be a dialing prefix or part of a significant destination representation. Preserve it during capture and require plan-aware evidence before removal. A blanket trim operation cannot safely distinguish those cases.

Should 00 always become a plus sign?

No. The libphonenumber FAQ explains that 00 is not the international dialing prefix in every originating region. Keep literal plus evidence distinct from leading digits. A context-aware adapter can apply its supported rules without rewriting the original capture record.

How should an extension be captured?

Store it separately from the main digit string and repeat it back explicitly. A downstream adapter may require its own extension representation. Concatenation can change the interpreted destination and should not be used as an undocumented shortcut to satisfy a numeric field.

What context does a national number need?

It needs the context required by the supported numbering-plan adapter, such as an explicitly supplied region. Preserve how that context was obtained. A configured default and a caller-supplied value are different evidence, even when they happen to contain the same label.

Can a regex validate worldwide telephone numbers?

A regex can enforce a documented capture shape, but it cannot replace evolving global numbering metadata. Keep its claim narrow. The reference checks literal ASCII groups and revision behavior; it deliberately does not establish current assignment, reachability, or ownership.

What is possible versus valid in libphonenumber?

They are different library checks whose exact semantics belong to the documented API and metadata version. Neither should be treated as proof that a destination belongs to the intended person. Record which check was performed and preserve its result separately from the original capture.

Does a valid number prove ownership?

No. Format or range validity is distinct from current assignment, reachability, and the identity of a respondent. A separate authorized verification workflow needs its own evidence. Capturing a destination correctly does not grant permission to disclose information to whoever answers.

How should grouped digits be repeated back?

Use the preserved candidate groups and make country context and extension explicit. Presentation can add clear spacing or pacing without changing the value. Let the caller repair a specific group, then confirm the resulting current revision rather than relying on an earlier readback.

What happens after a one-digit correction?

The correction creates a material new candidate revision and clears the old approval. A confirmation naming the previous revision is stale. The reference also checks expiry before accepting replacement, so a correction cannot revive an operation whose deadline already passed.

Should Arabic-Indic digits be accepted?

That depends on an explicit supported mapping policy. Preserve the original representation, map only documented decimal characters, and confirm the resulting candidate. The ASCII teaching subset returns unsupported input rather than silently normalizing every Unicode numeric character into a telephone digit.

How can fixtures avoid real telephone numbers?

Use clearly synthetic incomplete fragments and never dial them. Those fragments can establish text preservation, prefix evidence, revision handling, and extension separation. Do not claim that an incomplete fixture is a valid global destination or use it as a carrier test.

Can a phone prefix identify current location?

Not reliably. The numbering context and the person's current location are different facts. Forwarding, travel, reassignment, or other routing can break a location assumption. Collect location separately when the task actually needs it, with its own confirmation and privacy boundary.

Should recognizer language select country context?

No. Recognition language is a configuration or observation about speech, not authority over the destination's numbering plan. A caller can speak one language while providing another region's number. Ask for missing context rather than inserting a prefix from language metadata.

When should capture stop for clarification?

Stop when the supported grammar or required context is unresolved, including uncertain characters or a missing national-number region. Distinguish clarification from expiry and adapter failure. A plausible-looking joined string is insufficient evidence to guess a destination and proceed.

What is outside the standard-library example?

Worldwide numbering validation, tel URI parsing, speech interpretation, dialing, provider integration, and caller authentication remain outside its scope. The program preserves a bounded literal shape and current confirmation state. A real integration must supply and test each additional boundary separately.

Should formatting overwrite original input?

No. Keep the source capture and the interpreted or formatted value separately with provenance. A parser can make useful transformations under a documented policy, but those changes should remain reviewable. Silent replacement makes later correction and incident diagnosis much harder.

Which confirmation permits a downstream action?

Capture confirmation establishes the current destination candidate only. The action's complete proposal and authorization policy decide whether it may proceed. Bind approval to every material field, recheck after asynchronous work, and never treat a confirmed telephone string as universal business permission.

Start with one explicit destination contract

Preserve the supplied groups, leading zeroes, plus evidence, context provenance, and extension before interpretation. Run the complete synthetic reference and inspect its emitted capture report. Then add the actual numbering-plan adapter as a separately versioned boundary.

Test every serialization handoff and the caller's correction path. A useful release decision names which literals are supported, which ambiguities require clarification, which evidence confirms a current candidate, and which stronger claims remain untested. That gives the next engineer a repeatable destination contract rather than a collection of prefix guesses.