> ## Documentation index
> Fetch the complete documentation index at: https://developer.rhombus.com/llms.txt
> Use it to discover all available pages before exploring further.

# Retrieving Audio from A100 Audio Gateway

> Download recorded audio from a Rhombus A100 Audio Gateway using the API — fetch DASH MPD manifests, retrieve segments, and play back audio over WAN VOD.

In this guide, you will learn how to retrieve **recorded audio** from a Rhombus **A100 Audio Gateway** using the Rhombus API. You will:

1. Call the API to get media URI templates
2. Generate a federated session token for WAN playback
3. Download the DASH MPD and audio segments
4. Optionally convert the resulting Opus/WebM audio using ffmpeg

Rhombus exposes media access through **URI templates** rather than returning raw audio bytes directly from a single REST response.

## Prerequisites

> **Note**: All API calls in this guide use the following authentication headers:
>
> ```http
> Content-Type: application/json
> x-auth-scheme: api-token
> x-auth-apikey: YOUR_API_KEY
> ```
>
> Base URL: `https://api2.rhombussystems.com`

**You will need:**

- An A100 Audio Gateway UUID (Rhombus DeviceFacetUuid / RUUID style string)
- Recorded audio that exists and is available (determined by your device licensing and recording configuration)
- A client capable of:
  - Making HTTP requests
  - Parsing XML (for the MPD manifest)
  - Writing binary segment bytes to disk

## Implementation steps

1. **Get the A100 Audio Gateway Media URIs**

   Call the `getMediaUris` endpoint to retrieve the URI templates for your audio gateway.

   **Endpoint:** [`POST /api/audiogateway/getMediaUris`](https://developer.rhombus.com/api-reference/audiogateway-webservice/get-media-uris-for-audio-gateway)

   ```bash
   curl --request POST \
     --url https://api2.rhombussystems.com/api/audiogateway/getMediaUris \
     --header 'Content-Type: application/json' \
     --header 'x-auth-scheme: api-token' \
     --header 'x-auth-apikey: YOUR_API_KEY' \
     --data '{
       "gatewayUuid": "AAAAAAAAAAAAAAAAAAAAAA.v0"
     }'
   ```

   **Relevant response fields:**

   | Field                    | Description                                                                                |
   | ------------------------ | ------------------------------------------------------------------------------------------ |
   | `wanVodMpdUriTemplate`   | WAN VOD MPD template (use this for recorded audio over WAN)                                |
   | `wanLiveOpusUri`         | WAN live Opus stream (WebSocket). It doesn't end in `/ws`: append `/ws` before you connect |
   | `wanLiveMpdUri`          | WAN live MPD URI                                                                           |
   | `lanVodMpdUrisTemplates` | LAN VOD templates                                                                          |

   LAN URIs (`lanLiveOpusUris`, `lanLiveMpdUris`, `lanVodMpdUrisTemplates`) are served by the gateway itself, which accepts only federated tokens, by design. See [Streaming over LAN](https://developer.rhombus.com/implementations/lan-streaming) for authentication and the two-way audio WebSocket format.

   The live Opus socket (`wanLiveOpusUri` plus `/ws`) carries listening and two-way talk. From a server, authenticate it with your API key headers or an OAuth access token (`Authorization: Bearer <token>`); in a browser, use a federated token as query parameters. An OAuth token needs the `read` scope to listen and the `physical` scope to send talk audio: audio sent without `physical` closes the socket with code `1008`. If the credential is deleted or revoked, or its role changes, the open socket closes with code `1008` within 60 seconds.

2. **Generate a Federated Session Token**

   From a server, you can fetch the WAN MPD and its segments with your API key headers (`x-auth-scheme: api-token` and `x-auth-apikey`) on every request, or with `Authorization: Bearer <token>` for an OAuth access token with the `read` scope. This guide uses a short-lived federated session token instead, appended as query parameters, which also works in players that can't send headers. (The same two values also work as headers.)

   **Endpoint:** [`POST /api/org/generateFederatedSessionToken`](https://developer.rhombus.com/api-reference/org-webservice/generate-federated-session-token)

   ```bash
   curl --request POST \
     --url https://api2.rhombussystems.com/api/org/generateFederatedSessionToken \
     --header 'Content-Type: application/json' \
     --header 'x-auth-scheme: api-token' \
     --header 'x-auth-apikey: YOUR_API_KEY' \
     --data '{
       "durationSec": 60
     }'
   ```

   **Response:** Returns `federatedSessionToken` and `expiresAtMs`, the time the token expires in Unix milliseconds.

   > **Warning**: Don't set `domain` on a token you use from a server. A token minted with `domain` only works on requests whose `Origin` (or, when there's no `Origin`, `Referer`) is exactly that origin, such as `https://app.example.com`. Server-side requests send neither, so every MPD and segment request fails with 403 `DOMAIN_NOT_ALLOWED`. `domain` must be an absolute origin; a value such as `.rhombussystems.com` is refused when you mint (400). See [The domain field](https://developer.rhombus.com/authentication/federated-tokens#the-domain-field).

   > **Tip**: `durationSec` defaults to 3600 (one hour) and can't exceed 86400 (24 hours). For a download that takes longer than the token lasts, mint a new token before the `expiresAtMs` in the response, as the complete example below does.

3. **Build the WAN VOD MPD URL**

   Take the `wanVodMpdUriTemplate` from Step 1 and perform string substitution:

   - Replace `{START_TIME}` with your requested start time (in seconds)
   - Replace `{DURATION}` with your requested duration (in seconds)

   > **Warning**: **Timestamp units matter.**
   >
   > Rhombus audio endpoints use **seconds since epoch** (`startTimeSec`, `durationSec`). If your timestamps are stored in milliseconds, convert them:
   >
   > ```text
   > startTimeSec = startTimeMs / 1000
   > durationSec = durationMs / 1000
   > ```

4. **Fetch the MPD with the Federated Token**

   Append the authentication query parameters to your MPD URL:

   ```text
   <mpdUri>?x-auth-scheme=federated-token&x-auth-ft=<TOKEN>
   ```

   This MPD is a DASH manifest that describes how to fetch the init segment and subsequent audio segments. On the WAN host, the segment URLs in the MPD carry the same query parameters, so a DASH player given this URL needs nothing else.

5. **Download and Stitch Segments**

   If you build the segment URLs yourself, as this guide does, download them in order, appending the same `?x-auth-scheme=federated-token&x-auth-ft=<TOKEN>` query parameters you used for the MPD — the segments are served from the same WAN host and use the same authentication:

   1. **Init segment** (e.g., `seg_init_audio.hdr`)
   2. **Media segments** (e.g., `seg_1.webm`, `seg_2.webm`, ...)

   > **Info**: Segments are **2 seconds each**. Calculate the number of segments:
   >
   > ```text
   > numSegments = durationSec / 2
   > ```

   Write the init header bytes followed by each segment's bytes to produce a valid WebM/Opus audio file (48 kHz, mono).

---

## Complete Python example

This example implements the full workflow described above:

```python
import time

import requests

BASE_URL = "https://api2.rhombussystems.com"
API_KEY = "YOUR_API_KEY"

HEADERS = {
    "Content-Type": "application/json",
    "x-auth-scheme": "api-token",
    "x-auth-apikey": API_KEY,
}


class FederatedToken:
    """A federated session token for WAN access (no domain: server-side use), minted again before it expires."""

    def __init__(self, duration_sec: int = 3600):
        self.duration_sec = duration_sec  # at most 86400 (24 hours)
        self.token = None
        self.expires_at_ms = 0

    def query(self) -> str:
        # Mint a new token when this one is within a minute of expiresAtMs
        if time.time() * 1000 > self.expires_at_ms - 60_000:
            url = f"{BASE_URL}/api/org/generateFederatedSessionToken"
            resp = requests.post(url, headers=HEADERS, json={"durationSec": self.duration_sec})
            resp.raise_for_status()
            body = resp.json()
            self.token = body["federatedSessionToken"]
            self.expires_at_ms = body["expiresAtMs"]
        return f"?x-auth-scheme=federated-token&x-auth-ft={self.token}"


def get_audio_gateway_media_uris(gateway_uuid: str) -> dict:
    """Get media URI templates for an audio gateway."""
    url = f"{BASE_URL}/api/audiogateway/getMediaUris"
    resp = requests.post(url, headers=HEADERS, json={"gatewayUuid": gateway_uuid})
    resp.raise_for_status()
    return resp.json()


def build_mpd_uri(template: str, start_time_sec: int, duration_sec: int) -> str:
    """Substitute placeholders in the MPD URI template."""
    return template.replace("{START_TIME}", str(start_time_sec)).replace(
        "{DURATION}", str(duration_sec)
    )


def download_wan_vod_audio(
    gateway_uuid: str, start_time_sec: int, duration_sec: int, out_path: str
):
    """Download recorded audio from a Rhombus A100 Audio Gateway."""
    # Step 1: Get media URIs
    media = get_audio_gateway_media_uris(gateway_uuid)
    mpd_template = media["wanVodMpdUriTemplate"]

    # Step 2: Generate federated token (renewed before it expires)
    ft = FederatedToken(duration_sec=3600)

    # Step 3: Build MPD URL
    mpd_uri = build_mpd_uri(mpd_template, start_time_sec, duration_sec)

    # Step 4: Fetch MPD (authenticated via query string)
    mpd_resp = requests.get(mpd_uri + ft.query())
    mpd_resp.raise_for_status()

    # Step 5: Download init segment and media segments.
    # This script builds the segment URLs itself instead of reading them from
    # the MPD, so it appends the token to each one. (Sending the API key
    # headers on every request also works.)
    init_uri = mpd_uri.replace("file.mpd", "seg_init_audio.hdr")
    num_segments = duration_sec // 2

    with open(out_path, "wb") as f:
        # Download init segment
        init_resp = requests.get(init_uri + ft.query())
        init_resp.raise_for_status()
        f.write(init_resp.content)

        # Download media segments
        for i in range(1, int(num_segments) + 1):
            seg_uri = mpd_uri.replace("file.mpd", f"seg_{i}.webm")
            seg_resp = requests.get(seg_uri + ft.query())
            seg_resp.raise_for_status()
            f.write(seg_resp.content)


if __name__ == "__main__":
    gateway_uuid = "AAAAAAAAAAAAAAAAAAAAAA.v0"
    start_time_sec = 1700000000  # Example: November 14, 2023
    duration_sec = 60  # 1 minute (max: 86400 seconds / 24 hours)

    download_wan_vod_audio(
        gateway_uuid=gateway_uuid,
        start_time_sec=start_time_sec,
        duration_sec=duration_sec,
        out_path="rhombus_audio.webm",
    )
    print("Wrote rhombus_audio.webm")
```

## Converting audio with ffmpeg

The downloaded audio is in **Opus/WebM** format (48 kHz, mono). You can convert it to other formats using ffmpeg:

```bash WAV (16-bit PCM)
ffmpeg -i rhombus_audio.webm -acodec pcm_s16le rhombus_audio.wav
```

```bash MP3
ffmpeg -i rhombus_audio.webm rhombus_audio.mp3
```

## Troubleshooting

**Empty manifests or missing segments**

**Cause:** Template substitution uses wrong time units.

**Solution:** If your timestamps are in milliseconds but the template expects seconds, convert them:

```python
start_time_sec = start_time_ms // 1000
duration_sec = duration_ms // 1000
```

**MPD rewrite doesn't match your template**

**Cause:** The code assumes the MPD filename is `file.mpd`.

**Solution:** If your template uses a different filename, adjust the string replacement logic:

```python
# Current logic
init_uri = mpd_uri.replace("file.mpd", "seg_init_audio.hdr")

# Adjust to match your actual filename
init_uri = mpd_uri.replace("your_filename.mpd", "seg_init_audio.hdr")
```

**403 on the MPD or segments**

**Cause:** The token was minted with a `domain` (403 `DOMAIN_NOT_ALLOWED`), or the role of the key that minted it can't view the gateway's recorded audio.

**Solution:** Mint the token without `domain` for server-side use, from a key whose role can view the gateway, or send your API key headers instead. See [Media and streams](https://developer.rhombus.com/authentication/media#troubleshooting).

**Token expires before download completes**

**Cause:** The download takes longer than the token lasts. Requests then fail with 401 `CREDENTIAL_EXPIRED`. A token also stops working, with a 401, if it's revoked or the API key that minted it is deleted.

**Solution:** A token lasts at most 24 hours, so don't rely on a longer `durationSec`. Mint a new token before the `expiresAtMs` in the mint response and use it for the remaining segments, as `FederatedToken` in the complete example does.

## API reference

The following endpoints are used in this guide. Visit the [API Reference](https://developer.rhombus.com/api-reference/audiogateway-webservice/get-media-uris-for-audio-gateway) tab for full request and response schemas.

- [`POST /api/audiogateway/getMediaUris`](https://developer.rhombus.com/api-reference/audiogateway-webservice/get-media-uris-for-audio-gateway) — Get Media URIs for Audio Gateway
- [`POST /api/org/generateFederatedSessionToken`](https://developer.rhombus.com/api-reference/org-webservice/generate-federated-session-token) — Generate Federated Session Token
