> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rivi.software/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Credentials for listener websites, apps, push registration, and now-playing ingestion.

Station credentials are separate from staff sign-in. Use a dedicated credential for each consumer and choose the narrow scopes it needs.

## Listener tokens

Station administrators create credentials in the RadioTools dashboard (**Settings → API credentials**) with the **`listener:read`** scope. Send the token on every listener JSON read and GraphQL query:

```http theme={null}
Authorization: Bearer <tenant token>
```

The copied token starts with `rtm_` and does not include `Bearer `. Direct HTTP requests must add that prefix, including the space. Sending `Authorization: rtm_…` returns **`401 UNAUTHENTICATED`**, even with a valid token.

In the interactive API playground, and in other API clients with a dedicated **Bearer token** field, paste only the raw `rtm_…` token. The client adds the `Bearer ` prefix.

* **Published media** (`/api/media/public/…`) stays anonymous — no token on image or audio bytes.
* The **public realtime feed** is anonymous too; see [Public delivery](/radiotools/api-reference/public-delivery#public-realtime-feed).
* **OPTIONS** preflight stays unauthenticated; **conditional GETs** (`If-None-Match`) authenticate before returning `304`.
* Other scopes do **not** imply listener access. A dashboard session cookie does **not** substitute for a listener token.
* Missing, expired, revoked, wrong-scope, or cross-station tokens return **`401 UNAUTHENTICATED`**.

Store the token on your **consumer server** only. Browser clients should call your origin, which forwards `Authorization` to RadioTools. Never bundle the token in a mobile or web app and never put it in URLs.

For the fictional RIVI FM sample, set the station slug to `rivi-fm`. Use your own station slug and a credential issued for it in real requests. The environment variable below represents a secret stored on your server.

Set `BASE` to `https://radiotools.rivi.software` and `STATION_SLUG` to your station's public slug. Set `RADIOTOOLS_LISTENER_TOKEN` in your server environment, keeping it out of source control and logs.

```sh theme={null}
BASE="https://radiotools.rivi.software"
STATION_SLUG="rivi-fm"

curl -sS "$BASE/api/v1/stations/$STATION_SLUG" \
  -H "authorization: Bearer $RADIOTOOLS_LISTENER_TOKEN"
```

This server-side JavaScript example uses the raw token stored in an environment variable:

```js theme={null}
const response = await fetch(
  "https://radiotools.rivi.software/api/v1/stations/breeze/schedule?fromDate=2026-10-05&throughDate=2026-10-11",
  {
    method: "GET",
    headers: {
      Authorization: `Bearer ${process.env.RADIOTOOLS_LISTENER_TOKEN}`,
    },
  },
);
```

Replace `breeze` and the dates for your station. Use the full station path, such as `/api/v1/stations/breeze`; `/api/v1/stations/` is not an endpoint.

If you receive `401`, check the header prefix, then verify that the credential belongs to the requested station, includes `listener:read`, has not expired, and has not been revoked. A `404` for weather or traffic can mean the resource is disabled or unavailable; enabling it and the required entitlement are separate from authentication.

## Credential scopes

Credentials belong to **one station**, carry explicit scopes, and expire within one year. Secrets are shown **once** at creation (`rtm_…` prefix); store them securely and rotate or revoke them in the dashboard.

| Scope | Purpose |
| - | - |
| `listener:read` | Published listener REST and GraphQL |
| `push:register` | Listener push device registration and topic changes |
| `ingest:write` | Now-playing metadata ingestion with an integration credential |

## Now-playing ingestion

Use a dedicated **integration** credential for `POST /api/v1/integrations/playback`, with either `?stationSlug=<slug>` or `?stationId=<id>`. The station must match the credential; the body cannot retarget another station.

See [Now-playing ingest](/radiotools/api-reference/now-playing-ingest) for payload examples and [Integrations](/radiotools/station-admin/integrations) for dashboard setup.

## Try requests in the playground

The operation pages send playground requests to the RadioTools API origin. The sample station, record IDs, and device tokens are fictional. Replace them with your station's values and use the credential type required by the selected operation. Ingest and device operations change data when submitted.

For regular website and app delivery, keep credentials on your server and forward only permitted listener data to your clients. Published media URLs are anonymous and can be used directly in image/audio elements.

## Verify access before building the integration

Request the station entry document first, then follow its resource links. Confirm the response belongs to the expected station. A successful public media download does not validate a listener token because published media bytes are anonymous.

On a `401`, check the secret, expiry, revocation, station, and scope. On a `404` after authentication, ask the station administrator to check public-offline state, entitlement, and the API switch. Do not retry indefinitely with the same invalid token.


## Related topics

- [Account and stations](/radiotools/account-and-stations.md)
- [Get started](/radiotools/getting-started.md)
- [Roles and access](/radiotools/station-admin/roles-and-access.md)
- [Public delivery](/radiotools/api-reference/public-delivery.md)
- [GraphQL schema reference](/radiotools/api-reference/graphql-reference.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.