60db Logo
  • Guides

Ringback, early media, and answering machines

Ringback and early media do not prove human answer. Run a per-leg reducer for signaling, media, AMD, and explicit task outcomes.

Editorial date
Reading time
26 min
Separate gates retain signaling, media, classification, and task evidence

TL;DR: Ringback, early media, final SIP acceptance, answering-machine classification, and task completion are separate evidence. Track them per call leg, distinguish local ringback from received remote audio, and keep unknown classification explicit instead of turning a successful connection into a claim that a human answered.


An outbound call produces ringback, then the provider reports an answered call. The application begins a sensitive interaction, but the audible sound came from a local ring generator and the remote endpoint has not supplied usable audio. Another call reaches voicemail, yet the application treats its greeting as proof that a person is listening.

Separate the observations before adding another timeout. The SIP troubleshooting guide explains why signaling success cannot establish two-way media. Call progress adds further boundaries between media observation, classification, and the action your application is allowed to take.

This guide supplies a pure reducer over synthetic normalized events. It records per-leg evidence and returns policy dispositions. It does not parse SIP, classify audio, dial real numbers, establish caller identity, or claim measured answering-machine accuracy.

The base signaling model comes from RFC 3261, while RFC 3960 addresses early-media and ringing-tone behavior. Read the exchange and media observations rather than relying on a universal rule that one provisional status always contains playable audio.

Table of contents

  1. Name each observation boundary
  2. Separate local ringback from remote media
  3. Interpret provisional signaling carefully
  4. Keep answering-machine labels fallible
  5. Run a per-leg evidence reducer
  6. Apply policy without inventing certainty
  7. Diagnose missing and late callbacks
  8. Evaluate outcomes and denominators
  9. Replay a misleading answered-call trace
  10. Plan a bounded integration evaluation
  11. Frequently asked questions
  12. Make the next action evidence-based

Name each observation boundary

Signaling describes the session exchange at the point you observe it. A provisional response can indicate progress while final acceptance establishes another protocol fact. Neither automatically proves that the expected media arrived, decoded correctly, reached a playback device, or supported a useful exchange with a person.

Media observation also needs a named location. Locally received RTP packets are evidence about receipt at that receiver. Decoded inbound audio is stronger evidence about a usable local decoding path. Acoustic playback at the remote endpoint is a different observation, and human comprehension is different again.

Classification is an interpretation supplied by a detector or provider. A label such as human, machine, fax, or unknown must retain its source and meaning. It should not be promoted into business identity, consent, or authority merely because it is convenient for routing.

Task outcome belongs to the application workflow. A provider can report that a call completed even when the application never captured a required field or the user declined the action. Keep connection, interaction, and authorized task completion as distinct outcomes with their own denominators.

The voice outcome metrics guide explains that denominator discipline. A successful media connection can be a useful technical outcome without being a successful task. Reporting both lets an engineering team locate failures instead of hiding them in one overall answer rate.

A call leg is the scope for these observations. Forking, redirection, or provider routing can produce more than one leg with different signaling and media. Do not combine acceptance from one leg with audio from another and conclude that the selected leg has a healthy conversation path.

The transport comparison guide helps identify which layer exposes each event. A provider status callback, a SIP response, a media-stream event, and an application task result are different contracts. A normalized controller should preserve those distinctions even when it presents a smaller public event vocabulary.

Per-leg observations become an owned evidence snapshot before policy selection

The reference below deliberately returns policy-needed labels. It does not automatically speak, record, transfer, or perform a business action. That keeps a local evidence reducer from pretending it owns the legal, consent, identity, and product decisions attached to an outbound call.

Separate local ringback from remote media

Local ringback is audio generated near the caller side to represent progress. Remote early media is audio received from the remote side before final session acceptance under the relevant signaling and media policy. A caller hearing a ring-like sound does not identify which path produced it.

This distinction matters in a concrete negative fixture. Receive a provisional 180 response, observe no inbound RTP, and play a locally generated ringback tone. The local ringback can be audible while the remote-media field remains unobserved. Hearing ringback does not justify setting remote media to usable.

The fixture uses a synthetic zero observed inbound-packet count only to make that absence explicit. It is not a measured carrier result. A real receiver must define its observation window, source identity, and counters before interpreting zero as a complete statement about received traffic.

If packets arrive, inspect the media contract. Wrong payload mapping or sample interpretation can produce bytes without usable decoded audio. The PCMU versus PCMA guide provides deterministic format checks that should precede interpreting noisy media as a classification problem.

Usable inbound audio is still a local observation. A decoded remote announcement can establish that some remote media reached your decoder. It does not prove a bidirectional path or that your outbound speech reached a human. The playback acknowledgment guide explains why delivery claims need the exact acknowledgment boundary.

Provisional signaling and usable media form different call-progress cases

Keep ringback policy separate from the media flag. An application may need to stop a local tone when usable remote early media begins, depending on its stack and service policy. That transition does not mean the remote party answered or that the received announcement is a human greeting.

Do not use a generic audio-present flag for every source. Local prompt playback, local ringback, received remote audio, and acoustic echo can all produce energy at different observation points. The echo and barge-in guide shows why source attribution matters when a voice system hears its own output.

Interpret provisional signaling carefully

A provisional status code is a protocol observation with context. RFC 3960 discusses how ringing-tone generation interacts with early media. Avoid the common oversimplification that 180 always means no media while 183 always means playable remote audio. Inspect SDP, negotiation state, packet receipt, decoding, and the stack's media policy. A historical developer question about outbound SIP ringback illustrates the integration concern, but its simplified answer is not a current or universal standards rule.

A 183 response can occur without usable audio reaching your decoder. A 180 response can coexist with negotiated media in a supported exchange. The reference records the provisional code independently and changes its media field only when the normalized media adapter supplies an observation.

The media policy itself can matter. RFC 5009 defines a private SIP extension for early-media authorization within its applicable network context. It is useful evidence that early-media handling can involve explicit policy, not just a status-code shortcut. Do not present that extension as a universal requirement for every deployment.

Reliable provisional responses add another boundary. RFC 3262 defines the reliability mechanism involving PRACK. Reliability of a provisional exchange does not establish human answer, two-way audio, or a completed task. Keep protocol acknowledgment separate from the meaning of the received content.

A stack should own exact transaction mechanics. The pure example consumes normalized observations such as provisional 180 or final acceptance; it does not implement retransmission, offer-answer parsing, or PRACK handling. Trying to reconstruct those mechanics from a few callback strings would create a misleading miniature SIP stack.

No single signaling or classification event proves a useful conversation

The hold and re-INVITE guide addresses another reason to preserve negotiation context. Media properties can change during a session, and the application must distinguish a validated effective media revision from local playback ownership. An old successful exchange cannot authorize stale speech after the application has moved on.

When a provider abstracts SIP, use the provider's documented event semantics. A callback named answered may correspond to a provider-defined call boundary rather than your own observation of a human response. Record that boundary honestly. It can be useful without supporting every stronger interpretation the application would prefer.

Keep answering-machine labels fallible

Answering-machine detection is a classification feature with modes, timing behavior, and failure cases. Current Twilio answering-machine detection documentation distinguishes detection modes and possible results. Use the mode and result vocabulary configured for your integration.

A machine-start observation and an end-of-greeting observation serve different policies. If the application intends to leave a permitted message, knowing that a machine greeting started is not the same as knowing a suitable message window was detected. A detector's label remains evidence, not a guarantee that a recording will be complete or understood.

Keep fax and unknown explicit. Unknown can result when the detector cannot provide the required classification under its configured conditions. Treating unknown as human removes uncertainty from the data without adding evidence. Treating every unknown as machine can be equally misleading for outcome reporting.

Synchronous and asynchronous integration choices affect when the application receives evidence. A missing AMD callback does not become a human result because ordinary call-status callbacks arrived. Verify the exact configured callback route, request semantics, and endpoint behavior before changing the policy.

The historical question about status callbacks arriving without AMD callbacks is a useful diagnostic prompt, but its old snippets are not a current provider contract. A separate question about leaving a message after machine detection motivates the distinction between initial machine classification and greeting completion; current provider documentation remains authoritative. Do not copy destinations, credentials, or logs from forum posts into a reproducible example. The synthetic reducer avoids those identifiers entirely.

A human label does not authenticate the person. Shared phones, forwarding, or another person answering can defeat an identity assumption even when a detector classifies human speech correctly. The shared-phone memory guide develops that separate ownership and disclosure boundary.

Unknown classification remains unknown when a local wait ends

A greeting that sounds personal is not a reliable application identity proof either. Keep any business verification and consent steps in the workflow. The workflow enforcement guide explains why required actions and approvals should not depend on a model inferring permission from conversational appearance.

Run a per-leg evidence reducer

The complete Python 3.11-or-newer fence uses only the standard library. It accepts already authenticated, normalized, per-leg observations and records immutable snapshots. The adapter supplies a supported local total revision order for this replay; the program does not claim all remote callbacks naturally arrive in that order.

Strictly older revisions receive stale-revision before current-identity conflict checks. An exact retry of the current supported identity is a duplicate, while changed content under that identity is a conflict. Once the leg ends, expires, or overloads, later events cannot restore an active state.

from dataclasses import dataclass, replace, FrozenInstanceError

AMD = ('human', 'machine_start', 'machine_end_beep',
       'machine_end_silence', 'machine_end_other', 'fax', 'unknown')
VALUES = {'provisional': ('180', '183'), 'accepted': ('yes',),
          'local-ringback': ('audible',), 'remote-media': ('usable', 'unusable'),
          'amd': AMD, 'task': ('verified', 'unverified'), 'close': ('ended',)}


@dataclass(frozen=True)
class Event:
    leg: str
    identity: str
    revision: int
    kind: str
    value: str


@dataclass(frozen=True)
class Leg:
    identity: str
    deadline: int
    revision: int = -1
    status: str = 'active'
    provisional: str = ''
    accepted: bool = False
    local_ringback: bool = False
    remote_media: str = 'unobserved'
    amd: str = 'unknown'
    task: str = 'unverified'
    history: tuple = ()


def begin(identity, deadline):
    if type(identity) is not str or not identity or type(deadline) is not int or deadline < 0:
        raise ValueError('invalid leg')
    return Leg(identity, deadline)


def apply(state, event, now):
    # Adapter already authenticated and scoped the normalized observations.
    if type(now) is not int or now < 0:
        raise ValueError('invalid local clock')
    if event.leg != state.identity:
        return state, 'stale-leg'
    if state.status != 'active':
        return state, state.status
    if now >= state.deadline:
        return replace(state, status='expired'), 'expired'
    if type(event.revision) is not int or event.revision < 0:
        return state, 'invalid'
    if event.revision < state.revision:
        return state, 'stale-revision'
    if (type(event.identity) is not str or not event.identity
            or type(event.kind) is not str or event.kind not in VALUES
            or type(event.value) is not str or event.value not in VALUES[event.kind]):
        return state, 'invalid'
    signature = (event.revision, event.kind, event.value)
    old = dict(state.history)
    if event.identity in old:
        return state, 'duplicate' if old[event.identity] == signature else 'conflict'
    if event.revision == state.revision:
        return state, 'conflict'
    if len(old) >= 64:
        return replace(state, status='overloaded'), 'overloaded'
    old[event.identity] = signature
    state = replace(state, revision=event.revision, history=tuple(old.items()))
    if event.kind == 'provisional':
        state = replace(state, provisional=event.value)
    elif event.kind == 'accepted':
        state = replace(state, accepted=True)
    elif event.kind == 'local-ringback':
        state = replace(state, local_ringback=True)
    elif event.kind == 'remote-media':
        state = replace(state, remote_media=event.value)
    elif event.kind == 'amd':
        state = replace(state, amd=event.value)
    elif event.kind == 'task':
        state = replace(state, task=event.value)
    else:
        state = replace(state, status='ended')
    return state, 'recorded'


def advance_clock(state, now):
    if type(now) is not int or now < 0:
        raise ValueError('invalid local clock')
    if state.status == 'active' and now >= state.deadline:
        return replace(state, status='expired')
    return state


def policy(state, now):
    # Persist the returned snapshot, including clock-only terminal expiry.
    state = advance_clock(state, now)
    return state, policy_disposition(state)


def policy_disposition(state):
    if state.status != 'active':
        return state.status
    if not state.accepted:
        return 'await-final-acceptance'
    if state.remote_media != 'usable':
        return 'await-media-evidence'
    if state.amd == 'fax':
        return 'nonvoice-policy-needed'
    if state.amd in ('machine_end_beep', 'machine_end_silence', 'machine_end_other'):
        return 'message-policy-needed'
    if state.amd == 'human':
        return 'bounded-interaction-policy-needed'
    return 'classification-unknown'


def fixtures():
    initial = begin('synthetic-leg-a', 100)
    def ev(n, kind, value, identity=None, leg='synthetic-leg-a'):
        return Event(leg, identity or str(n), n, kind, value)
    state, _ = apply(initial, ev(0, 'provisional', '180'), 1)
    state, _ = apply(state, ev(1, 'local-ringback', 'audible'), 2)
    assert state.local_ringback and state.remote_media == 'unobserved'
    # Zero observed inbound RTP is not remote media evidence.
    inbound_rtp_packets = 0
    assert inbound_rtp_packets == 0 and not state.accepted
    state, _ = apply(state, ev(2, 'accepted', 'yes'), 3)
    assert policy(state, 6)[1] == 'await-media-evidence'
    state, _ = apply(state, ev(3, 'remote-media', 'usable'), 4)
    assert policy(state, 6)[1] == 'classification-unknown' and state.task == 'unverified'
    human = ev(4, 'amd', 'human')
    state, _ = apply(state, human, 5)
    assert policy(state, 6)[1] == 'bounded-interaction-policy-needed'
    assert state.task == 'unverified'
    assert apply(state, human, 5)[1] == 'duplicate'
    assert apply(state, replace(human, value='fax'), 5)[1] == 'conflict'
    assert apply(state, ev(3, 'amd', 'fax', identity='4'), 5)[1] == 'stale-revision'
    other = begin('synthetic-leg-b', 100)
    assert apply(other, human, 5)[1] == 'stale-leg'
    assert other.remote_media == 'unobserved' and not other.accepted
    p183, _ = apply(initial, ev(0, 'provisional', '183'), 1)
    assert p183.remote_media == 'unobserved'
    p180media, _ = apply(state, ev(5, 'provisional', '180'), 6)
    assert p180media.remote_media == 'usable'
    for label in AMD:
        labeled, _ = apply(state, ev(5, 'amd', label), 6)
        assert labeled.task == 'unverified'
        if label.startswith('machine_end'):
            assert policy(labeled, 6)[1] == 'message-policy-needed'
        elif label in ('unknown', 'machine_start'):
            assert policy(labeled, 6)[1] == 'classification-unknown'
    lost, _ = apply(state, ev(5, 'remote-media', 'unusable'), 6)
    assert policy(lost, 6)[1] == 'await-media-evidence'
    closed, _ = apply(state, ev(5, 'close', 'ended'), 6)
    assert apply(closed, human, 7)[1] == 'ended'
    assert closed.task == 'unverified'
    expired, result = apply(state, ev(5, 'amd', 'unknown'), 100)
    assert result == 'expired' and expired.amd == 'human'
    assert policy(expired, 6)[1] == 'expired'
    assert apply(expired, human, 5)[1] == 'expired'
    idle_expired, disposition = policy(state, 100)
    assert disposition == 'expired' and idle_expired.status == 'expired'
    assert idle_expired.amd == 'human'
    assert policy(idle_expired, 5)[1] == 'expired'
    assert apply(idle_expired, human, 101)[1] == 'expired'
    assert policy(state, 99)[1] == 'bounded-interaction-policy-needed'
    invalid, outcome = apply(state, ev(5, 'amd', 'certain-human'), 6)
    assert outcome == 'invalid' and invalid is state
    assert apply(state, replace(human, revision=True), 6)[1] == 'invalid'
    full = replace(state, history=tuple((str(i), (i, 'amd', 'unknown')) for i in range(64)))
    assert apply(full, ev(100, 'amd', 'unknown', identity='fresh'), 6)[1] == 'overloaded'
    try:
        state.amd = 'fax'
    except FrozenInstanceError:
        pass
    else:
        raise AssertionError('mutable snapshot')
    print('synthetic call-progress evidence fixtures passed')

if __name__ == '__main__':
    fixtures()

The local deadline and history bound use synthetic integer units and a deliberately small fixture limit. They are not recommended carrier timeouts or measured workload capacity. A deployment must choose its clock, event population, and bounds through its supported adapter and policy.

The terminal checks preserve historical evidence without reopening the leg. An expired snapshot can retain the last AMD label because that was the observed history, while its policy disposition remains expired. Retaining a label is different from authorizing another action from it.

Separate immutable construction from trust. Strings, booleans, integers, and tuple history prevent caller mutation of a returned snapshot through a nested dictionary. They do not verify that an upstream callback was authentic, that a label was accurate, or that a media adapter correctly identified its source.

Apply policy without inventing certainty

Call policy(state, now) at each selection boundary and persist both its returned snapshot and disposition. It advances the local deadline even when no callback arrives; once stored as expired, that snapshot cannot be revived by a later callback or an earlier clock argument. The local owner must schedule a timer if it needs expiry recorded while no policy selection occurs.

The policy function returns await-final-acceptance until its local acceptance field is true. It then requires usable remote media before considering the classification label. These are deliberately conservative requirements for this example's interaction policy, not a universal restriction on every early-media application.

For a human label, the function returns bounded-interaction-policy-needed. It does not return authenticated, authorized, or task-complete. For a detected message window, it returns message-policy-needed. The business application still decides whether any message is permitted and what content can be disclosed.

Machine-start and unknown retain classification-unknown under this bounded policy. A machine-start label contains useful evidence, but the example does not equate it with a ready message window. A different application can define another disposition if it explains and tests that policy explicitly.

The criteria table uses ✓ for a property an observation can establish within its stated boundary. A ✗ means that observation cannot establish the property by itself. These are capability distinctions, not performance scores or guarantees about a provider detector.

Observation Signaling progress Locally usable remote audio Human identity Task success
Provisional response ✓ ✗ ✗ ✗
Audible local ringback ✗ by itself ✗ ✗ ✗
Valid decoded inbound media ✗ by itself ✓ ✗ ✗
AMD human label ✗ by itself ✗ by itself ✗ ✗
Verified application outcome ✗ by itself ✗ by itself ✗ without identity evidence ✓ within task criteria

Human, machine-window, fax, and unknown labels retain separate task outcomes

Do not let a classification deadline create certainty. If waiting ends before a supported result arrives, the wait expired. The truthful waiting-speech guide gives a related principle: progress language should match observed state rather than imply an action or result that has not occurred.

If policy permits proceeding under uncertainty, record that decision explicitly. The outcome should show both the unknown evidence and the chosen bounded action. Deleting the unknown label from the report makes the later task result appear to have had stronger input evidence than it did.

Diagnose missing and late callbacks

Begin with event routing and identity. Confirm that the callback belongs to the expected leg, that the configured route receives the relevant event type, and that signature verification and parsing succeeded. A valid callback for another forked leg should not update the selected leg's evidence.

The observability guide helps connect these boundaries without storing caller recordings in general logs. Safe metadata can include the provider event type, opaque leg scope, local ingress order, validation disposition, and current terminal state. Avoid copying raw credential-bearing requests into incident reports.

Keep status and AMD callback paths separate when the provider defines them separately. The Twilio Call resource documentation defines call-state and callback behavior for that interface. Receiving a completed status does not prove a separate AMD event was configured, delivered, or interpreted correctly.

Delayed events need explicit handling. An old provisional observation arriving after final acceptance should not erase acceptance. A late AMD result after terminal close may be retained in a separate audit route if the application needs it, but it must not reopen interaction or start message playback.

The reference rejects late input at the terminal controller boundary. An adapter that needs to preserve late audit evidence should write an append-only observation record separately from the active policy state. Mixing the two encourages a historical callback to regain operational authority.

Inspect media timing independently. The WebSocket head-of-line guide explains why valid old audio can arrive after a transport stall. A delayed greeting is not necessarily a new caller turn, and it should not be classified under a different leg simply because that leg is currently active.

If media disappears after it was usable, update current media evidence rather than leaving a permanent healthy flag. The reducer has an unusable observation for that reason. Historical successful media can remain in a trace while the current policy waits for fresh evidence.

Evaluate outcomes and denominators

Evaluate call-progress policy with synthetic fixtures before attempting a real integration trial. Include 180 without media, 183 without media, 180 with usable media, acceptance without media, local ringback with no inbound packets, unknown AMD, missing AMD callbacks, and late events after close.

The regression testing guide provides the structure for stable fixture identities and expected dispositions. Each fixture should establish one local property. A passing reducer replay does not validate a provider's live callback schedule or a physical endpoint's audio behavior.

For a consented integration trial, define the offered call population and every terminal outcome. Preserve no-answer, busy, failed setup, accepted-without-media, classification-unknown, cancellation, and task outcomes where those apply. Do not calculate detector accuracy from only calls that produced convenient labels.

A detector evaluation requires trustworthy labels and a documented labeling procedure. Human, machine, fax, ambiguous greeting, and unavailable evidence should not be merged merely to simplify a confusion matrix. State whether the test covers a particular language, greeting style, endpoint path, and detector configuration.

The multilingual benchmark guide is relevant when prompts or greetings span languages. A result from one controlled cohort does not establish behavior for another language or speaking style. Avoid attributing differences to a population stereotype when source conditions and labels are uncertain.

Measure time boundaries separately. The latency benchmark guide distinguishes event timing from caller-perceived response. Detection completion, local callback receipt, first application speech, and a verified task result are different endpoints. Keep clock domains and missing observations explicit.

The cost-per-success guide explains why a connected-call denominator can conceal expensive unsuccessful attempts. An early-media announcement or machine greeting can consume resources while producing no useful task. Preserve those outcomes rather than reporting only the cost of successful interactions.

For an application using 60db Agent, evaluate the observable integration boundary available to your deployment. This guide makes no claim about private detector logic or internal call routing. The useful artifact is a public evidence model that prevents protocol progress from being mistaken for a human or task result.

Replay a misleading answered-call trace

Consider a synthetic trace with a provisional response, audible local ringback, final acceptance, and no usable remote audio. A controller with one answered flag can make the entire sequence look healthy. The per-leg model records progress without erasing the missing receive-path evidence.

The first useful question is where the ringback originated. If the local application generated it, record local-ringback rather than remote-media. If the provider supplied an early-media announcement, record the observed receive and decode boundary. An audible sound at one point in the system should not automatically become proof about another point.

Next inspect the accepted session's current media contract. A codec mismatch, unsupported packet mapping, destination change, or receive-path interruption can leave signaling successful while decoded media remains unavailable. Use the smallest known-format fixture that isolates the suspected boundary instead of changing the AMD timeout first.

If usable inbound media later arrives, update that field under a newer supported revision. The earlier local ringback remains historical evidence but does not supply the new media observation. Keeping both facts makes it possible to reconstruct why the application waited and which event changed its policy disposition.

Now add an AMD human label. The controller can choose a bounded interaction policy if the required conditions hold, but the label still does not identify the person. A workflow that needs account ownership or consent must obtain those facts separately. The synthetic trace should end with task unverified until a trusted application outcome supplies a different observation.

Repeat the trace with a machine-start result. The reference retains classification-unknown for its message-window policy because a greeting start is insufficient for that particular next action. This does not discard the machine-start evidence. It distinguishes a useful classifier observation from the stronger evidence the chosen policy requires.

Repeat with a machine-end-beep observation, then close the leg before the application acts. The terminal close wins. A queued message must not begin from the old evidence after close, even if its content was prepared successfully. Output ownership and cancellation need their own final admission check at the writer boundary.

The interruption-safe response guide explains that final output boundary. This article's reducer does not own a playback queue, so its policy disposition cannot guarantee cancellation of audio already admitted elsewhere. Integrate the two contracts explicitly instead of assuming a terminal evidence state magically stops every remote side effect.

Add a second forked leg with usable media while the selected leg still lacks it. Confirm that no field crosses the leg boundary. Selecting a winner is a separate orchestration decision with a documented source of truth; it must not splice the most convenient observations from several legs into a fictional successful conversation.

Finally, replay late provisional and AMD events after the leg ended. The active controller should retain its terminal disposition. If the incident requires the original late observations for diagnosis, store them in the audit record with their own receipt times. Historical completeness does not require restoring active interaction authority.

Preserve the distinction between an event's original identity and its local delivery attempt. A provider retry can arrive through a new HTTP request while describing the same underlying observation. The adapter should use the documented event identity or its explicitly supported normalization policy. Generating a new observation identity for every delivery can make a retry look like fresh progress.

Do not invent a global remote sequence when the interface supplies none. This reference uses a supported local total revision order for replay. A production adapter must explain whether it orders by verified provider sequence, local receipt, or another documented rule. Each choice limits what a stale disposition means. Local receipt order cannot establish the remote order of events across unsynchronized systems.

Keep these limitations in the incident record. An event can be authentic yet stale for active policy, or current in local delivery order yet describe an older remote observation. The active controller needs a safe operational decision, while the audit record can preserve the original timestamps and uncertainty for later diagnosis. Combining both jobs in a single mutable answered flag loses information needed to explain the failure.

Plan a bounded integration evaluation

Begin with a written question. You might need to know whether a configured provider emits an AMD callback, whether local ringback stops when usable early media begins, or whether the selected endpoint has bidirectional media after acceptance. Each question requires a different observation and should have a specific expected disposition.

Use consented test endpoints and synthetic task content. Do not use ordinary customer destinations to discover a callback contract. Keep the trial population, allowed actions, recording permissions, and stop conditions explicit. A media diagnostic should not unexpectedly leave messages, disclose account information, or perform a business mutation.

Prepare scenarios whose expected endpoint behavior is known independently. A controlled human test participant, a permitted machine-greeting fixture, a fax test path where supported, and a deliberately unavailable classification can exercise different branches. The endpoint label must come from the test plan or verified observation rather than being inferred from the very detector you are evaluating.

Retain setup failures and missing labels. If the call never reaches the relevant media condition, it may be ineligible for a classifier comparison but still belongs in setup reliability reporting. Explain each subset and its coverage. Removing inconvenient attempts from every table makes both engineering diagnosis and operational planning weaker.

Use clocks according to their domains. Provider event time, local callback receipt, decoder observation, and application action time can differ. Unless those clocks have a supported synchronization relationship, do not subtract them to create a precise cross-system latency. Record local elapsed intervals where the boundary is under your control.

Keep the original cumulative receiver counters and receiver identity if you derive media deltas. A counter reset or new source can otherwise create a negative or misleading difference. Zero observed packets is different from an unavailable counter, and both are different from packets that arrived but failed decoding. Report those dispositions separately.

Do not use tone presence alone as a human label. A local ring generator, a remote announcement, or playback echo can all create structured audio. The observation can help diagnose the media path without supporting a claim about a live person. Keep source attribution in the test procedure and in the retained result.

Run the same policy configuration across the intended comparison cohort. If you change the AMD mode, deadline, callback route, or greeting-handling policy, create a new condition. Otherwise a result table mixes different experiments and makes it difficult to identify which change caused a behavior difference.

Review task outcomes after the technical observations. A connected human interaction can end in refusal, incomplete capture, or failed authorization, all of which differ from a media failure. The application should preserve those outcomes without treating the caller's legitimate refusal as a broken detector or an unsuccessful network connection.

A release decision should name the supported next action under each evidence state. Unknown may trigger a bounded wait, a generic non-sensitive prompt, an alternate route, or stop, depending on the product policy. Publish the chosen action and the missing evidence. A hidden default that guesses human is harder to audit than an explicit uncertain-policy branch.

Keep the integration result scoped to the tested provider, configuration, endpoint path, and material. A successful trial is useful evidence for that route; it is not a universal claim about every carrier, greeting, language, or device. Schedule reevaluation when a material part of that route changes rather than borrowing old evidence for a new contract.


Frequently asked questions

Does ringback prove the remote phone is ringing?

It proves only the ringback observation at its named source. A local generator can produce the sound without inbound remote media. Inspect signaling and media separately, and avoid using an audible local tone as evidence that a particular remote endpoint is ringing.

Is 180 always a no-media response?

No. Treat that as an oversimplification. RFC 3960 discusses ringing and early-media behavior with the surrounding session context. A 180 observation can coexist with negotiated media, so the application should record the code and media evidence independently.

Does 183 prove early audio is usable?

No. The application still needs the relevant negotiation and receive/decode evidence. Packets may be missing or misinterpreted. The reference records provisional 183 without setting remote media to usable, which makes that missing evidence visible.

What is early media?

It is media exchanged before final session acceptance under the applicable signaling and media policy. It can include announcements or other remote content. It does not by itself establish human answer, bidirectional communication, consent, or a completed application task.

What does PRACK establish?

It participates in the reliable provisional-response mechanism defined by RFC 3262. That protocol reliability boundary is separate from audio usability and human classification. A successful acknowledgment does not prove that a person heard or understood anything.

Can local ringback coexist with zero inbound RTP?

Yes. The synthetic fixture explicitly represents that case and keeps remote media unobserved. A real zero counter needs a declared receiver and observation window. Local audible progress must not be borrowed as evidence about remote packet receipt.

Does final SIP acceptance mean a human answered?

No. It establishes a signaling boundary, while the remote endpoint can be a machine, service, or another participant. Classification, caller identity, and application authorization need their own evidence. The reference leaves all those fields independent.

Is decoded inbound audio proof of two-way media?

No. It establishes a local receive-and-decode observation under the media adapter's contract. Outbound delivery and endpoint playback remain separate. Even working bidirectional media would not by itself establish human comprehension or business consent.

Are answering-machine labels certain?

No. They are fallible classification evidence with provider-specific modes and outcomes. Retain the label and its source, and keep unknown explicit. Do not present a detector label as caller identity or a universal permission to disclose information.

What is different about machine-start and machine-end?

A start observation can indicate that a machine greeting began, while an end observation can support a different message-window policy. The exact provider mode matters. Neither guarantees that a permitted message was recorded completely or understood.

Should unknown be counted as human?

Only if a report explicitly defines a policy bucket with that meaning, and it should still preserve the underlying unknown label. Unknown is missing classification certainty. Silently relabeling it as human makes both accuracy and operational outcomes misleading.

Why might ordinary status callbacks arrive without AMD?

They can be separate configured event paths with different semantics. Check the current provider request, callback route, signature handling, and parsing. A successful status callback does not prove that AMD was enabled or that its separate result was delivered.

Can a late AMD callback reopen a closed leg?

Not in this contract. Terminal state remains terminal, and the active controller rejects the event. If late audit evidence is required, retain it separately without granting it authority to restart speech, message delivery, or another application action.

Why keep evidence per call leg?

Forked or redirected legs can have different signaling, audio, and classification. Combining acceptance from one with media from another creates a false healthy path. Scope every normalized event to its observed leg before updating the selected policy state.

What counts as a duplicate event?

The current supported identity with exactly matching revision, kind, and value is a duplicate. Changed content reusing that identity is a conflict. Strictly older revisions receive stale-revision first, so late delivery does not become a false current-state conflict.

What happens when the local deadline expires?

Clock-aware policy selection returns an updated expired snapshot and disposition even without another callback. Persist that snapshot; it retains historical classification evidence without allowing another action. Expiry ends waiting; it does not prove that the remote party was human, machine, unreachable, or successful.

Does provider completed mean task completed?

No. Provider completion describes its call boundary. The application needs separate evidence that its declared task criteria were met. Preserve both outcomes so failed authorization, incomplete capture, or user refusal do not disappear inside a connected-call result.

Can a human label establish the right person answered?

No. A detector can classify speech as human while a shared phone or forwarding reaches another person. Apply the workflow's identity and disclosure checks separately. The human label does not authorize access to a record or sensitive message.

What should a local fixture suite cover?

Cover independent signaling and media states, local ringback without remote packets, missing classification, fax, machine-window distinctions, wrong-leg events, duplicate/conflict behavior, stale revisions, and terminal late delivery. Those fixtures test controller logic, not real detector accuracy.

What evidence requires a real integration trial?

Provider callback behavior, negotiated media interoperability, device playback, detector accuracy, and application task outcomes require their real observation boundaries. Use consented bounded trials and preserve missing results. A synthetic reducer replay cannot establish carrier or acoustic behavior.

Make the next action evidence-based

Record signaling, media, classification, and task outcome as separate per-leg evidence. Keep local ringback distinct from received remote content, and preserve unknown labels and missing callbacks. Those distinctions make both policy and diagnosis more accurate.

Run the complete reducer fixture, then test the provider adapter under a bounded consented plan. Let the application choose its next action from current scoped evidence, with separate authorization for sensitive work. A connected call is useful progress; it is not a substitute for every result that follows.