---
title: "List two-party telephony bridge sessions"
method: GET
path: "/voice/bridge"
tags: ["voice"]
---

# List two-party telephony bridge sessions

`GET /voice/bridge`

## Request

- Base URL: `https://corebee.ai/api/v1`
- URL: `https://corebee.ai/api/v1/voice/bridge`
- Auth: HTTP bearer

Enumerate this organization's bridge sessions so they can be pulled into your own reporting/backoffice. Keyset (cursor) paginated on (created_at, id): follow meta.next_cursor until meta.has_more is false. Do NOT page by offset, rows are inserted continuously and an offset scan silently skips sessions. Keep `order` and the filters identical for every page of a walk; a cursor issued for one direction is refused for the other. Phone numbers are returned unmasked: this endpoint is authenticated with an org-scoped API key and returns only your own sessions. Rows that are not ordinary production traffic (sandbox magic-number tests, seeded sample data, briefing-only test calls) are RETURNED and flagged rather than hidden, exclude them yourself with the sandbox / mock / briefing_only booleans. For the state-transition timeline (and transcript, where enabled) fetch the single session at status_url. By default only the trailing 30 days are returned. Check meta.range.defaulted: when it is true you are seeing a window we bounded for you, and meta.has_more:false means "this window is exhausted", NOT "this is all your data". For a full backfill pass an explicit `from`. For billing use /voice/bridge/usage, which carries billed/amount_cents. That feed no longer needs to be cross-referenced against this one: every row now carries a three-valued `mode` (live / sandbox / demo) and an `invoiceable` boolean, and seeded demo rows come back billed:false. Filter on `invoiceable` for revenue, or on `mode == "live"` if you want the wider set including rows an evidence rule flagged. Briefing-only rows are still excluded from that feed entirely, here as well as from its `total`.

## Query parameters

- `limit` integer
- `cursor` string
- `order` 'asc' | 'desc'
- `from` string
- `to` string
- `state` string[]
- `failure_reason` string[]
- `customer_ref` string

## Response `200`

OK

- object
  - `data` BridgeListItem[], required
    - `id` integer
    - `state` 'created' | 'dialing_business' | 'ivr_nav' | 'amd_verdict' | 'human' | 'voicemail' | 'prep_line' | 'converse' | 'dialing_consumer' | 'bridging' | 'bridged' | 'completed' | 'failed'
    - `failure_reason` string, nullable: Why the bridge failed; null unless state=failed. OPEN set, see BridgeStatus.failure_reason for the full catalogue and the caution about treating unrecognized values as generic failures.
    - `amd_result` 'human_residence' | 'human_business' | 'machine' | 'silence' | 'fax_detected' | 'not_sure', nullable: Premium AMD detection.ended result, null until determined.
    - `business_number` string, nullable: The business leg, unmasked (you supplied it).
    - `consumer_number` string, nullable: The consumer leg, unmasked (you supplied it).
    - `from_number` string, nullable: The originating DID owned by your organization that placed the calls.
    - `customer_ref` string, nullable: Your own identifier, echoed from metadata.customer_ref at create time.
    - `leg_a_call_control_id` string, nullable: Business leg.
    - `leg_b_call_control_id` string, nullable: Consumer leg.
    - `created_at` string, date-time, nullable: When the bridge was requested. This is the field both `from`/`to` and the cursor order on.
    - `updated_at` string, date-time, nullable
    - `bridged_at` string, date-time, nullable: When both legs were actually connected; null if they never were.
    - `recording_duration_sec` integer, nullable: Seconds of SAVED AUDIO for this call, the length of the mp3, as reported by the carrier (whole seconds). This is NOT the billed duration and it is NOT the whole call: see `joined_seconds` for what you are invoiced for. ⛔ SCOPE: the audio covers the AI-TO-BUSINESS leg ONLY. Recording stops before the consumer is dialed, so the consumer is never in it and was never recorded. Do not label this "call recording" in a UI shown to anyone who might reasonably read that as including the consumer. null when no recording exists for the session, which is the ORDINARY case, not an error: recording is a per-account feature and most sessions have no recording row at all. null ALSO when a recording exists but its length was never stored. It is never 0 for "unknown": a 0 would be a claim about the call, a null is a claim about our knowledge. This field never fails the request and never carries a link to audio.
    - `joined_seconds` number, nullable: Seconds two HUMANS were on the line together, THE BILLED INTERVAL. Identical in name, source and value to `joined_seconds` on GET /voice/bridge/usage, so the operational feed and the billing feed cannot quote you different numbers. Read from the carrier's own call.bridged start_time, NOT from `bridged_at`: that field is stamped by our webhook worker a median 7.73s after the fact and on three historical sessions post-dates the hangup, so differencing it yields a negative conversation. null when the evidence pass has not run over this session or the carrier stamps did not survive its checks. Never negative, never guessed from a clock, and never silently falling back to `bridged_at`. It is unrelated to `recording_duration_sec` and the two routinely disagree: a call can be recorded for 41 seconds and never join anybody.
    - `sandbox` boolean: True when this session was produced by a reserved magic test number. Shaped exactly like a real call on purpose, exclude it from your own totals.
    - `mock` boolean: True for seeded sample data shown before real traffic exists. Never a real call.
    - `briefing_only` boolean: True when the request asked for a briefing-only test: the business leg was dialed and the prep line played, but the consumer was NEVER dialed.
    - `status_url` string: Path to the full single-session projection (timeline, transcript).
  - `meta` BridgeListMeta, required: Keyset pagination envelope for GET /voice/bridge.
    - `next_cursor` string, nullable, required: Pass back as `cursor` to fetch the next page. Null when there are no more rows. Opaque, do not parse or construct it.
    - `prev_cursor` string, nullable: Always null: this feed is forward-only. Re-walk from the start to go back.
    - `has_more` boolean, required: Stop your pagination loop when this is false, not when data is empty. It describes the REQUESTED WINDOW only: if range.defaulted is true, false here means the trailing 30 days are exhausted, not that you have every session.
    - `limit` integer, required
    - `returned` integer, required: Rows in this page.
    - `order` 'asc' | 'desc', required
    - `range` object: The created_at window actually applied, after defaults.
      - `from` string, date-time, required
      - `to` string, date-time, required
      - `defaulted` boolean, required: True when you did not pass `from` and we bounded the window to the trailing 30 days. While this is true, has_more:false means "this window is exhausted", NOT "you have all your sessions", a backfill loop that stops here will silently miss everything older. Pass an explicit `from` for a full pull.

## Other responses

- `400`: Validation error
- `401`: Invalid or missing credential

---

[API](https://skmtc.dev/corebee/apis/corebee-api.md) · [All operations](https://skmtc.dev/corebee/apis/corebee-api/llms.txt) · [OpenAPI document](https://skmtc.dev/corebee/apis/corebee-api/revisions/a48ea543b4dc?raw)
