# Amplifier Health — API Reference

Concise reference for the Amplifier Health voice biomarker API (v2). Every endpoint, every enum value, and every error code an integration needs is in this document. For decision context, audio quality codes, level definitions, and recommended-action definitions, see the companion **[User Guide](user-guide.md)**.

---

## Base URL & authentication

**Production base URL:** `https://api.amplifierhealth.com`

All v2 requests require both headers on every call:

| Header | Description |
|---|---|
| `X-Account-ID` | Your account identifier. |
| `X-API-Key` | Your v2 API key. |

Sign up at <https://console.amplifierhealth.com>, open the console, and create an API key under **Settings → API Keys**. Your account ID is shown in the console.

### Smoke test

```bash
curl -X GET "https://api.amplifierhealth.com/v2/account/credits" \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key"
```

A successful response returns `{"credits": <integer>}`.

### Limits

| Limit | Value |
|---|---|
| Maximum audio file size | 32 MB per upload |
| Maximum audio duration | 20 minutes (1200 seconds) |
| Minimum audio duration | 15 seconds |
| Supported formats | WAV, FLAC, MP3 |

### Rate limits

Per-account rate limits use a sliding 60-second window:

| Plan | General endpoints | Upload / analyze endpoints |
|---|---|---|
| Standard (default) | 10 requests/min | 5 requests/min |
| Enterprise | 1 000 requests/min | 500 requests/min |

Upload / analyze endpoints are `POST /v2/use-case/analyze` and `POST /v2/condition/analyze`. All other endpoints count as general endpoints. Exceeding the limit returns `429 RATE_LIMIT_EXCEEDED` with a `Retry-After` header.

To request rate-limit changes, contact <sales@amplifierhealth.com>.

---

## POST /v2/use-case/analyze

Submit audio for voice biomarker analysis using a named use-case bundle.

**Content-Type:** `multipart/form-data`

### Request fields

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `audio` | file | yes | — | Audio file. WAV, FLAC, or MP3. Max 32 MB per upload. |
| `use_case` | string | yes | — | Use case identifier. See [Enumerations — `use_case`](#use_case-ids). |
| `diarize` | boolean | no | `false` | Enable speaker diarization. Adds a flat +50 tokens to the cost. |
| `webhook_url` | string | no | — | Per-request HTTPS webhook URL. Overrides any account-level webhook for this job. Must be provided together with `webhook_secret_key`. |
| `webhook_secret_key` | string | no | — | Per-request webhook signing secret. Must be provided together with `webhook_url`. |

`webhook_url` and `webhook_secret_key` must both be provided or both be omitted. Providing only one returns `400 Bad Request`.

### Response

`200 OK` — returns the full job object immediately. `status` is `running` and `result` is `null` until processing completes.

```json
{
  "job_id": "189bce4a-52cb-4e60-8586-cef89e719109",
  "status": "running",
  "created_at": "2026-02-22T09:14:00Z",
  "completed_at": null,
  "result": null,
  "audio_content_type": "audio/wav",
  "audio_size_bytes": 876032,
  "audio_duration_seconds": 27.4,
  "audio_sample_rate": 16000,
  "job_type": "use_case",
  "api_version": "v2",
  "use_case": "behavioral-health",
  "condition": null
}
```

Retrieve the completed result via [`GET /v2/jobs/{job_id}`](#get-v2jobsjob_id) or via webhook.

### Example

```bash
curl -X POST https://api.amplifierhealth.com/v2/use-case/analyze \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key" \
  -F "use_case=behavioral-health" \
  -F "audio=@recording.wav;type=audio/wav"
```

### Errors

| HTTP | Code | Condition |
|---|---|---|
| 400 | — | Only one of `webhook_url` / `webhook_secret_key` was provided. |
| 400 | `INVALID_USE_CASE` | Unrecognized `use_case` value. |
| 400 | `AUDIO_TOO_SHORT` | Recording is under 15 seconds. |
| 400 | `AUDIO_TOO_LONG` | Recording exceeds 20 minutes. |
| 400 | `UNSUPPORTED_FORMAT` | Audio format is not WAV, FLAC, or MP3. |
| 422 | `AUDIO_POOR_QUALITY` | Recording conditions below the threshold used for analysis. |

See [Error reference](#error-reference) for full descriptions.

---

## POST /v2/condition/analyze

Submit audio to analyze a single condition model. Returns the same job object shape as the use-case endpoint, but the completed `result` interior uses a singular `signal` object instead of `summary` + `signals[]`.

**Content-Type:** `multipart/form-data`

### Request fields

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `audio` | file | yes | — | Audio file. WAV, FLAC, or MP3. Max 32 MB per upload. |
| `condition` | string | yes | — | Condition `model_id` to analyze (e.g. `depression`, `anxiety`, `cognitive-impairment`). See [Enumerations — `model_id`](#model_id-condition-ids). |
| `diarize` | boolean | no | `false` | Enable speaker diarization. |
| `webhook_url` | string | no | — | Per-request HTTPS webhook URL. Must be provided together with `webhook_secret_key`. |
| `webhook_secret_key` | string | no | — | Per-request webhook signing secret. Must be provided together with `webhook_url`. |

### Response

`200 OK` — returns the full job object immediately:

```json
{
  "job_id": "4a8f12c9-e7b2-4d3a-9f1c-8b6d2e5a7c0f",
  "status": "running",
  "created_at": "2026-02-22T09:14:00Z",
  "completed_at": null,
  "result": null,
  "audio_content_type": "audio/wav",
  "audio_size_bytes": 876032,
  "audio_duration_seconds": 27.4,
  "audio_sample_rate": 16000,
  "job_type": "condition",
  "api_version": "v2",
  "use_case": null,
  "condition": "depression"
}
```

Retrieve the completed result via [`GET /v2/jobs/{job_id}`](#get-v2jobsjob_id) or via webhook.

### Example

```bash
curl -X POST https://api.amplifierhealth.com/v2/condition/analyze \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key" \
  -F "condition=depression" \
  -F "audio=@recording.wav;type=audio/wav"
```

### Custom bundles

To analyze multiple conditions against the same recording, submit separate requests with the same audio — one per `condition`. Each call creates an independent job; aggregate the results in your application.

### Errors

Same as `POST /v2/use-case/analyze` plus `400 Bad Request` for unrecognized `condition` values. See [Error reference](#error-reference).

---

## GET /v2/jobs/{job_id}

Retrieve a previously submitted job by ID. For a valid `job_id` on your account, the API returns `200 OK` with the job object. **`status`** tells you whether the job is still running, succeeded (`done`), or failed (`failed`) — failure is expressed in the body, not via HTTP error codes.

### Path parameters

| Parameter | Type | Description |
|---|---|---|
| `job_id` | string | The `job_id` returned from a v2 analyze endpoint. |

### Response fields

| Field | Type | Description |
|---|---|---|
| `job_id` | string | Unique job identifier. |
| `status` | string | `pending` \| `running` \| `done` \| `failed`. |
| `created_at` | string (ISO 8601) | Timestamp when the job was created. |
| `completed_at` | string or null | ISO 8601; null while pending/running. |
| `job_type` | string | `"use_case"` or `"condition"`. |
| `api_version` | string | `"v1"` or `"v2"`. |
| `use_case` | string or null | Use-case bundle name. Populated for use-case jobs, null for condition jobs. |
| `condition` | string or null | Condition `model_id`. Populated for condition jobs, null for use-case jobs. |
| `audio_content_type` | string | MIME type of the submitted audio. |
| `audio_size_bytes` | integer | Size of the submitted audio in bytes. |
| `audio_duration_seconds` | number | Duration in seconds. |
| `audio_sample_rate` | integer or null | Sample rate in Hz. |
| `result` | object or null | Full analysis when `status` is `done`; typically `null` when `failed`. See [Schemas](#schemas). |

### Status semantics

- **`pending` / `running`** — analysis in progress. `result` is `null`. Poll at 2–5 second intervals.
- **`done`** — analysis complete. `result` contains the full analysis (use-case or condition shape).
- **`failed`** — processing ended unsuccessfully. `result` is typically `null`; `completed_at` may also be `null`. The API returns `200 OK` with the job object (failure is in the body, not via 4xx). For the failure reason, check operational logs or contact support — the job object does not include a structured failure code.

### Example

```bash
curl https://api.amplifierhealth.com/v2/jobs/189bce4a-52cb-4e60-8586-cef89e719109 \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key"
```

### Errors

| HTTP | Code | Condition |
|---|---|---|
| 404 | `NOT_FOUND` | `job_id` is invalid, the job belongs to another account, or the job does not exist. Verify the ID and your `X-Account-ID`. |

---

## GET /v2/jobs

List jobs for the account, paginated, in reverse chronological order.

### Query parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `page` | integer | `0` | Page number, 0-based. |

### Response

```json
{
  "jobs": [
    {"job_id": "...", "status": "done", "created_at": "2026-02-22T09:14:00Z"}
  ],
  "page": 0,
  "total_pages": 12
}
```

Each item in `jobs` has `job_id`, `status` (`pending` \| `running` \| `done` \| `failed`), and `created_at` (ISO 8601). Use `GET /v2/jobs/{job_id}` to retrieve the full result for a job.

### Example

```bash
curl -X GET "https://api.amplifierhealth.com/v2/jobs?page=0" \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key"
```

---

## Account endpoints

### POST /v2/account/webhook

Set the account-level webhook URL and secret. Once configured, the API sends a signed HTTP POST to that URL when an analysis completes. Per-request `webhook_url` / `webhook_secret_key` form fields override this for individual jobs.

**Content-Type:** `application/json`

**Request body:**

| Field | Type | Required | Description |
|---|---|---|---|
| `url` | string | yes | HTTPS URL where webhooks are sent. Must be publicly accessible. |
| `secret_key` | string | yes | Secret used to sign webhook payloads. Store securely; use only for signature verification on your server. |

**Response:** `200 OK`

```json
{"success": true, "message": "Webhook configured"}
```

**Example:**

```bash
curl -X POST "https://api.amplifierhealth.com/v2/account/webhook" \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-server.com/webhooks/amplifier","secret_key":"your-webhook-secret"}'
```

### GET /v2/account/credits

Check remaining credits for the account. If the account has no remaining credits, analyze endpoints return `402 INSUFFICIENT_CREDITS`.

**Response:** `200 OK`

```json
{"credits": 1234}
```

**Example:**

```bash
curl -X GET "https://api.amplifierhealth.com/v2/account/credits" \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key"
```

---

## Webhook payload

Webhooks are sent **asynchronously** when analysis completes successfully. The API sends an HTTP POST to your configured URL (account-level or per-request).

### Headers

| Header | Description |
|---|---|
| `Content-Type` | `application/json` |
| `X-Webhook-Signature` | HMAC-SHA256 hex digest of the raw request body, computed with your `secret_key`. Use to verify the request came from Amplifier. |

### Body

The body contains the completed job object — the same shape returned by `GET /v2/jobs/{job_id}` when `status` is `done`: `job_id`, `status`, `use_case`/`condition`, `audio_*` fields, and `result` (with `summary`, `signals`, `audio_quality`, and `extended_metrics` for use-case jobs; with `signal`, `audio_quality`, and `extended_metrics` for condition jobs). See [Schemas](#schemas).

### Signature verification

1. Read the raw request body (as bytes or string) and the `X-Webhook-Signature` header.
2. Compute HMAC-SHA256 of the raw body using your `secret_key`.
3. Compare the computed digest with the header using a constant-time comparison.

```python
import hmac
import hashlib

def verify_webhook_signature(secret_key: str, payload_body: str, signature_header: str) -> bool:
    expected = hmac.new(
        secret_key.encode("utf-8"),
        payload_body.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)
```

### Delivery and retries

- Respond within 30 seconds with a 2xx status to acknowledge delivery.
- If your endpoint is unavailable or returns a non-2xx status, the system retries delivery up to 5 times with increasing delays.
- Implement an idempotency check on `job_id` so retried deliveries do not double-process.

---

## Schemas

This is the single source of truth for response field shapes. The User Guide links here for field-level detail.

### Job object (use-case)

Returned by `POST /v2/use-case/analyze`, `GET /v2/jobs/{job_id}` for a use-case job, and the webhook payload.

| Field | Type | Nullable | Description |
|---|---|---|---|
| `job_id` | string | no | Unique job identifier. |
| `status` | string | no | `pending` \| `running` \| `done` \| `failed`. |
| `created_at` | string | no | ISO 8601; format may include `Z`, numeric offset, or no designator; fractional seconds vary. |
| `completed_at` | string | yes | ISO 8601; null while pending/running. May be null when `failed`. |
| `job_type` | string | no | `"use_case"`. |
| `api_version` | string | no | `"v2"`. |
| `use_case` | string | no | Use-case bundle name (e.g. `"behavioral-health"`). |
| `condition` | null | — | Always null for use-case jobs. |
| `audio_content_type` | string | no | MIME type of the submitted audio. |
| `audio_size_bytes` | integer | no | Size of the submitted audio in bytes. |
| `audio_duration_seconds` | number | no | Duration in seconds. |
| `audio_sample_rate` | integer | yes | Sample rate in Hz. |
| `result` | object | yes | Use-case `result` (see below). Null until `status` is `done`; typically null when `failed`. |

#### `result` (use-case)

| Field | Type | Description |
|---|---|---|
| `summary` | object | Aggregated result across all signals. |
| `signals` | array | Per-model signal results. |
| `audio_quality` | object | Recording quality scores and detected issue codes. |
| `extended_metrics` | array | Per-metric wellness scores for this use case. Empty array when no metrics are configured. |

### `summary` object

| Field | Type | Description |
|---|---|---|
| `overall_level` | string | Highest level across all signals. See [Enumerations — `level`](#level-values). |
| `recommended_action` | string | Highest-priority action across all signals. See [Enumerations — `recommended_action`](#recommended_action-values). |
| `flagged_count` | integer | Count of signals where `flagged: true`. |
| `primary_signals` | string[] | Top 1–3 `model_id` values by score, descending. |
| `description` | object | Narrative `summary` plus `vocal_features`. |

#### `summary.description`

| Field | Type | Description |
|---|---|---|
| `summary` | string | LLM-generated plain-text description of the acoustic findings. Surface only after review by qualified care staff. |
| `vocal_features` | array | Annotated acoustic feature values that informed the summary. |

Each entry in `vocal_features`:

| Field | Type | Description |
|---|---|---|
| `feature` | string | Canonical feature identifier (e.g. `speech_rate`, `voice_shimmer`, `pitch_mean`). Use for internal logic. |
| `label` | string | Human-readable feature name (e.g. `"Speech Rate"`). Use for display. |
| `value` | number | Measured value for this recording. |
| `unit` | string | Unit of measurement (e.g. `"dB"`, `"syllables/s"`, `"Hz"`). |
| `value_interpretation` | string | One of `"within range"`, `"slightly elevated"`, `"high"`, `"reduced"`, `"low"`. |

### `signals` array

One entry per model in the requested use-case bundle.

| Field | Type | Description |
|---|---|---|
| `model_id` | string | Model identifier. See [Enumerations — `model_id`](#model_id-condition-ids). |
| `label` | string | Human-readable model name. |
| `score` | number | Raw confidence score, 0.0–1.0. For internal use only; use `level` for display. |
| `level` | string | One of `none`, `low`, `consider`, `moderate`, `elevated`, `inconclusive`. |
| `flagged` | boolean | `true` when `level` is `consider`, `moderate`, or `elevated`. |

### Job object (condition)

Returned by `POST /v2/condition/analyze`, `GET /v2/jobs/{job_id}` for a condition job, and the webhook payload. Same outer shape as the use-case job object except `job_type: "condition"`, `condition` populated, and `use_case: null`. The `result` interior is structurally different.

#### `result` (condition)

| Field | Type | Description |
|---|---|---|
| `signal` | object | Singular signal for the requested condition (see below). |
| `audio_quality` | object | Recording quality scores and detected issue codes. |
| `extended_metrics` | array | Per-metric wellness scores. May be empty. |

### `signal` object (condition response)

| Field | Type | Description |
|---|---|---|
| `model_id` | string | Condition model analyzed. |
| `label` | string | Human-readable condition name. |
| `score` | number | Raw confidence score, 0.0–1.0. For internal use only. |
| `level` | string | One of `none`, `low`, `consider`, `moderate`, `elevated`, `inconclusive`. |
| `flagged` | boolean | `true` when `level` is `consider`, `moderate`, or `elevated`. |
| `recommended_action` | string | Action for this condition: `none`, `monitor`, `consider`, `review`, `escalate`, or `inconclusive`. Derived from this single condition's level. |
| `description` | object | Narrative `summary` plus `vocal_features`. Same schema as [`summary.description`](#summarydescription). |

### `audio_quality` object

Present in both use-case and condition responses.

| Field | Type | Nullable | Description |
|---|---|---|---|
| `issues` | string[] | no | Detected quality issue codes. Empty array when no issues. See [User Guide §6](user-guide.md#audio-quality-issue-codes) for code definitions and recommended actions. |
| `voice_percentage` | number | yes | Percentage of the recording with active speech (0–100). Values below 30 typically correspond to an `insufficient_speech` issue. |
| `audio_clarity` | number | yes | Background-noise quality score (0–100), derived from SI-SDR. Higher is clearer. Values below 50 typically correspond to a `high_background_noise` issue. |

`issues`, `voice_percentage`, and `audio_clarity` are coherent — when a score is below its threshold, the corresponding issue code is typically present.

### `extended_metrics` array

Per-metric wellness scores for the use case. The set returned depends on the `use_case`. Empty array when no metrics are configured.

| Field | Type | Description |
|---|---|---|
| `metric_id` | string | Canonical metric identifier (e.g. `anxious-mood`, `vad-valence`). |
| `label` | string | Human-readable metric name. |
| `score_mean` | number | Mean score, 0.0–1.0. |
| `score_std` | number | Standard deviation of the score, 0.0–1.0. |
| `low_anchor` | string | Verbal anchor at score ≈ 0 (e.g. `"tranquil"`). |
| `high_anchor` | string | Verbal anchor at score ≈ 1 (e.g. `"panicked"`). |

### Error response

Returned for all error conditions.

```json
{"code": "AUDIO_TOO_SHORT", "message": "Audio duration is below the minimum required threshold.", "status": 400}
```

| Field | Type | Description |
|---|---|---|
| `code` | string | Error code. See [Error reference](#error-reference). |
| `message` | string | Human-readable explanation. |
| `status` | integer | HTTP status code. |

### Timestamps

`created_at` and `completed_at` are ISO 8601 / RFC 3339-compatible. Format may vary:

- A timezone designator may appear as `Z` (UTC) or numeric `±hh:mm`, or be omitted entirely.
- Fractional seconds may appear at variable precision.

Use a standard ISO 8601-compatible datetime parser. Treat values without a designator as UTC. Avoid string-comparing timestamps.

Examples (both valid):

```json
"created_at": "2026-02-22T09:14:00Z"
"created_at": "2026-03-28T01:05:35.943000"
```

---

## Enumerations

Definitions for `level`, `recommended_action`, and audio quality issue codes are in the User Guide. This section lists the valid values.

### `use_case` IDs

In the canonical flat order used across both documents:

- `behavioral-health`
- `substance-use`
- `womens-health`
- `wellness`
- `athletics-occupational`
- `cognitive`
- `cardiometabolic`
- `respiratory`

### `model_id` (condition IDs)

| `model_id` | Tier |
|---|---|
| `acute-stress` | Established |
| `adhd` | Emerging |
| `alcohol-use` | Emerging |
| `allergy` | Emerging |
| `anemia` | Investigational |
| `anxiety` | Established |
| `anxiety-female` | Established |
| `cognitive-impairment` | Established |
| `copd` | Established |
| `dehydration` | Investigational |
| `depression` | Established |
| `depression-female` | Established |
| `dry-mouth` | Investigational |
| `elevated-blood-pressure` | Established |
| `fatigue` | Established |
| `hyperandrogenism` | Emerging |
| `overweight-obesity` | Emerging |
| `ptsd` | Emerging |
| `substance-use` | Emerging |
| `traumatic-brain-injury` | Established |

For tier definitions and bundle membership, see [User Guide §1 (Evidence tiers)](user-guide.md#evidence-tiers) and [§5 (Models)](user-guide.md#5-models).

### `level` values

`none` \| `low` \| `consider` \| `moderate` \| `elevated` \| `inconclusive`

For definitions and the score-to-level calibration note, see [User Guide §7 (Levels)](user-guide.md#levels).

### `recommended_action` values

`none` \| `monitor` \| `consider` \| `review` \| `escalate` \| `inconclusive`

For the trigger logic table and the `inconclusive` handling rule, see [User Guide §7 (Recommended actions)](user-guide.md#recommended-actions).

### `audio_quality.issues` codes

`poor_voice_quality` \| `insufficient_speech` \| `high_background_noise` \| `invalid_speaker`

For each code's condition and recommended action, see [User Guide §6 (Audio quality issue codes)](user-guide.md#audio-quality-issue-codes).

### `vocal_features[].value_interpretation` values

`"within range"` \| `"slightly elevated"` \| `"high"` \| `"reduced"` \| `"low"`

### Job `status` values

`pending` \| `running` \| `done` \| `failed`

### `job_type` values

`"use_case"` \| `"condition"`

---

## Error reference

Every error response uses the same JSON shape: `{"code", "message", "status"}`.

| HTTP | Code | When it fires | Resolution |
|---|---|---|---|
| 401 | `UNAUTHORIZED` | `X-Account-ID` is missing or invalid, or the credential header is missing or invalid (`X-API-Key` for v2). | Verify your credentials and resubmit. |
| 402 | `INSUFFICIENT_CREDITS` | No credits remaining for the account. | Add credits or contact support before resubmitting. |
| 404 | `NOT_FOUND` | Resource not found. From `GET /v2/jobs/{job_id}`: the `job_id` is invalid, belongs to another account, or does not exist. | Verify the ID and your `X-Account-ID`; correct and resubmit. |
| 400 | `INVALID_USE_CASE` | The `use_case` value is not recognized. | Correct using the [Enumerations — `use_case` IDs](#use_case-ids) table and resubmit. |
| 422 | `AUDIO_POOR_QUALITY` | Recording conditions may be below the threshold used for analysis. | Improve the recording environment and resubmit. |
| 400 | `AUDIO_TOO_SHORT` | Recording is under 15 seconds. | Submit a recording of at least 15 seconds. |
| 400 | `AUDIO_TOO_LONG` | Recording exceeds the 20-minute (1200-second) maximum. | Trim to 20 minutes or fewer and resubmit. |
| 400 | `UNSUPPORTED_FORMAT` | Audio format is not supported. | Use WAV, FLAC, or MP3. |
| 429 | `RATE_LIMIT_EXCEEDED` | Rate limit reached. | Check the `Retry-After` response header and retry using exponential backoff. |
| 500 | `PROCESSING_ERROR` | Internal processing error. | Retry with the same payload using exponential backoff. |

### Retry guidance

For `429` and `500` responses:

- Start at 1 second.
- Double the delay on each attempt.
- Maximum 5 retries.
- For `429`, check the `Retry-After` response header before retrying.

For `4xx` codes other than `429`, correct the request condition before resubmitting (no exponential-backoff retry).

For decision-making context, audio recording guidance, and end-to-end integration walkthroughs, return to the **[User Guide](user-guide.md)**.
