# Configuration Analysis
Source: https://docs.heify.com/api-reference/configuration/analytics
api-reference/openapi-configurations.json POST /analytics
Generates an AI-powered analytics report for all completed transcriptions under a configuration.
The AI output language is controlled by `analytics_language` on the [Configuration](/core/configuration#analytics-language). Set it to `"df"` for auto-detection based on the most prevalent transcription language.
This endpoint has a **monthly call limit**. The counter resets automatically on the first call of a new month. Exceeding the limit returns `429 Too Many Requests`.
Only **completed** transcriptions are included in the analysis. Pending, processing, or failed transcriptions are excluded.
If no completed transcriptions exist for the configuration, the endpoint returns `200 OK` with `{"data": {"message": "No completed transcriptions found to analyze."}}` — not an error.
The `actionable_insights` and `executive_narrative` fields in `ai_insights` are **Markdown-formatted strings**, not structured objects. Render them accordingly.
# Create Configuration
Source: https://docs.heify.com/api-reference/configuration/create
api-reference/openapi-configurations.json POST /create-configuration
Create a new configuration template that defines how audio/video files will be processed.
For full details on the Configuration object, see [Configuration](/core/configuration).
Each account has a maximum of **20 configurations**. Plan your templates to cover multiple use cases — you cannot exceed this limit.
`tag` is permanent — it cannot be changed after creation. Choose a clear, descriptive name.
The more specific the `description` on each extraction field, the more accurate the AI extraction will be. See [Best Practices for Extraction Fields](/core/configuration#best-practices-for-extraction-fields).
# Delete Configuration
Source: https://docs.heify.com/api-reference/configuration/delete
api-reference/openapi-configurations.json POST /delete-configuration
Permanently deletes a specific configuration. This action is irreversible.
**This action is irreversible!** Once deleted, the configuration cannot be recovered.
Returns `404` if the configuration is not found.
Existing transcriptions that were processed with this configuration remain accessible after deletion. Deleting a configuration does not affect already-processed data.
For a detailed explanation of the Configuration and its parameters, please consult the [Configuration](/core/configuration).
# Get Configuration
Source: https://docs.heify.com/api-reference/configuration/get
api-reference/openapi-configurations.json POST /get-configuration
Retrieve a single configuration by its ID.
For full details on all Configuration fields, see [Configuration](/core/configuration).
Returns `404` if the configuration is not found.
# List Configurations
Source: https://docs.heify.com/api-reference/configuration/list
api-reference/openapi-configurations.json POST /list-configurations
Returns all configurations belonging to the authenticated client.
Each configuration object in the response has the same structure as the [Get Configuration](/api-reference/configuration/get) response. See [Configuration](/core/configuration) for full field details.
# Update Configuration
Source: https://docs.heify.com/api-reference/configuration/update
api-reference/openapi-configurations.json POST /update-configuration
Partially update an existing configuration. Only fields included in the request body are modified.
For full details on all Configuration fields, see [Configuration](/core/configuration).
This endpoint uses **PATCH semantics** — omitted fields are not touched. Only `configuration_id` is required; include only the fields you want to change.
**`extraction_fields` is not a simple replacement — it uses a merge-by-name strategy.** Any existing field **not included** in your request will be **removed**. Always send the complete desired list of fields, not just the ones you're changing.
The `type` of an existing extraction field **cannot be changed**. Attempting to do so returns a `400` error. To change a field type, delete and recreate the configuration.
`tag` cannot be updated — it is immutable after creation. See [Configuration](/core/configuration#tag).
`vocabulary` and `webhooks` are **fully replaced** by whatever you send. To remove webhooks entirely, pass `"webhooks": null`.
Returns `404` if the configuration is not found.
# Evaluator Analysis
Source: https://docs.heify.com/api-reference/evaluator/analytics
api-reference/openapi-evaluators.json POST /analytics-evaluator
Generate a performance dashboard for a specific evaluator across all its evaluations.
This endpoint counts against your monthly analytics quota. Each call is counted even if no data is returned. Returns `429` when the limit is reached. See [Rate Limits & Quotas](/platform/rate-limits).
Only **COMPLETED** transcriptions with a non-null evaluation score are included. IN\_PROGRESS and FAILED transcriptions are excluded.
Results are capped at **20,000 transcriptions**. If `data.metadata.query_limit_reached` is `true`, the report is based on a partial dataset.
Use `data.charts.heatmap` to identify which days and hours show the lowest performance — useful for targeting training or adjusting scheduling.
`data.charts.heatmap` is built from each transcription's `created_at` hour — recordings submitted with a **date-only** [`period_date`](/api-reference/transcription/request-upload-url#recording-date-period_date) all land at hour `"0"` (midnight UTC). Send a time with `period_date` if you want an accurate hour axis.
`data.comparison.delta` may be an empty object `{}` when either the current or previous period has no calls.
The optional `start_date` / `end_date` filters (`YYYY-MM-DD`, both inclusive, each independent) scope the **entire response** to a date window — every metric is computed over the filtered set only. They match on each transcription's `created_at`, which is exactly what `period_date` sets at upload time, so a call uploaded in June but dated April is correctly returned by a Q2 filter. `data.metadata.filtered` tells you whether a filter was applied — distinguishing "no data" from "the filter excluded everything".
**`fail_rate` changed meaning.** In `data.criteria_breakdown`, `fail_rate` (and the new `pass_rate`) are now computed over `evaluated_count` — the calls where the criterion was actually scored — instead of total calls, so they are guaranteed within 0–100 (remove any client-side clamp). The array now lists **every** criterion, including never-failed ones, sorted worst-first with no-data entries last. When `evaluated_count` is `0`, the rates and `avg_score` are `null` — that means **no data, not a perfect score**.
Per-criterion scores are only materialized for transcriptions evaluated **after this feature's release** — there is no backfill. Older calls still count in `kpis`, `charts`, `comparison`, and `raw_data`, but contribute nothing to `criteria_breakdown` or `criteria_evolution` — so `total_calls: 25` alongside a criterion with `evaluated_count: 5` is correct, not a bug.
`data.criteria_evolution` is an **object keyed by criterion ID**, not an array — unlike every other collection in the response. Interpret `avg_score` via each criterion's `type`: `0.0`–`1.0` compliance rate for `boolean`/`strict`, `1.0`–`5.0` for `scale` — the two scales must not share a chart axis.
`data.criteria_breakdown` only includes criteria that still exist on the evaluator. Deleted criteria are excluded.
# Create Evaluator
Source: https://docs.heify.com/api-reference/evaluator/create
api-reference/openapi-evaluators.json POST /create-evaluator
Create a new evaluator with its evaluation criteria.
The sum of all non-`strict` criterion weights must equal **exactly 100**. The weight of `strict` criteria is automatically set to `0` regardless of what you send.
The `description` of each criterion is the text the AI uses to evaluate the call. A clear, specific description directly improves evaluation accuracy. For guidance on writing effective criteria, see [Evaluator — criteria](/core/evaluator#criteria).
`language` defaults to `"df"` (auto-detect from audio). See [Evaluator — language](/core/evaluator#language) for supported language codes.
Maximum **20 evaluators** per account and **10 criteria** per evaluator. See [Rate Limits & Quotas](/platform/rate-limits).
# Delete Evaluator
Source: https://docs.heify.com/api-reference/evaluator/delete
api-reference/openapi-evaluators.json POST /delete-evaluator
Permanently delete an evaluator and all its criteria.
This action is permanent and cannot be undone.
Returns `404` if the evaluator is not found.
# Get Evaluator
Source: https://docs.heify.com/api-reference/evaluator/get
api-reference/openapi-evaluators.json POST /get-evaluator
Retrieve a single evaluator by ID.
Returns `404` if the evaluator is not found.
For the full Evaluator data model and criteria types, see [Evaluator](/core/evaluator).
# List Evaluators
Source: https://docs.heify.com/api-reference/evaluator/list
api-reference/openapi-evaluators.json POST /list-evaluators
Returns all evaluators for the authenticated account, sorted by creation date (newest first).
Results are not paginated. `data.count` reflects the total number returned (max 20 per account).
# Update Evaluator
Source: https://docs.heify.com/api-reference/evaluator/update
api-reference/openapi-evaluators.json POST /update-evaluator
Update an evaluator's description, language, context, or criteria.
**Criteria not included in the request will be permanently deleted.** Always send the full list of criteria you want to keep, not just the changes. See [Evaluator — criteria](/core/evaluator#criteria).
`tag` cannot be changed after creation.
To update an existing criterion, include its `id`. To add a new one, omit `id`. Providing an `id` that does not belong to this evaluator returns `400`.
Non-`strict` criteria weights must still sum to exactly `100` after the update. See [Evaluator — weights](/core/evaluator#weights).
Returns `404` if the evaluator is not found.
# Participant Analysis
Source: https://docs.heify.com/api-reference/participant/analytics
api-reference/openapi-participants.json POST /analytics-participant
Generate a performance dashboard for an individual participant across all their evaluations.
This endpoint counts against your monthly analytics quota. Each request is counted even if no data is found. Returns `429` when the limit is reached.
Only **COMPLETED** transcriptions with a non-null evaluation score are included. IN\_PROGRESS and FAILED transcriptions are excluded.
Results are capped at **20,000 transcriptions**. If `data.metadata.query_limit_reached` is `true`, the dashboard is based on a partial dataset.
`data.comparison.delta` may be an empty object `{}` when there is no previous-period data to compare against (e.g. the participant had no calls last month).
The optional `start_date` / `end_date` filters (`YYYY-MM-DD`, both inclusive, each independent) scope the **entire response** to a date window — every metric is computed over the filtered set only. They match on each transcription's `created_at`, which is exactly what [`period_date`](/api-reference/transcription/request-upload-url#recording-date-period_date) sets at upload time, so a call uploaded in June but dated April is correctly returned by a Q2 filter. `data.metadata.filtered` tells you whether a filter was applied — distinguishing "no data" from "the filter excluded everything".
**`fail_rate` changed meaning.** In `data.criteria_breakdown`, `fail_rate` (and the new `pass_rate`) are now computed over `evaluated_count` — the calls where the criterion was actually scored — instead of total calls, so they are guaranteed within 0–100 (remove any client-side clamp). The array now lists **every** criterion, including never-failed ones, sorted worst-first with no-data entries last. When `evaluated_count` is `0`, the rates and `avg_score` are `null` — that means **no data, not a perfect score**.
Per-criterion scores are only materialized for transcriptions evaluated **after this feature's release** — there is no backfill. Older calls still count in `kpis`, `charts`, `comparison`, and `raw_data`, but contribute nothing to `criteria_breakdown` or `criteria_evolution` — so `total_calls: 25` alongside a criterion with `evaluated_count: 5` is correct, not a bug.
`data.criteria_evolution` is an **object keyed by criterion ID**, not an array — unlike every other collection in the response. Interpret `avg_score` via each criterion's `type`: `0.0`–`1.0` compliance rate for `boolean`/`strict`, `1.0`–`5.0` for `scale` — the two scales must not share a chart axis.
Criterion **names repeat across evaluators** — two evaluators can both define a "Manejo de Objeciones" with different IDs. Use the `evaluator_id` / `evaluator_tag` fields on `criteria_breakdown` and `criteria_evolution` entries to attribute and label them.
# Create Participant
Source: https://docs.heify.com/api-reference/participant/create
api-reference/openapi-participants.json POST /create-participant
Create a new participant profile to track a person across transcriptions — a support agent, job candidate, salesperson, or any individual.
For the full Participant data model, see [Participant](/core/participant).
`tag` is trimmed of leading and trailing whitespace automatically.
`metadata` values are always stored as strings, regardless of the input type. See [Participant — metadata](/core/participant#metadata).
Each account is limited to **200 participants**. Returns `400` if the limit is reached.
# Delete Participant
Source: https://docs.heify.com/api-reference/participant/delete
api-reference/openapi-participants.json POST /delete-participant
Permanently delete a participant.
Deletion is **permanent** and cannot be undone. Transcriptions previously linked to this participant will retain the `participant_id` value, but the participant record will no longer be resolvable.
Returns `404` if the participant is not found.
# List Participants
Source: https://docs.heify.com/api-reference/participant/list
api-reference/openapi-participants.json POST /list-participants
Returns all participant profiles for the authenticated account.
For the full Participant data model, see [Participant](/core/participant).
# Update Participant
Source: https://docs.heify.com/api-reference/participant/update
api-reference/openapi-participants.json POST /update-participant
Update a participant's tag and/or metadata.
`metadata` uses **full replacement** semantics — the entire object is replaced by what you send. Any keys not included in the request will be lost. See [Participant — metadata](/core/participant#metadata).
`tag` and `metadata` are both optional. If neither is provided, the request still succeeds and only `updated_at` is refreshed.
Returns `404` if the participant is not found.
# Delete Transcription
Source: https://docs.heify.com/api-reference/transcription/delete
api-reference/openapi-transcriptions.json POST /delete-transcription
Permanently delete a transcription record. This action is irreversible.
This action is **irreversible**. Once deleted, the transcription and all its processed data cannot be recovered.
Returns `404` if the transcription is not found.
# Get Transcription
Source: https://docs.heify.com/api-reference/transcription/details
api-reference/openapi-transcriptions.json POST /get-transcription
Retrieve the full details of a single transcription, including conversation, summary, extracted fields, and evaluation breakdown.
For the full Transcription data model, see [Transcription](/core/transcription).
The response structure varies by `status`. See [Transcription status](/core/transcription#status).
`summary`, `fields`, and `conversation` are `null` if those features were not enabled in the [Configuration](/core/configuration) used for this transcription.
Returns `404` if the transcription is not found.
For the evaluation `breakdown` structure and criterion types (`boolean`, `scale`, `strict`), see [Evaluator](/core/evaluator).
# List Transcriptions
Source: https://docs.heify.com/api-reference/transcription/list
api-reference/openapi-transcriptions.json POST /list-transcriptions
Returns all transcriptions for the authenticated account with lightweight metadata and QA summary. No request body required.
This endpoint returns **lightweight metadata only** — conversation, summary, extracted fields, and evaluation breakdown are not included. Use [Get Transcription](/api-reference/transcription/details) to retrieve full details for a specific transcription.
Results are capped at **20,000 transcriptions**. If `query_limit_reached` is `true`, not all transcriptions are shown.
# Submit from local file
Source: https://docs.heify.com/api-reference/transcription/request-upload-url
api-reference/openapi-transcriptions.json POST /request-upload-url
Upload a local audio or video file for transcription using a two-step process.
Use this endpoint when your audio file is stored locally. Call this API first to receive a pre-signed upload URL, then upload your file directly to it. Transcription processing starts automatically once the upload completes.
## Step 1 — Request upload URL
```bash cURL theme={null}
curl -X POST https://api.heify.com/request-upload-url \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"configuration_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"evaluator_id": "2abb5563-dd64-47bb-bb17-94252e168b06",
"name": "sales-call-2026-03-21.mp3",
"period_date": "2026-03-21T14:30:00Z"
}'
```
```python Python theme={null}
import requests
response = requests.post(
"https://api.heify.com/request-upload-url",
headers={"x-api-key": "YOUR_API_KEY"},
json={
"configuration_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"evaluator_id": "2abb5563-dd64-47bb-bb17-94252e168b06",
"name": "sales-call-2026-03-21.mp3",
"period_date": "2026-03-21T14:30:00Z"
}
)
data = response.json()["data"]
upload_url = data["upload_url"]
transcription_id = data["transcription_id"]
```
The `upload_url` expires in **5 minutes**. Proceed to Step 2 immediately after receiving it.
`participant_id` and `evaluator_id` are optional. If provided, they must exist and belong to your account.
The `name` field is sanitized to ASCII — accented or non-Latin characters are automatically stripped.
`period_date` is optional — it sets **when the recording was made** and overwrites the transcription's `created_at`. If omitted, `created_at` is the upload timestamp. See [Recording date](#recording-date-period_date) below for accepted formats.
## Step 2 — Upload the file
Send a `PUT` request to the `upload_url` from the Step 1 response. You must include the metadata fields as headers — they are embedded in the upload URL signature and the request will fail if they are missing or don't match.
```bash cURL theme={null}
curl -X PUT \
-H "Content-Type: audio/mpeg" \
-H "x-amz-meta-configuration_id: CONFIGURATION_ID_FROM_STEP_1" \
-H "x-amz-meta-evaluator_id: EVALUATOR_ID_FROM_STEP_1" \
-H "x-amz-meta-period_date: PERIOD_DATE_FROM_STEP_1" \
--data-binary @recording.mp3 \
"UPLOAD_URL_FROM_STEP_1"
```
```python Python theme={null}
data = response.json()["data"] # Step 1 response
# Build metadata headers from Step 1 response
meta_headers = {
"Content-Type": "audio/mpeg",
"x-amz-meta-configuration_id": data["configuration_id"],
}
if data.get("participant_id"):
meta_headers["x-amz-meta-participant_id"] = data["participant_id"]
if data.get("evaluator_id"):
meta_headers["x-amz-meta-evaluator_id"] = data["evaluator_id"]
if data.get("period_date"):
meta_headers["x-amz-meta-period_date"] = data["period_date"]
if data.get("name"):
meta_headers["x-amz-meta-name"] = data["name"]
with open("recording.mp3", "rb") as f:
upload_response = requests.put(data["upload_url"], data=f, headers=meta_headers)
if upload_response.status_code == 200:
print(f"Upload successful. Transcription ID: {data['transcription_id']}")
```
A `200 OK` with an empty body confirms the upload succeeded. Processing starts automatically in the background.
If you sent `period_date` in Step 1, replay the **normalized value echoed in the Step 1 response** (`data.period_date`) as the `x-amz-meta-period_date` header — not your original input. Like the other metadata headers, it is embedded in the upload URL signature.
Use the `transcription_id` from Step 1 to [check results](/api-reference/transcription/details) once processing completes, or configure a [webhook](/core/configuration#webhooks) to receive a notification automatically.
For supported formats, file size limits, and duration limits, see [Rate Limits & Quotas](/platform/rate-limits).
## Recording date (`period_date`)
Customers often upload calls in **batches, after the fact** — a batch recorded in April might be uploaded in June. Without `period_date`, every transcription is stamped with the *upload* time, so all of April's calls would land in June and monthly or quarterly reporting would be impossible.
`period_date` states when the recording actually happened. Because it overwrites `created_at`, setting it correctly at upload time automatically fixes every time-based analytic at once: the timeline, the month-over-month comparison, [per-criterion evolution](/api-reference/evaluator/analytics), the evaluator heatmap, and the `start_date`/`end_date` analytics filters — all are derived from `created_at`.
It behaves identically here and on [Submit from public URL](/api-reference/transcription/submit): same format, same validation, same errors.
### Accepted formats
The date is required; the time is optional. Everything is resolved to a **UTC instant** and stored in `created_at`.
| Input | Resolves to |
| :---------------------------- | :----------------------------------------------------------------- |
| `"2026-04-15"` | `2026-04-15T00:00:00Z` — date only lands at midnight UTC |
| `"2026-04-15T14:30"` | `2026-04-15T14:30:00Z` — seconds optional |
| `"2026-04-15T14:30:00"` | `2026-04-15T14:30:00Z` — **no timezone is read as UTC** |
| `"2026-04-15T14:30:00.000Z"` | `2026-04-15T14:30:00Z` — fractional seconds accepted and truncated |
| `"2026-04-15T14:30:00+02:00"` | `2026-04-15T12:30:00Z` — offset converted to UTC |
Two defaults callers tend to assume wrongly: **no time means `00:00:00` UTC** (not "some time that day"), and **no timezone means UTC** (not your local time). A client that means local time must send the offset — and note an offset can move the instant to the previous day, and therefore into the previous reporting month (`2026-04-01T00:30:00+02:00` resolves to March 31st).
Omitted, `null`, or empty-string values are treated as "not provided" — no error, `created_at` falls back to the upload timestamp. Anything else malformed returns `400`: month-only values (`"2026-04"`), a space instead of the `T` separator, non-ISO formats (`"15/04/2026"`), impossible dates, or non-string types.
**If you know the recording's time of day, send it.** The evaluator dashboard's `charts.heatmap` is a weekday × hour grid built from `created_at` — date-only uploads all pile into the midnight column, flattening the hour axis. Everything else (month, timeline, evolution, comparison, date filters, weekday) is correct either way.
There is **no endpoint to correct the date afterwards** — [Update Transcription Group](/api-reference/transcription/update-group) does not accept `period_date`. If a recording was submitted with the wrong date, delete it and submit the audio again with the correct `period_date`.
# Submit from public URL
Source: https://docs.heify.com/api-reference/transcription/submit
api-reference/openapi-transcriptions.json POST /submit
Submit an audio or video file for transcription using a publicly accessible URL.
Use this endpoint when your media file is already hosted at a public URL. For local files, use [Submit from local file](/api-reference/transcription/request-upload-url) instead.
Processing is **asynchronous** — the API returns a `transcription_id` immediately. Use [Get Transcription Details](/api-reference/transcription/details) or webhooks to track completion.
The `url` must be publicly accessible with no authentication required. The download has a **60-second timeout** — use a fast, stable URL.
For supported formats, file size limits, and duration limits, see [Rate Limits & Quotas](/platform/rate-limits).
`participant_id` and `evaluator_id` are optional. If provided, they must exist and belong to your account.
The optional `period_date` field sets **when the recording was made** — a date (`YYYY-MM-DD`) or an ISO 8601 datetime, time optional — and overwrites the transcription's `created_at`, which all time-based analytics are derived from. It behaves identically to the same field on [Submit from local file](/api-reference/transcription/request-upload-url#recording-date-period_date) — see the accepted formats and gotchas there. There is no way to correct the date afterwards: delete and resubmit with the right `period_date`.
`period_date` is validated **before** the audio is downloaded from `url` — a malformed date fails fast with a `400` and does not consume the 60-second download.
# Update Transcription Group
Source: https://docs.heify.com/api-reference/transcription/update-group
api-reference/openapi-transcriptions.json POST /update-transcription-group
Assign or clear the review workflow group of a transcription.
`group` is the **only updatable field** on a transcription. All other fields are set during processing and are immutable.
For valid group values and their meaning in the review workflow, see [Transcription — group](/core/transcription#group).
Pass `""` (empty string) as `group` to clear it (sets the field to `null`).
Returns `404` if the transcription is not found.
The response includes the **full updated transcription** — same structure as [Get Transcription](/api-reference/transcription/details).
# Authentication
Source: https://docs.heify.com/authentication
Learn how to authenticate your API requests with Heify
# Authentication
All requests to the Heify API must be authenticated using an **API Key**. This key identifies your account and ensures secure access to your transcription resources.
## Obtaining Your API Key
You can generate and manage your API Keys from the [Sandbox](/sandbox/api-keys) or your Heify Dashboard.
Navigate to the Sandbox and authenticate with your credentials
Access the [API Keys & Sandbox](/sandbox/api-keys) page
Click "Create New API Key" and give it a descriptive name
Copy the key immediately - it will only be shown once
**Keep your API key secure!** Never share it publicly or commit it to version control. Treat it like a password.
## Using Your API Key
Include your API key in the `x-api-key` header with every API request:
### Header Format
| Header | Value |
| :------------- | :----------------- |
| `x-api-key` | `YOUR_API_KEY` |
| `Content-Type` | `application/json` |
## Example Requests
```bash cURL theme={null}
curl -X POST https://api.heify.com/list-configurations \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{}'
```
```python Python theme={null}
import requests
url = "https://api.heify.com/list-configurations"
headers = {
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY"
}
response = requests.post(url, headers=headers, json={})
print(response.json())
```
## Best Practices
Store your API key in environment variables, not in your source code.
```bash theme={null}
export HEIFY_API_KEY="your_actual_key_here"
```
Then reference it in your code:
```python theme={null}
import os
api_key = os.environ.get('HEIFY_API_KEY')
```
Use different API keys for development, staging, and production environments. This allows you to rotate keys without affecting all environments.
For enhanced security, periodically generate new API keys and deactivate old ones from your dashboard.
## Authentication Errors
If authentication fails, you'll receive a `403 Forbidden` response.
Common causes of authentication errors:
* **Missing header**: The `x-api-key` header was not included in the request
* **Invalid key**: The API key is incorrect or has been deactivated
* **Expired key**: The API key has been deleted from your account
If you receive authentication errors, verify that:
1. The API key is correct and active in your dashboard
2. The `x-api-key` header is properly formatted
3. There are no extra spaces or special characters in the key
# Configuration
Source: https://docs.heify.com/core/configuration
Reference for the Configuration object — the reusable template that drives every transcription.
A **Configuration** is a reusable template that defines how audio/video files are processed. It sets the language, vocabulary, extraction fields, and optional features like summaries or webhooks. Every transcription requires a configuration.
## Fields
| Field | Type | Required | Description |
| :------------------- | :-------------- | :------------- | :---------------------------------------------------------------------------------------------- |
| `configuration_id` | `string` | Auto-generated | Unique identifier (UUID) |
| `client_id` | `string` | Auto-assigned | Your account identifier |
| `tag` | `string` | **Yes** | Descriptive name (max 255 characters) |
| `vocabulary` | `array` | No | Custom terms to improve recognition accuracy — fully replaced when updated |
| `extraction_fields` | `array` | No | Structured data fields to extract via AI (max 10) — see [Extraction Fields](#extraction-fields) |
| `summary` | `boolean` | No | Generate an AI summary (default: `false`) |
| `custom_summary` | `string` | No | Custom prompt to guide the AI summary (max 300 chars) |
| `summary_language` | `string` | No | Language for the summary (`"df"` = auto-detect) |
| `analytics_language` | `string` | No | Language for analytics reports (`"df"` = auto-detect) |
| `webhooks` | `object` | No | URLs to notify on completion or failure — see [Webhooks](#webhooks) |
| `created_at` | `string` | Auto-generated | ISO 8601 creation timestamp |
`tag` cannot be changed after the configuration is created.
## Extraction Fields
Each item in `extraction_fields` tells the AI what structured data to extract from the transcript.
| Field | Type | Required | Description |
| :------------ | :------- | :------- | :---------------------------------------------------------------------------- |
| `name` | `string` | **Yes** | Field identifier (e.g. `"sentiment"`, `"ticket_id"`) — 1–50 chars |
| `type` | `string` | **Yes** | Data type: `string`, `number`, `boolean`, `array` |
| `description` | `string` | **Yes** | Instructions for the AI — the more specific, the more accurate — 1–1000 chars |
Field names are normalized on creation: converted to lowercase and special characters replaced with `_`. For example, `"My Field!"` becomes `"my_field_"`.
Once a field is created, its `type` is immutable. Only `description` can be updated.
When updating `extraction_fields`, the submitted list uses a **merge-by-name** strategy: any field **not included** in the update is **permanently removed**. Always include all the fields you want to keep, even if you're only changing one of them.
## Best Practices for Extraction Fields
Define a clear, limited set of possible values to improve consistency and accuracy.
```json theme={null}
{
"name": "sentiment",
"type": "string",
"description": "Classify the overall sentiment of the conversation. Must be one of: POSITIVE, NEGATIVE, or NEUTRAL."
}
```
This ensures the AI returns predictable, standardized values instead of open-ended descriptions.
Give detailed descriptions and concrete examples to guide the AI toward more accurate results.
**Poor description:**
```json theme={null}
{
"name": "classification",
"type": "string",
"description": "Classifies the conversation"
}
```
**Good description:**
```json theme={null}
{
"name": "issue",
"type": "string",
"description": "Classifies the conversation into one of: \"BILLING\", \"TECHNICAL\", \"GENERAL\". BILLING covers payment or invoice questions. TECHNICAL covers product bugs or setup issues. GENERAL covers all other topics."
}
```
The more context you provide, the better the extraction quality.
Provide clear, detailed descriptions for extraction fields. The more context you give, the more accurate the extraction will be.
## Webhooks
| Field | Type | Description |
| :------------ | :------- | :------------------------------------------------- |
| `success_url` | `string` | POST notification when the transcription completes |
| `error_url` | `string` | POST notification when the transcription fails |
When updating a configuration, pass `"webhooks": null` to remove all webhook URLs.
## Example
```json theme={null}
{
"configuration_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"client_id": "client-uuid",
"tag": "Sales Call Analysis",
"vocabulary": ["CRM", "upsell", "churn"],
"extraction_fields": [
{
"name": "sentiment",
"type": "string",
"description": "Overall sentiment of the conversation: POSITIVE, NEGATIVE, or NEUTRAL."
},
{
"name": "next_action",
"type": "string",
"description": "The agreed next action with the customer, if any."
}
],
"summary": true,
"custom_summary": "Focus on action items and next steps agreed by both parties.",
"summary_language": "en",
"analytics_language": "en",
"webhooks": {
"success_url": "https://example.com/webhooks/success",
"error_url": "https://example.com/webhooks/error"
},
"created_at": "2025-01-15T10:00:00.000000+00:00"
}
```
# Evaluator
Source: https://docs.heify.com/core/evaluator
Reference for the Evaluator object — the quality-scoring template applied to transcriptions.
An **Evaluator** is a quality-scoring template made up of criteria. When attached to a transcription, the AI evaluates the conversation against each criterion and produces a score. Evaluators are optional — use them when you need automated quality assurance.
## Fields
| Field | Type | Required | Description |
| :------------- | :-------------- | :------------- | :---------------------------------------------------------------------- |
| `evaluator_id` | `string` | Auto-generated | Unique identifier (UUID) |
| `tag` | `string` | **Yes** | Name of the evaluator (not updatable after creation) — max 100 chars |
| `description` | `string` | No | Description of the evaluator's purpose — max 250 chars |
| `language` | `string` | No | Language used for evaluation (`"df"` = auto-detect) |
| `context` | `string` | No | Additional context to guide the AI evaluation — max 1000 chars |
| `criteria` | `array` | **Yes** | List of evaluation criteria (min 1, max 10) — see [Criteria](#criteria) |
| `created_at` | `string` | Auto-generated | ISO 8601 creation timestamp |
## Criteria
Each criterion defines one aspect of the evaluation.
| Field | Type | Description |
| :------------ | :------- | :------------------------------------------------------------------------ |
| `id` | `string` | Criterion UUID (auto-generated) |
| `name` | `string` | Name of the criterion — 1–100 chars |
| `description` | `string` | Instructions for the AI to evaluate this criterion — 5–2000 chars |
| `type` | `string` | `boolean`, `scale`, or `strict` — see [Criterion Types](#criterion-types) |
| `weight` | `number` | Score contribution — see [Weight Rules](#weight-rules) |
When updating `criteria`, any criterion **not included** in the update is **permanently removed**. Provide the criterion `id` to update an existing criterion; omit `id` to add a new one.
### Criterion Types
| Type | AI input | Scoring | Fails when |
| :-------- | :--------- | :------------------------------------ | :--------------------------------------------------- |
| `boolean` | `0` or `1` | `weight` if pass, `0` if fail | result is `0` |
| `scale` | `1`–`5` | `(value / 5) × weight` (proportional) | result `< 3` |
| `strict` | `0` or `1` | Always `0` — does not affect score | result is `0` → sets `critical_fail_triggered: true` |
### Weight Rules
The sum of `weight` across all **non-`strict`** criteria must equal exactly **100**. Non-`strict` criteria must have `weight > 0`. `strict` criteria must have `weight: 0` — if you pass a non-zero weight for a `strict` criterion, it is automatically overridden to `0`. The default type is `boolean`.
## Example
```json theme={null}
{
"evaluator_id": "2abb5563-dd64-47bb-bb17-94252e168b06",
"tag": "Customer Service Standard",
"description": "Evaluates call quality for the support team.",
"language": "en",
"context": "Inbound customer support calls for a SaaS product.",
"criteria": [
{
"id": "c1d2e3f4-...",
"name": "Greeting",
"description": "Did the agent greet the customer with the standard opening phrase?",
"type": "boolean",
"weight": 34
},
{
"id": "d2e3f4g5-...",
"name": "Active Listening",
"description": "Did the agent listen without interrupting, confirm key details, and ask relevant questions before offering a solution?",
"type": "scale",
"weight": 33
},
{
"id": "e3f4g5h6-...",
"name": "Issue Resolved",
"description": "Was the customer's issue fully resolved by the end of the call?",
"type": "scale",
"weight": 33
},
{
"id": "f4g5h6i7-...",
"name": "No offensive language",
"description": "Did the agent use any rude or inappropriate language?",
"type": "strict",
"weight": 0
}
],
"created_at": "2025-01-15T10:00:00.000000"
}
```
# Participant
Source: https://docs.heify.com/core/participant
Reference for the Participant object — the person tracked across transcriptions: a support agent, job candidate, salesperson, or any individual.
A **Participant** represents any individual whose performance or results are tracked across transcriptions — a support agent, salesperson, job candidate being interviewed, or any other person. Participants are optional — use them when you need per-person analytics.
## Fields
| Field | Type | Required | Description |
| :--------------- | :------- | :------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| `participant_id` | `string` | Auto-generated | Unique identifier (UUID) |
| `tag` | `string` | **Yes** | Name or identifier of the participant (e.g. agent name, candidate name, employee ID) — 1–100 chars, whitespace auto-trimmed |
| `metadata` | `object` | No | Free-form key-value pairs for any additional data — max 10 keys, key ≤50 chars, value ≤250 chars |
| `created_at` | `string` | Auto-generated | ISO 8601 creation timestamp |
| `updated_at` | `string` | Auto-updated | ISO 8601 last-modified timestamp |
All metadata values are stored as **strings** regardless of the type provided (e.g. `42` → `"42"`, `true` → `"True"`).
When updating `metadata`, the new object **fully replaces** the previous one (PUT semantics). To update a single key without losing the rest, fetch the participant first, modify the object locally, and send the complete updated `metadata`.
## Example
```json theme={null}
{
"participant_id": "c3d4e5f6-a7b8-9012-3456-789012cdef01",
"tag": "Sarah Johnson",
"metadata": {
"email": "sarah.johnson@example.com",
"role": "Senior Support Agent",
"department": "Customer Success",
"employee_id": "EMP-0042"
},
"created_at": "2025-01-15T10:00:00.000000+00:00",
"updated_at": "2025-01-15T10:00:00.000000+00:00"
}
```
# Transcription
Source: https://docs.heify.com/core/transcription
Reference for the Transcription object — the result of a single audio or video processing job.
A **Transcription** represents a single audio or video processing job. Its fields evolve as the job moves through its lifecycle.
## Fields
| Field | Type | Description |
| :------------------ | :------- | :----------------------------------------------------------------------------------------------- |
| `transcription_id` | `string` | Unique identifier (UUID) |
| `status` | `string` | Current status: `IN_PROGRESS`, `COMPLETED`, `FAILED` — see [Status Lifecycle](#status-lifecycle) |
| `configuration_id` | `string` | ID of the configuration used |
| `configuration_tag` | `string` | Tag of the configuration used |
| `evaluator_id` | `string` | ID of the evaluator used (`null` if none) |
| `evaluator_tag` | `string` | Tag of the evaluator used (`null` if none) |
| `participant_id` | `string` | ID of the participant associated (`null` if none) |
| `participant_tag` | `string` | Tag of the participant associated (`null` if none) |
| `name` | `string` | Custom name (`null` if not set) — normalized to ASCII on creation |
| `group` | `string` | Review group (`null` if not set) — see [Groups](#groups) |
| `duration` | `number` | Duration in seconds |
| `details` | `object` | Full results — see [Details Object](#details-object) |
## Status Lifecycle
The audio is being transcribed and analyzed.
Processing finished. The `details` object contains all results.
Processing failed. The `details` object contains `message`, `code`, and `failed_at`.
Transcriptions are retained for **one year from submission**, then permanently deleted — including all derived content. The retention clock always runs from the real submission time, even when `period_date` backdates the recording. See [Data Retention](/platform/data-retention).
## Groups
Use `group` to manage the review workflow for each transcription.
| Value | Description |
| :--------------- | :----------------------- |
| `PENDING_REVIEW` | Needs manual review |
| `UNDER_REVIEW` | Currently being reviewed |
| `ARCHIVED` | Completed and archived |
| `null` | No group assigned |
New transcriptions start with `group = null`. The typical workflow progression is: `null` → `PENDING_REVIEW` → `UNDER_REVIEW` → `ARCHIVED`.
To remove a group, pass `""` (empty string) as the `group` value when calling `/update-transcription-group`.
## Details Object
The `details` object is always present but its structure depends on `status`.
### When COMPLETED
| Field | Type | Description |
| :------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `language` | `string` | Detected language code (see [Supported Languages](#supported-languages)) |
| `num_speakers` | `number` | Number of unique speakers identified |
| `created_at` | `string` | ISO 8601 — when the transcription was created. Defaults to the submission time, but can be set explicitly at submission via the `period_date` field of [Submit from local file](/api-reference/transcription/request-upload-url#recording-date-period_date) or [Submit from public URL](/api-reference/transcription/submit) |
| `completed_at` | `string` | ISO 8601 — when processing finished |
| `summary` | `object` | AI-generated summary (`null` if disabled in the configuration) — see [Summary](#summary) |
| `conversation` | `object` | Full transcript split into speaker segments — see [Conversation](#conversation) |
| `fields` | `object` | Extracted structured data (`null` if no extraction fields configured) — see [Fields (Extraction)](#fields-extraction) |
| `evaluation` | `object` | Quality evaluation result (`null` if no evaluator was used) — see [Evaluation](#evaluation) |
`created_at` is the backbone of everything time-based: the analytics timeline, the month-over-month comparison, per-criterion evolution, the evaluator heatmap, and the `start_date`/`end_date` analytics filters are all derived from it. Setting `period_date` correctly at submission time is what makes reporting accurate for recordings uploaded after the fact — and it cannot be changed later (to fix a wrong date, delete the transcription and resubmit the audio).
### When FAILED
| Field | Type | Description |
| :---------- | :------- | :----------------------------------- |
| `message` | `string` | Human-readable error description |
| `code` | `number` | Internal error code |
| `failed_at` | `string` | ISO 8601 — when the failure occurred |
### Conversation
`details.conversation.segments` is an array of speaker-separated transcript segments.
| Field | Type | Description |
| :-------- | :------- | :---------------------------------------------- |
| `text` | `string` | Transcribed text for this segment |
| `speaker` | `string` | Speaker label (e.g. `SPEAKER_00`, `SPEAKER_01`) |
### Summary
`details.summary` is an object with the following field.
| Field | Type | Description |
| :-------- | :------- | :------------------------ |
| `summary` | `string` | AI-generated summary text |
### Fields (Extraction)
`details.fields.fields` is an array of extracted values corresponding to the configuration's `extraction_fields`.
| Field | Type | Description |
| :------ | :------- | :-------------------------------------------------------------------------------------------------- |
| `name` | `string` | Field name matching the configured extraction field |
| `value` | `any` | Extracted value — type matches the field's configured type (`string`, `number`, `boolean`, `array`) |
### Evaluation
`details.evaluation` is present when an evaluator was used.
| Field | Type | Description |
| :-------------- | :-------- | :------------------------------------------------------------ |
| `score` | `number` | Overall quality score (0–100) |
| `critical_fail` | `boolean` | Whether a `strict` criterion was triggered |
| `status` | `string` | `PENDING`, `COMPLETED`, or `FAILED` |
| `breakdown` | `object` | Detailed per-criterion analysis — see [Breakdown](#breakdown) |
#### Breakdown
| Field | Type | Description |
| :------------------------ | :-------- | :------------------------------------------- |
| `evaluator_id` | `string` | ID of the evaluator used |
| `evaluator_tag` | `string` | Tag of the evaluator used |
| `generated_at` | `string` | ISO 8601 — when the evaluation was generated |
| `language_used` | `string` | Language used for evaluation |
| `final_score` | `number` | Final score (same as `score`) |
| `critical_fail_triggered` | `boolean` | Whether a `strict` criterion was triggered |
| `summary_feedback` | `string` | Overall narrative feedback (markdown) |
| `results` | `array` | Per-criterion results — see below |
#### Criterion Results (`breakdown.results`)
| Field | Type | Description |
| :--------------- | :------- | :-------------------------------------------------------------- |
| `criteria_id` | `string` | ID of the criterion |
| `name` | `string` | Name of the criterion |
| `type` | `string` | Criterion type: `boolean`, `scale`, or `strict` |
| `max_weight` | `number` | Maximum score this criterion can contribute |
| `score_raw_ai` | `number` | Raw AI score (0 or 1 for `boolean`; 1–5 for `scale`) |
| `score_obtained` | `number` | Actual score contribution to the total |
| `status` | `string` | `PASS` or `FAIL` |
| `reasoning` | `string` | AI's reasoning for this result |
| `quote` | `string` | Relevant excerpt from the transcript (`null` if not applicable) |
| `feedback` | `string` | Actionable improvement feedback (`null` if not applicable) |
## Example
```json theme={null}
{
"transcription_id": "926f3be9-143e-4df6-8b10-64e4d781e203",
"status": "COMPLETED",
"configuration_id": "3b8006ad-b1a2-464b-9a6b-79dcd45c9433",
"configuration_tag": "Pitch Analysis DEMO",
"name": null,
"group": null,
"duration": 609.2,
"participant_id": "580cc0e1-3a60-49d9-8e20-1745252f0aae",
"evaluator_id": "2abb5563-dd64-47bb-bb17-94252e168b06",
"participant_tag": "Sarah Johnson",
"evaluator_tag": "Customer Service Standard",
"details": {
"language": "en",
"num_speakers": 2,
"created_at": "2025-01-15T10:00:00.123Z",
"completed_at": "2025-01-15T10:03:45.789Z",
"summary": {
"summary": "A support call where the customer reported a billing issue that was resolved by the agent."
},
"conversation": {
"segments": [
{ "text": "Good morning, this is Sarah from support.", "speaker": "SPEAKER_00" },
{ "text": "Hi, I have an issue with my subscription.", "speaker": "SPEAKER_01" }
]
},
"fields": {
"fields": [
{ "name": "sentiment", "value": "POSITIVE" },
{ "name": "next_action", "value": "Send confirmation email" }
]
},
"evaluation": {
"score": 90,
"critical_fail": false,
"status": "COMPLETED",
"breakdown": {
"evaluator_id": "2abb5563-dd64-47bb-bb17-94252e168b06",
"evaluator_tag": "Customer Service Standard",
"generated_at": "2025-01-15T10:03:44.000Z",
"language_used": "en",
"final_score": 90.0,
"critical_fail_triggered": false,
"summary_feedback": "Overall strong performance. The agent greeted the customer correctly and resolved the issue.",
"results": [
{
"criteria_id": "c1d2e3f4-...",
"name": "Greeting",
"type": "boolean",
"max_weight": 20.0,
"score_raw_ai": 1,
"score_obtained": 20.0,
"status": "PASS",
"reasoning": "The agent opened with the standard greeting phrase.",
"quote": "SPEAKER_00: Good morning, this is Sarah from support.",
"feedback": null
},
{
"criteria_id": "d2e3f4g5-...",
"name": "No offensive language",
"type": "strict",
"max_weight": 0.0,
"score_raw_ai": 1,
"score_obtained": 0.0,
"status": "PASS",
"reasoning": "No inappropriate language was detected throughout the call.",
"quote": null,
"feedback": null
}
]
}
}
}
}
```
***
## Supported Languages
The following languages are supported for **transcriptions**, **summaries** (`summary_language`), and **analytics reports** (`analytics_language`).
Use `"df"` for automatic language detection. For `summary_language`, the summary is generated in the detected language of each individual file. For `analytics_language`, the report uses the majority language across all files in the configuration.
| Language | ISO Code |
| :---------- | :------- |
| Afrikaans | `af` |
| Albanian | `sq` |
| Arabic | `ar` |
| Azerbaijani | `az` |
| Basque | `eu` |
| Belarusian | `be` |
| Bengali | `bn` |
| Bosnian | `bs` |
| Bulgarian | `bg` |
| Catalan | `ca` |
| Chinese | `zh` |
| Croatian | `hr` |
| Czech | `cs` |
| Danish | `da` |
| Dutch | `nl` |
| English | `en` |
| Estonian | `et` |
| Finnish | `fi` |
| French | `fr` |
| Galician | `gl` |
| German | `de` |
| Greek | `el` |
| Gujarati | `gu` |
| Language | ISO Code |
| :--------- | :------- |
| Hebrew | `he` |
| Hindi | `hi` |
| Hungarian | `hu` |
| Indonesian | `id` |
| Italian | `it` |
| Japanese | `ja` |
| Kannada | `kn` |
| Kazakh | `kk` |
| Korean | `ko` |
| Latvian | `lv` |
| Lithuanian | `lt` |
| Macedonian | `mk` |
| Malay | `ms` |
| Malayalam | `ml` |
| Marathi | `mr` |
| Norwegian | `no` |
| Persian | `fa` |
| Polish | `pl` |
| Portuguese | `pt` |
| Punjabi | `pa` |
| Language | ISO Code |
| :--------- | :------- |
| Romanian | `ro` |
| Russian | `ru` |
| Serbian | `sr` |
| Slovak | `sk` |
| Slovenian | `sl` |
| Spanish | `es` |
| Swahili | `sw` |
| Swedish | `sv` |
| Tagalog | `tl` |
| Tamil | `ta` |
| Telugu | `te` |
| Thai | `th` |
| Turkish | `tr` |
| Ukrainian | `uk` |
| Urdu | `ur` |
| Vietnamese | `vi` |
| Welsh | `cy` |
# Introduction
Source: https://docs.heify.com/index
Heify is an AI-powered audio intelligence platform. Turn voice recordings into structured data, quality scores, and deep analytics — automatically.
Heify processes voice recordings — calls, meetings, interviews, training sessions — and converts them into structured data, quality metrics, and actionable insights. It's not just a transcription tool: it's a full conversation analysis system that listens, understands, extracts, evaluates, and reports — all configurable without code.
## The Heify Pipeline
Every recording flows through a modular pipeline. Each component is independent and optional — combine them as your use case requires.
The core template. Defines what to extract from audio: structured fields, AI summary, custom vocabulary, language, and webhooks. Required for every transcription.
Quality-scoring rubrics. Define weighted criteria — Heify scores every conversation against them automatically and flags critical failures.
Person profiles — support agents, job candidates, salespeople, or any individual. Attach a participant to track their performance across transcriptions over time.
The result. Full transcript with speaker labels, extracted fields, AI summary, evaluation score — all in one object.
Only a **Configuration** is required. Evaluators and Participants are optional layers you activate when you need quality scoring or per-person performance tracking.
***
## Two Ways to Use Heify
### Heify API
A programmatic interface for developers. Integrate transcription, extraction, quality scoring, and analytics directly into your applications and workflows.
**Key capabilities:**
* **Manage all modules** — create, update, and delete Configurations, Evaluators, and Participants programmatically
* **Submit audio via URL or direct upload** — public URL or presigned S3 upload
* **Extract structured fields** from conversations using AI — define any schema you need
* **Automatic AI summaries** with custom instructions per configuration
* **Quality scoring** — attach an Evaluator to score every call automatically
* **Per-person tracking** — attach a Participant (agent, candidate, salesperson) to build individual performance analytics
* **Webhooks** — get notified the moment a transcription completes or fails
* **Analytics endpoints** — AI-powered corpus analysis across configurations, evaluators, and participants
### Heify Sandbox
A browser-based interface for business teams, QA analysts, and anyone who wants to explore Heify without writing code.
**Key features:**
* **No-code configuration builder** with extraction field templates and quick-action shortcuts
* **Drag & drop audio upload** or paste a URL — results appear in minutes
* **Evaluator designer** — build quality rubrics with weighted criteria visually
* **Participant management** — register agents, candidates, or any person and track their performance over time
* **AI Analytics dashboards** — patterns, anomalies, correlations, and rankings across your entire call corpus
* **Workflow groups** — organize transcriptions into Pending Review, Under Review, or Archived
* **Team access** — admins can invite collaborators who access the same workspace via OTP
* **PDF export** — download full transcription reports and analytics dashboards
***
## API Overview
The Heify API is organized around these principles:
* **JSON-based**: All requests and responses use JSON format
* **HTTP methods**: All endpoints use `POST`
* **API Key authentication**: Secure access with API keys via the `x-api-key` header
* **Rate limiting**: Fair usage policies applied per endpoint to ensure service stability
**Base URL**: `https://api.heify.com`
***
## Explore the Docs
Your first transcription in minutes — Sandbox or API, your choice
API Keys, passwordless login, and session management
Create and manage reusable processing templates
Submit audio and retrieve full results
Build quality-scoring rubrics and audit conversations at scale
Track individual performance (agents, candidates, salespeople) across transcriptions and evaluators
# Data Retention
Source: https://docs.heify.com/platform/data-retention
How long Heify keeps your transcriptions, and what happens when they expire.
Heify retains every transcription for **one year (365 days) from the moment it is submitted**. After that, the transcription and all of its derived artifacts are **permanently deleted, automatically**.
## What gets deleted
When a transcription reaches the end of its retention period, the following are removed:
* The transcription record itself — it disappears from [List Transcriptions](/api-reference/transcription/list), [Get Transcription](/api-reference/transcription/details), and the Sandbox.
* All derived content: the conversation, the summary, extracted fields, and the evaluation details.
* Its contribution to analytics — expired transcriptions no longer appear in any dashboard, timeline, or per-criterion metric.
**Audio files are never retained at all.** The original audio is deleted from our storage immediately after processing completes, regardless of retention — this has always been the case and is independent of the one-year policy.
## How the clock works
* The retention period starts at **submission time** — the moment the audio is uploaded and processed.
* Setting a `period_date` (the recording's real date, see [Transcription](/core/transcription)) does **not** affect retention. A recording made eleven months ago and uploaded today is still kept for a full year from today.
* Deletion is automatic and happens shortly after the 365-day mark (typically within a day or two).
* The policy applies to **all** transcriptions, including `FAILED` ones.
## Deletion is permanent
Expired transcriptions **cannot be recovered**. If you need to keep transcription content beyond one year, export it before it expires — [Get Transcription](/api-reference/transcription/details) returns the full conversation, summary, extracted fields, and evaluation breakdown as JSON, ready to store on your side.
You can also delete transcriptions earlier at any time with [Delete Transcription](/api-reference/transcription/delete); retention is simply the upper bound on how long Heify keeps them.
# Error Codes
Source: https://docs.heify.com/platform/errors
HTTP status codes and error message reference for the Heify API.
## Error Response Format
All error responses follow this structure:
```json theme={null}
{
"error": {
"message": "Descriptive error message explaining what went wrong",
"code": 400
}
}
```
***
## HTTP Status Codes
| Code | Status | Description |
| :---- | :-------------------- | :----------------------------------- |
| `200` | OK | Request completed successfully |
| `201` | Created | New resource created successfully |
| `400` | Bad Request | Validation error or bad request data |
| `402` | Payment Required | Insufficient transcription minutes |
| `403` | Forbidden | Invalid or missing API key |
| `404` | Not Found | Resource or endpoint not found |
| `429` | Too Many Requests | Rate limit or monthly quota exceeded |
| `500` | Internal Server Error | Unexpected error on Heify's servers |
***
## 400 Bad Request
The request is malformed or contains invalid parameters.
**Error**: `"Missing required field: configuration_id"`
Ensure all required parameters are included in the request body.
```json theme={null}
{
"configuration_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef"
}
```
**Error**: `"Invalid JSON format"`
Validate your JSON before sending. Common issues:
* Missing commas between fields
* Trailing commas after the last field
* Unescaped quotes inside string values
**Error**: `"Maximum of 20 configurations per client exceeded"`
Delete unused resources before creating new ones. See [Rate Limits & Quotas](/platform/rate-limits) for all limits.
**Error**: `"Unsupported audio format"`
Ensure your file meets the requirements:
* **Supported formats**: AAC, AIFF, AMR, ASF, FLAC, MP3, OGG, WAV, WebM, M4A, MP4
* **Max size**: 200 MB
* **Max duration**: 2 hours (7200 seconds)
**Error**: `"Invalid group value"`
Use only the allowed group values when updating a transcription:
* `PENDING_REVIEW`
* `UNDER_REVIEW`
* `ARCHIVED`
* `""` (empty string to remove the group)
***
## 402 Payment Required
Your account has run out of transcription minutes.
Submit operations on transcriptions will be rejected until your balance is topped up. Contact [hola@heify.com](mailto:hola@heify.com) to add more minutes.
***
## 403 Forbidden
Authentication failed. Your API key is invalid, missing, or inactive.
| Cause | Solution |
| :------------------------- | :----------------------------------- |
| Missing `x-api-key` header | Add the header to every request |
| Invalid API key value | Verify the key in your dashboard |
| Deactivated API key | Generate a new key |
| Wrong header name | Use `x-api-key`, not `Authorization` |
***
## 404 Not Found
The requested resource or endpoint does not exist.
| Cause | Solution |
| :------------------------- | :---------------------------------------------- |
| Typo in the endpoint URL | Double-check the path against the API reference |
| Resource ID does not exist | Verify the ID belongs to your account |
| Using GET instead of POST | All Heify endpoints use `POST` |
| Wrong base URL | Use `https://api.heify.com/` |
***
## 429 Too Many Requests
You have exceeded the rate limit for an endpoint or reached a monthly quota.
See [Rate Limits & Quotas](/platform/rate-limits) for per-endpoint limits and a backoff implementation example.
***
## 500 Internal Server Error
An unexpected error occurred on Heify's servers.
The error is likely temporary. Wait a few seconds and try again.
If the issue persists, contact [hola@heify.com](mailto:hola@heify.com).
# Rate Limits & Quotas
Source: https://docs.heify.com/platform/rate-limits
Per-endpoint request limits, burst allowances, and resource quotas for the Heify API.
## Rate Limits
Rate limits are applied per **account** and enforced at the API gateway level. All limits are measured in **requests per minute** with an additional burst capacity for short spikes.
**Burst capacity** allows you to exceed the steady-state rate limit for a short period. For example, an endpoint with 50 req/min and burst 3 means you can fire up to 3 requests in rapid succession before the 50 req/min cap is enforced.
### Configurations
| Endpoint | Requests/min | Burst |
| :--------------------------- | :----------: | :---: |
| `POST /create-configuration` | 10 | 2 |
| `POST /list-configurations` | 50 | 3 |
| `POST /get-configuration` | 500 | 17 |
| `POST /update-configuration` | 50 | 3 |
| `POST /delete-configuration` | 50 | 3 |
### Evaluators
| Endpoint | Requests/min | Burst |
| :----------------------- | :----------: | :---: |
| `POST /create-evaluator` | 10 | 2 |
| `POST /list-evaluators` | 50 | 3 |
| `POST /get-evaluator` | 500 | 17 |
| `POST /update-evaluator` | 500 | 17 |
| `POST /delete-evaluator` | 500 | 17 |
### Participants
| Endpoint | Requests/min | Burst |
| :------------------------- | :----------: | :---: |
| `POST /create-participant` | 10 | 2 |
| `POST /list-participants` | 50 | 3 |
| `POST /get-participant` | 500 | 17 |
| `POST /update-participant` | 500 | 17 |
| `POST /delete-participant` | 500 | 17 |
### Transcriptions
| Endpoint | Requests/min | Burst |
| :--------------------------------- | :----------: | :---: |
| `POST /submit` | 500 | 17 |
| `POST /request-upload-url` | 500 | 17 |
| `POST /list-transcriptions` | 500 | 17 |
| `POST /get-transcription` | 500 | 17 |
| `POST /update-transcription-group` | 500 | 17 |
| `POST /delete-transcription` | 500 | 17 |
### Analytics
| Endpoint | Requests/min | Burst |
| :---------------------------- | :----------: | :---: |
| `POST /analytics` | 5 | 2 |
| `POST /analytics-evaluator` | 50 | 3 |
| `POST /analytics-participant` | 50 | 3 |
***
## Quotas
### Monthly Analytics Quotas
Analytics endpoints are subject to a **monthly call quota** in addition to the per-minute rate limit. Quotas reset automatically on the 1st of each calendar month.
| Endpoint | Monthly limit |
| :---------------------------- | :-----------: |
| `POST /analytics` | 30 calls |
| `POST /analytics-evaluator` | 500 calls |
| `POST /analytics-participant` | 500 calls |
Your current usage and remaining calls are visible in the Sandbox from the user menu (top right).
### Resource Limits
The following limits apply to the number of objects you can create per account.
| Resource | Limit |
| :------------------------------------------------ | :---------------: |
| Configurations per account | 20 |
| Extraction fields per configuration | 10 |
| Evaluators per account | 20 |
| Criteria per evaluator | 10 |
| Participants per account | 200 |
| Team members (sub-users) | 10 |
| Transcriptions returned by `/list-transcriptions` | 20,000 |
| Transcriptions analyzed by `/analytics` | 20,000 |
| Max audio duration | 2 hours (7,200 s) |
| Max audio file size | 200 MB |
| Transcriptions in queue | 100 |
| Concurrent processing | 25 |
| Transcription data retention (TTL) | 1 year |
### Supported Audio & Video Formats
| Format | Extension | Description |
| :----- | :-------: | :---------------------------- |
| AAC | `.aac` | Advanced Audio Coding |
| AIFF | `.aiff` | Audio Interchange File Format |
| AMR | `.amr` | Adaptive Multi-Rate |
| ASF | `.asf` | Advanced Systems Format |
| FLAC | `.flac` | Free Lossless Audio Codec |
| MP3 | `.mp3` | MPEG Audio Layer 3 |
| OGG | `.ogg` | Ogg Vorbis |
| WAV | `.wav` | Waveform Audio File Format |
| WebM | `.webm` | WebM Audio |
| M4A | `.m4a` | MPEG-4 Audio |
| MP4 | `.mp4` | MPEG-4 Video Container |
### Need higher limits?
Default limits work for most use cases, but if your project requires more — more analytics calls, more participants, or a larger team — we can adjust them for you.
Reach out to **[hola@heify.com](mailto:hola@heify.com)** and tell us what you need. We'll get back to you quickly.
***
## Handling 429 Errors
A `429 Too Many Requests` response means either a per-minute rate limit or a monthly quota has been exceeded. Implement exponential backoff to retry gracefully:
```python Python theme={null}
import requests
import time
def request_with_backoff(url, headers, payload, max_retries=5):
delay = 1
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=payload)
if response.status_code != 429:
return response
print(f"Rate limited. Retrying in {delay}s...")
time.sleep(delay)
delay *= 2
return response
headers = {
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY"
}
result = request_with_backoff(
"https://api.heify.com/list-configurations",
headers,
{}
)
print(result.json())
```
```bash cURL theme={null}
#!/bin/bash
MAX_RETRIES=5
DELAY=1
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(curl -s -o /tmp/response.json -w "%{http_code}" \
-X POST https://api.heify.com/list-configurations \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{}')
if [ "$RESPONSE" != "429" ]; then
cat /tmp/response.json
exit 0
fi
echo "Rate limited. Retrying in ${DELAY}s..."
sleep $DELAY
DELAY=$((DELAY * 2))
done
```
# Quickstart
Source: https://docs.heify.com/quickstart
From zero to your first transcription in minutes — no matter how you want to use Heify.
Heify turns voice recordings into structured data. You define what to extract, Heify does the rest: transcription, field extraction, AI summaries, quality scoring, and analytics — all through a pipeline you control.
## How the Pipeline Works
Every transcription in Heify flows through the same building blocks:
The template that defines **how** to process audio: what fields to extract, whether to summarize, which language, custom vocabulary, and webhooks.
An optional quality-scoring rubric. Define criteria with weights that sum to 100 — Heify scores every call against them automatically.
An optional person profile — a support agent, job candidate, salesperson, or any individual. Attach a participant to a transcription to track their performance over time.
The result: full transcript, extracted fields, AI summary, evaluation score, and more — ready to query or export.
Only a **Configuration** is required. Evaluators and Participants are optional modules you can add when you need quality scoring or per-person performance tracking.
***
## Choose Your Path
The Sandbox is Heify's browser-based interface. No code required — create configurations, upload audio, and explore results in minutes.
Go to [sandbox.heify.com](https://sandbox.heify.com) and enter your email address.
Heify uses **passwordless authentication** — no passwords to set or remember. After you submit your email:
1. A 6-digit OTP code is sent to your inbox
2. Enter the code in the Sandbox (you have 3 minutes before it expires)
3. You're in — your session lasts **30 days**
You can paste the full 6-digit code directly into the first field and it fills in automatically.
Before you can transcribe, the Sandbox needs an API key linked to your account.
Open **API Keys & Sandbox** from the top-left menu (or follow the yellow banner on the Dashboard). Your sandbox key auto-provisions and auto-configures — no manual steps needed. The status indicator turns green: **Ready**.
Your sandbox key is automatically shared across devices. Log in from any browser and it's already configured.
A Configuration tells Heify what to do with each audio file.
1. Go to **Configurations** → **Create Configuration**
2. Give it a **Tag** (a name, e.g. `support-calls-v1`)
3. Toggle **Automatic Summary** on if you want an AI-generated executive summary per call
4. Add **Extraction Fields** to pull structured data from conversations:
| Field name | Type | Description |
| -------------------- | ------- | ------------------------------------------------- |
| `customer_sentiment` | string | Overall sentiment: POSITIVE, NEGATIVE, or NEUTRAL |
| `call_reason` | string | Main reason the customer called |
| `resolved` | boolean | Whether the issue was resolved during the call |
5. (Optional) Add **custom vocabulary** — product names, internal terms, abbreviations — to improve transcription accuracy
6. Click **Create Configuration**
Use the **Quick Actions** shortcuts to add common fields (sentiment analysis, quality rating, next action) with a single click.
1. Go to **Transcribe** in the sidebar
2. Select your Configuration from the dropdown
3. (Optional) Select an **Evaluator** and/or a **Participant** if you have them
4. Choose how to submit:
* **Upload file** — drag & drop or click to browse (MP3, WAV, MP4, WebM, AAC, and more)
* **URL** — paste a public link to an audio file
5. Click **Process**
Once submitted, a link appears to open the transcription detail directly. The transcription is processed asynchronously — if you see a `Processing` status, wait a moment and click the **Refresh** button to check for updates.
Once processing is complete (status: **Completed**), click the transcription to open the detail view:
* **Full transcript** with speaker labels and timestamps
* **Extracted fields** — the structured data you defined (e.g. `resolved: true`, `call_reason: "billing issue"`)
* **AI Summary** — if you enabled it in the configuration
* **Evaluation** — score, pass/fail status, and per-criterion feedback (if you used an evaluator)
* **Export to PDF** — the full report in one click
The Heify API lets you submit audio, retrieve results, and query analytics programmatically. All endpoints use `POST` and return a standard JSON envelope.
**All requests require:**
* `Content-Type: application/json`
* `x-api-key: ` header
**All responses follow this shape:**
```json theme={null}
// Success
{ "data": { ... } }
// Error
{ "error": { "message": "...", "code": 400 } }
```
Log in to [sandbox.heify.com](https://sandbox.heify.com), navigate to **API Keys**, and either:
* Click **Auto-provision** to create a sandbox key instantly, or
* Click **Add new key** to create a named production key
Copy the key — you'll use it as the `x-api-key` header on every request.
A Configuration is the reusable template that tells Heify what to extract from audio.
```bash cURL theme={null}
curl -X POST https://api.heify.com/create-configuration \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"tag": "support-calls-v1",
"summary": true,
"summary_language": "en",
"extraction_fields": [
{
"name": "customer_sentiment",
"type": "string",
"description": "Overall sentiment of the customer: POSITIVE, NEGATIVE, or NEUTRAL"
},
{
"name": "resolved",
"type": "boolean",
"description": "Whether the customer issue was resolved during the call"
}
]
}'
```
```python Python theme={null}
import requests
response = requests.post(
"https://api.heify.com/create-configuration",
headers={
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY"
},
json={
"tag": "support-calls-v1",
"summary": True,
"summary_language": "en",
"extraction_fields": [
{
"name": "customer_sentiment",
"type": "string",
"description": "Overall sentiment of the customer: POSITIVE, NEGATIVE, or NEUTRAL"
},
{
"name": "resolved",
"type": "boolean",
"description": "Whether the customer issue was resolved during the call"
}
]
}
)
configuration_id = response.json()["data"]["configuration_id"]
print(f"Configuration created: {configuration_id}")
```
```json Response (201) theme={null}
{
"data": {
"message": "Configuration created successfully",
"configuration_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
```
Save the `configuration_id` — you'll use it every time you submit a transcription.
Choose the submission method that fits your use case:
If your audio is hosted online (S3, CDN, etc.), submit it directly:
```bash cURL theme={null}
curl -X POST https://api.heify.com/submit \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"configuration_id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/call-recording.mp3",
"name": "Support call 2024-01-15"
}'
```
```python Python theme={null}
import requests
response = requests.post(
"https://api.heify.com/submit",
headers={
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY"
},
json={
"configuration_id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/call-recording.mp3",
"name": "Support call 2024-01-15"
}
)
transcription_id = response.json()["data"]["transcription_id"]
print(f"Transcription started: {transcription_id}")
```
```json Response (201) theme={null}
{
"data": {
"message": "Transcription created successfully",
"transcription_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
```
For local files, use the two-step presigned upload:
**Step 1 — Request an upload URL:**
```bash cURL theme={null}
curl -X POST https://api.heify.com/request-upload-url \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"configuration_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Support call 2024-01-15"
}'
```
```python Python theme={null}
import requests
response = requests.post(
"https://api.heify.com/request-upload-url",
headers={
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY"
},
json={
"configuration_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Support call 2024-01-15"
}
)
result = response.json()["data"]
upload_url = result["upload_url"]
transcription_id = result["transcription_id"]
```
**Step 2 — Upload the file directly to S3:**
```bash cURL theme={null}
curl -X PUT "UPLOAD_URL_FROM_PREVIOUS_STEP" \
--upload-file "./call-recording.mp3"
```
```python Python theme={null}
with open("call-recording.mp3", "rb") as f:
requests.put(upload_url, data=f)
print(f"Uploaded. Transcription ID: {transcription_id}")
```
The presigned URL expires in **5 minutes**. Upload the file immediately after requesting the URL.
**Supported formats:** `aac`, `aiff`, `amr`, `asf`, `flac`, `mp3`, `ogg`, `wav`, `webm`, `m4a`, `mp4`
Transcription is asynchronous. Poll `/get-transcription` until `status` is `COMPLETED` or `FAILED`.
```bash cURL theme={null}
curl -X POST https://api.heify.com/get-transcription \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"transcription_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}'
```
```python Python theme={null}
import requests, time
def wait_for_transcription(transcription_id, api_key):
while True:
response = requests.post(
"https://api.heify.com/get-transcription",
headers={
"Content-Type": "application/json",
"x-api-key": api_key
},
json={"transcription_id": transcription_id}
)
result = response.json()["data"]
status = result["status"]
if status == "COMPLETED":
return result
elif status == "FAILED":
raise Exception(result.get("error_message", "Transcription failed"))
print(f"Status: {status} — waiting...")
time.sleep(5)
result = wait_for_transcription(transcription_id, "YOUR_API_KEY")
```
A completed transcription returns:
```json Response (200) theme={null}
{
"data": {
"transcription_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "COMPLETED",
"configuration_tag": "support-calls-v1",
"name": "Support call 2024-01-15",
"duration": 312.4,
"language": "en",
"transcript": "Agent: Thank you for calling. How can I help you today? Customer: Hi, I have an issue with my invoice...",
"summary": "The customer called regarding a billing discrepancy on their January invoice. The agent resolved the issue by applying a correction credit.",
"extraction": {
"customer_sentiment": "NEGATIVE",
"resolved": true
},
"evaluation_score": null,
"critical_fail_triggered": false,
"group": null,
"created_at": "2024-01-15T10:30:00.000000"
}
}
```
Instead of polling, configure a **webhook** on your Configuration (`webhooks.success_url`) to receive a POST notification the moment processing completes.
### What's next from the API?
Create an Evaluator and pass `evaluator_id` when submitting — every transcription gets auto-scored
Create Participants and pass `participant_id` to build performance analytics per agent
Call `/analytics` on any Configuration to analyze your full corpus with AI
Learn about key rotation, rate limits, and best practices
***
## Success!
You've successfully transcribed your first audio/video file with Heify!
# Heify Recorder
Source: https://docs.heify.com/recorder/index
Chrome extension that records any meeting on your computer and emails you a Heify report. No bots joining the call — capture happens locally.
The **Heify Recorder** records the audio of any meeting (Zoom, Teams, Meet, Slack huddles, any app or tab) and emails you a Heify report when you stop. Your audio is uploaded to your sandbox, transcribed, extracted against the [Configuration](/core/configuration) you choose, optionally attributed to a [Participant](/core/participant), optionally scored against an [Evaluator](/core/evaluator), and delivered as an email report.
Listed as **Heify — AI meeting reports**.
The Recorder only captures **audio**. Chrome's screen-share API requires you to pick a source — the video track is discarded immediately, except when you take a [screenshot](/recorder/recording#screenshots).
***
## Connect to your account
Click the Heify icon in the Chrome toolbar. A small app window opens.
A tab opens at `sandbox.heify.com` for OTP login (same flow as [Sandbox Authentication](/sandbox/authentication)). Once signed in, the tab confirms the connection and the Recorder switches to the main screen.
If connection fails, the screen shows a clear message (*no active session*, *invalid/expired API key*, *couldn't reach Heify*) with a **Retry connection** button.
***
## The Recorder window
The header always shows three controls:
* **🌐 Language** — UI language (ES/EN). Doesn't affect the report.
* **☀️ / 🌙 Theme** — light or dark.
* **👤 Profile** — your account modal (minutes, emails, role, disconnect). See [Account & plan](/recorder/recording#account-and-plan).
Closing the Recorder window **mid-recording cancels the recording**. Closing while idle is safe — state is preserved.
# Recording a meeting
Source: https://docs.heify.com/recorder/recording
Prepare the session, capture audio, take screenshots, and send the report.
The main screen has three cards: **Meeting**, **Report**, and **Send**. Fill them in, press **Start recording**, and Heify handles the rest.
***
## Meeting
| Field | Purpose |
| :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| **Name** | How this recording will appear in Heify and in the email subject (max 200 chars). Pre-filled from the active tab title when relevant. |
| **Configuration** *(required)* | What to extract from the audio. See [Configuration](/core/configuration). Create new ones from the **+** button. |
| **Participant** *(optional)* | The main person in the meeting. See [Participant](/core/participant). |
| **Evaluator** *(optional)* | A scoring rubric. See [Evaluator](/core/evaluator). Without one, the **Evaluation** section of the report is disabled. |
## Report
Pick which sections to include — **at least one must be active**: **Data**, **Summary**, **Fields** (from your configuration), **Evaluation** (only if an evaluator is selected).
The **report language** (ES/EN) is independent from the UI language.
## Send
Up to **10 external recipients per report**. Type an email and press Enter (comma, space, semicolon, and paste also work). **Your own email always gets a copy** — it doesn't take a recipient slot but does consume 1 unit from your monthly quota. See [plan limits](#account-and-plan).
Leave **Subject** empty to use the default `Heify Report · `.
***
## Start the recording
Press **Start recording**. Chrome opens its source picker with three tabs:
| Picker tab | Use for |
| :---------------- | :---------------------------------------------- |
| **Chrome Tab** | Meet, Zoom Web, Teams Web |
| **Window** | Zoom desktop, Teams desktop, Slack, Discord |
| **Entire Screen** | When unsure, or the meeting spans multiple apps |
**Tick "Share system audio"** in Chrome's picker. Without it, the recording is silent — only your microphone is captured, not the meeting.
The first time, Chrome also asks for microphone permission. Accept it to record your voice.
***
## During the recording
The window switches to the recording screen with a pulsing blue border, the live timer, the source you're sharing, and an audio level meter (flat bars = no audio coming through — verify before wasting time).
### Controls
| Button | Action |
| :------------------ | :---------------------------------------------------------- |
| 📷 **Capture** | Take a screenshot (max 5 — see [Screenshots](#screenshots)) |
| ⏸ / ▶ | Pause / resume. Audio during the pause is not captured |
| ⏹ **Stop** | Opens the review modal |
| ✏️ **Edit details** | Edit fields without interrupting the recording |
| 🗑 | Discard the recording (asks for confirmation) |
If you **stop sharing from Chrome** (close the tab/window, end the call): if you've recorded ≥3 seconds the Recorder auto-triggers the send flow with what was captured. Under 3 seconds, it's treated as an error.
### Edit while recording
**✏️ Edit details** opens the prep screen without stopping the capture. A sticky header keeps the timer running. You can change every field **except the source** — that choice was locked when you pressed **Start recording**.
***
## Screenshots
Press **📷 Capture** during a recording to snap a screenshot of the shared source. Up to **5 per recording** — they go **inline** in the report email, not as attachments. When you press Capture the Recorder briefly minimises so it doesn't appear in the shot.
| Action | Effect |
| :-------------- | :-------------------------------------------------- |
| Click thumbnail | Open the viewer (←/→ to navigate, **Esc** to close) |
| Click the name | Inline rename |
| ✏️ | Open the annotation editor |
| ✕ | Delete |
The annotation editor has the usual tools (pencil, rectangle, arrow, text, highlighter, eraser), 8 preset colors, and 3 stroke widths. Shortcuts: **Ctrl+Z** undo · **Ctrl+Shift+Z** / **Ctrl+Y** redo · **Esc** cancel. **Save** replaces the original screenshot with the edited version.
***
## Sending the report
When you press **⏹ Stop**, a **review modal** lists everything that will be sent: duration, configuration, participant, evaluator, language, active sections, recipients (your own email tagged **You**), and subject. Press **Send report** to start the upload.
The processing screen has two sub-states: **Uploading audio** (the `.webm` goes to your Heify bucket — you can **Cancel upload** here, and **Download audio (.webm)** is always available) and **Generating the report** (Heify processes the audio and dispatches the email).
Closing the Recorder window during upload **loses the recording**. Wait for the **Done!** screen.
### Happy path
The **Done!** screen offers **View in Heify**, **Download audio (.webm)** (until you start a new recording), and **New recording**.
### Audio saved (partial failure)
If the audio uploaded but the email failed, the screen turns amber with the translated reason (no emails available / session expired / transient). Use **Retry email send** (no re-upload) or **Reconnect** as appropriate. The audio is safe — already in Heify and downloadable.
***
## Account and plan
Click **👤** in the header to see remaining minutes, remaining emails (progress bar turns amber under 20%, red at 0), role, and **Disconnect**.
| Limit | Value |
| :------------------------------ | :----------------------------------------------------- |
| **Emails dispatched per month** | **100** (your own copy included) |
| Recipients per report | up to **11** — you + 10 external |
| Screenshots per recording | **5** |
| Recording length | up to **2 hours** ([see FAQ](/recorder/reference#faq)) |
Every email dispatched consumes 1 unit, including your own copy. Need more capacity? [hola@heify.com](mailto:hola@heify.com).
# Reference
Source: https://docs.heify.com/recorder/reference
Errors, shortcuts, privacy, requirements, and FAQ.
## Common errors
| Message | Fix |
| :--------------------------------- | :--------------------------------------------------------------------------------------------------- |
| **No audio** | "Share system audio" was not ticked in the picker — start over and tick it |
| **No audio detected** | The chosen source stayed silent — verify the app/tab was actually playing audio |
| **Microphone permission denied** | Open `chrome://settings/content/microphone` and allow `*.heify.com` (the screen has a direct button) |
| **Recording / upload interrupted** | Audio is kept locally when possible — press **Download audio (.webm)** |
| **Upload session expired** | Download the audio before retrying; the presigned URL is no longer valid |
| **No emails available** | Top up your plan from **Open Heify** |
| **Session expired** | Press **Reconnect** and sign in again |
Every error screen has **Copy diagnostic** — copies the relevant events to the clipboard with no sensitive data (no API key, no full recipients). Paste it when you contact support.
***
## Keyboard shortcuts
| Shortcut | Action |
| :---------------------------- | :----------------------------------- |
| **Ctrl+Enter** / **⌘+Enter** | Start recording (prep screen) |
| **Esc** | Close active modal or viewer |
| **Ctrl+Z** / **Ctrl+Shift+Z** | Undo / redo in the annotation editor |
| **← / →** | Navigate screenshots in the viewer |
***
## Privacy
Audio uploads directly from your browser to Heify's private bucket via a signed temporary URL — no third-party servers in between. No tracking, no analytics, no third-party scripts. The recent-recipients history lives only on your machine. API keys and Cognito tokens never appear in console output or diagnostics.
Full policy: [heify.com/en/privacy](https://heify.com/en/privacy).
***
## Requirements
Chrome 116+ (other Chromium browsers may work but aren't guaranteed), a [heify.com](https://heify.com) account, and an internet connection for the upload + report generation. The recording itself happens locally.
***
## FAQ
No — only audio (system + microphone mix). Chrome's picker requires a "source" because of its API, but the video track is discarded immediately, except briefly to take screenshots when you request them.
Because technically you are — without screen-share, Chrome wouldn't let any extension capture system audio. The red bar comes from Chrome, not Heify.
Yes — pick **Window** in Chrome's picker and select the app. Remember to tick **Share audio**.
Heify analyses up to **2 hours per recording**. On top of that, the browser holds the whole audio in memory until you stop — recordings longer than **\~1 hour can crash Chrome** on weaker machines. Split long sessions into shorter ones if you hit memory issues.
If it lasted ≥3s and the source ended on its own (call finished, you closed Zoom), the send flow starts automatically. If Chrome crashed or you closed the Recorder window, the audio is lost.
At [sandbox.heify.com](https://sandbox.heify.com) → Transcriptions.
Anything else? Write to **[hola@heify.com](mailto:hola@heify.com)**.
# Configuration Analysis
Source: https://docs.heify.com/sandbox/analytics
Generate an AI-powered analytics report for a configuration — distribution metrics, anomalies, extraction field insights, temporal trends, and cross-variable relationships.
The Configuration Analysis page aggregates all transcriptions processed under a selected configuration and organizes the results into seven collapsible dashboard sections: AI synthesis, distribution metrics, data quality, anomaly detection, extraction field analysis, temporal trends, and cross-variable relationships.
Each analysis generation consumes **one unit** of your account's analytics quota. Viewing previously cached results for the same configuration ID is free.
***
## Generating an analysis
Select a configuration from the searchable dropdown and click **Generate Analysis**. The dashboard will populate once the report is ready.
Navigating directly to a URL that includes a configuration ID (e.g. from a bookmark or shared link) loads the last cached result immediately — no quota is consumed.
***
## Dashboard controls
Once results are loaded, the dashboard header shows:
| Control | Behavior |
| ------------------ | -------------------------------------------------------------------------------------- |
| **Files Analyzed** | Total number of audios included in this analysis |
| **Total Duration** | Combined duration of all analyzed audios |
| **Last updated** | Relative time since the last fetch |
| **Timezone** | Changes the reference timezone for time-based charts. Does not trigger a new API call. |
| **Full Screen** | Fills the browser window with the dashboard — surrounding UI is hidden |
| **Export** | Opens a dropdown with three export options |
### Export options
| Option | Format | How it works |
| ----------- | -------------- | --------------------------------------------------------------------------------- |
| **As HTML** | `.html` file | Renders all sections and packages them into a self-contained downloadable file |
| **Nativa** | PDF via print | Renders all sections and opens the browser print dialog — choose "Save as PDF" |
| **Ligera** | Structured PDF | Generates a lightweight PDF report directly, without rendering the live dashboard |
**Nativa** requires the browser to allow the print popup. If it is blocked, use **Ctrl+P** (Windows) / **Cmd+P** (Mac) as a fallback.
***
## Dashboard sections
All sections are **collapsible cards**. The **Synthesis** section is expanded by default; all others start collapsed. A section is only rendered when the API returns data for it — sections with no data are hidden automatically.
A sticky sub-menu in the dashboard header tracks which section is in view and lets you jump between sections without scrolling.
***
### Synthesis *(open by default)*
AI-generated top-level analysis of the entire dataset.
* **Executive Narrative** — prose summary describing the dataset's key patterns, notable findings, and overall trends
* **Actionable Insights** — a prioritized list of specific recommendations derived from the data
***
### General Distribution
Key metrics across all analyzed audios.
| Sub-section | Content |
| ------------- | --------------------------------------------------------------------------------- |
| **Duration** | Minimum, average, and maximum audio duration across all transcriptions |
| **Speakers** | Average speakers per audio · Distribution of audios by speaker count (1, 2, 3… N) |
| **Languages** | Count and percentage breakdown of each detected language |
***
### Quality and Confidence
Measures the completeness and reliability of the extracted data.
**Completeness** — a horizontal progress bar for each extraction field defined in the configuration, showing what percentage of audios had that field successfully populated (`validCount / totalCount valid`).
**Audios to Review** — a paginated table of audios with an unusually high number of missing extraction fields:
| Column | Detail |
| ------------------ | --------------------------------------------------------------------------------- |
| **Missing Fields** | Count of missing fields, e.g. "3 of 5 missing" |
| **ID** | Transcription ID with a copy button |
| **Group** | Group badge + **Change Group** button — update the group directly from this table |
| **Date** | Creation date |
| **Details** | Link to the full transcription detail view |
***
### Anomalies
Detects audios with values that deviate significantly from the normal pattern using the **IQR (Interquartile Range) method**.
Three categories are analyzed:
| Category | What is analyzed |
| ------------------ | -------------------------------------------------------------- |
| **Daily Volume** | Days with an abnormally high or low number of submitted audios |
| **Metadata** | Metadata fields (e.g. duration) with outlier values |
| **Extracted Data** | Extracted fields with values outside the expected range |
Each category shows the expected normal range and a paginated table of outliers. Each outlier row has a **High** or **Low** badge, the transcription ID, its group (with a **Change Group** button), and a link to the detail view.
Anomalies are not always errors — they may represent legitimate edge cases worth reviewing. Use the **Change Group** button to tag them for follow-up without leaving the analysis.
***
### Extraction Fields
A dedicated card for each extraction field defined in the configuration. The card header shows the field name, completeness percentage, and valid count.
The card body varies by field type:
| Field type | Visualizations shown |
| ----------- | ----------------------------------------------------------------- |
| **Number** | Average · Median · Sum · Min · Max · Distribution histogram |
| **String** | Total unique values · Frequency chart of top N most common values |
| **Boolean** | Positive rate (large display) · True/False proportional bar |
***
### Temporal Analysis
Two sub-sections separated by a divider.
**Temporal Evolution** — a multi-line time-series chart:
* **Volume line** — daily count of submitted audios
* **Metric lines** — one line per extracted numeric field (daily average)
* Lines are interpolated and remain continuous even on days with no data
* Use the metric checkboxes to show or hide individual lines
* **Reset zoom** button restores the default chart view after zooming in
Below the chart, a summary table shows for each metric: Max · Min · Average · Trend direction (improving / degrading / stable).
**Activity Heatmap** — a day-of-week × hour-of-day grid:
* X-axis: hours of the day (0–23)
* Y-axis: days of the week (Monday–Sunday)
* Color intensity: light = low activity, dark = high activity
Auto-generated insights below the heatmap:
| Insight | Content |
| ---------------------------- | ------------------------------------------------------ |
| **Peak Time** | The specific day and hour with the highest audio count |
| **Busiest Day** | The weekday with the most audios across all hours |
| **Most Frequent Hour Range** | The hour-of-day with the most accumulated audios |
The **Timezone** selector in the dashboard header affects both Temporal Evolution and Activity Heatmap. Changing the timezone does not reload data from the API.
***
### Relationships and Patterns
Two advanced analyses separated by a divider.
**Correlations** — a matrix comparing all numerical extraction fields:
* Each cell shows a correlation coefficient (−1 to +1)
* **Blue** = positive correlation · **Red** = negative correlation · **Grey** = weak or no correlation
* Tooltip shows the number of data pairs used per cell
* Requires at least **2 numerical fields** with data
Auto-generated insights: Strongest Positive Correlation · Strongest Negative Correlation · Weakest Association.
**Co-occurrence** — one matrix per array or categorical field, showing how often two values appear together in the same audio:
* 4 color intensity levels: Very Low → Low → Medium → High
* Tooltip shows total appearances and co-occurrence count
Auto-generated insights: Most Frequent Pair · Most Common Item.
***
Manage your configurations
Browse individual transcription results
# Evaluator Analysis
Source: https://docs.heify.com/sandbox/analytics-evaluator
Generate a performance dashboard for an evaluator — date-range filtering, KPIs, timeline trends, per-criterion performance and evolution, quality heatmap, participant rankings, and a filterable transcription table.
The Evaluator Analysis page aggregates all transcriptions processed with a selected evaluator and organizes the results into KPI summaries, trend charts, a per-criterion evolution chart, a per-criterion performance ranking, a weekly quality heatmap, participant rankings, and a filterable transcription detail table. A period filter scopes the whole dashboard to a date range.
Each analysis generation consumes **one unit** of your account's `analytics_evaluator` quota. Loading a previously cached result for the same evaluator ID does not consume quota.
***
## Generating an analysis
Select an evaluator from the searchable selector at the top of the page and click **Generate Analysis**. The selector shows each evaluator's name, ID, and criteria count.
Navigating directly to a URL that includes an evaluator ID (e.g. from a bookmark or shared link) auto-loads the last cached result — no quota consumed.
The **Generate Analysis** button is disabled when no evaluator is selected or when your quota is exhausted. If you click it without a selection, a warning is shown: *"You must select an evaluator to generate the analysis."*
***
## Dashboard header
Once data is loaded, the dashboard header shows:
| Control | Behavior |
| ------------------ | ---------------------------------------------------------------------------------- |
| **Evaluator name** | Displayed below the title once an evaluator is active |
| **Last updated** | Relative time since the last fetch |
| **Download PDF** | Generates and downloads a structured PDF report. Only visible when data is loaded. |
| **Refresh** | Re-fetches the analysis for the current evaluator |
***
## Period filter
The first card of the dashboard — shown once an analysis has been run — scopes **everything below it** to a date range.
| Element | Behavior |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| **From** / **To** | Native date pickers. Neither accepts a future date; **From** is capped by **To** and vice versa. |
| **Apply** | Re-runs the analysis server-side over the selected range. Empty fields are omitted — full history. |
| **Clear** | Blanks both dates and re-runs unfiltered. Only rendered when at least one date is set. |
If the start date is after the end date, **Apply** is disabled and a message appears: *"The start date can't be after the end date."*
Both **Apply** and **Clear** issue a real API call and consume one unit of analytics quota, exactly like **Generate Analysis**.
Navigating to another page and back keeps both the data and the From/To inputs. A **browser reload does not** — the dashboard re-queries unfiltered and the inputs come back empty.
There is no "quarter" mode — a quarter is just a range. For Q2 2026, set From `2026-04-01` and To `2026-06-30`.
The filter matches on each transcription's **recording date** (which the [recording date controls](/sandbox/transcribe#recording-date) set at upload) — so calls uploaded late but dated correctly land in the right period.
***
## KPI Grid
Four metric cards summarize the overall state of the evaluator's dataset.
| KPI | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------- |
| **Total Calls** | Count of all transcriptions evaluated with this evaluator |
| **Average Score** | Overall score out of 100. Color: Emerald ≥91 · Green 71–90 · Amber 51–70 · Orange 31–50 · Red \<31 |
| **Pass Rate** | Percentage of calls that passed. Badge: **Excellent** ≥80% · **Good** 60–79% · **Fair** 40–59% · **Low** \<40% |
| **Criticals** | Calls where a Strict criterion failed. Red badge if >0; *"No critical incidents"* if 0 |
When **Average Score** or **Pass Rate** has no value, the card shows **—** (an em dash) with its `/100` or `%` suffix, rather than a misleading `0`.
### Critical alert banner
If any pending critical calls exist, a clickable red banner appears above the KPI grid:
> *"N pending critical calls — Pending review · Click for details"*
Clicking it jumps directly to the [Transcription Details](#transcription-details) table with the *Pending Critical* filter pre-applied.
***
## Charts
### Monthly Comparison
Two side-by-side period cards — **This Month** and **Previous Month** — each showing:
* **Calls** — total count
* **Average Score** — numeric
* **Pass Rate** — percentage
A delta summary below the cards shows the score change (green upward arrow for improvement, red downward for decline) and the pass rate change between periods.
| State | Display |
| ------------------------- | -------------------------------------------------------- |
| One period has no data | Amber warning on that card |
| Both periods have no data | *"Data in both months is needed to show the comparison"* |
### Duration Analysis
Three tiles grouping calls by duration using thresholds dynamically calculated from the dataset:
| Tier | Threshold |
| ---------- | ------------------- |
| **Short** | Below p25 |
| **Medium** | Between p25 and p75 |
| **Long** | Above p75 |
Each tile shows the call count and average score for that tier.
Duration thresholds are dynamically calculated using the p25 and p75 percentiles of your dataset — they adapt to the actual distribution of your calls, not a fixed value.
### Timeline Evolution
A dual-axis chart overlaying two series:
* **Left Y-axis** — Average Score (line)
* **Right Y-axis** — Call Volume (bars)
Hover over any point to see both metrics simultaneously.
### Score Distribution
A bar chart grouping all transcriptions into 20-point score ranges: **0–20 · 20–40 · 40–60 · 60–80 · 80–100**. Shows how scores are distributed across the full dataset.
***
## Evolution by criterion
A multi-line chart — one line per criterion, plotted per period — showing whether each criterion is getting better or worse over time. Rendered only when the analysis contains per-period criteria data. The header shows the covered range (e.g. `Jan 2026 - Jun 2026`) and an **"N periods"** pill.
| Control | Options | Notes |
| ------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Grouped by** | **Month** *(default)* · **Quarter** | Quarter aggregation happens in the browser — no extra query, no quota consumed. |
| **View mode** | **Native** *(default)* · **%** | **Native** plots each criterion's real 1–5 score — scale criteria only. **%** plots everything as a compliance percentage (0–100%), comparable across any criterion type. |
| **Criterion pills** | One per criterion, plus **All** / **None** | The first 5 criteria are selected by default; click a pill to toggle its line. |
In **Native** mode, the pills for Yes/No and Strict criteria are disabled (*"Yes/No criteria are only shown in %"*) — a 0–1 compliance rate and a 1–5 score cannot share a Y axis. If there are no scale criteria at all, the **Native** button itself is disabled and the chart opens in **%**.
Hovering a data point shows `85% (4.25/5)` in % mode, `4.25/5` in Native mode, and *"No data"* where a period has none.
**Empty states:**
* No criterion selected → *"Select at least one criterion to see its evolution."*
* No evolution data at all → **"No criterion evolution yet"** — *"There's no per-period criteria data yet. Assign a period when uploading recordings to see their evolution."*
Per-criterion data (this chart and the section below) only accumulates for transcriptions evaluated **after this feature was released** — older calls are not backfilled. If all your calls predate it, the chart stays empty until new recordings are evaluated. Set the [recording date](/sandbox/transcribe#recording-date) at upload so each call lands in the right period.
***
## Performance by criterion
A ranked list of **every criterion with data in the analysed range** — scrollable, with no top-5 cut-off. Two sort buttons in the section header (the choice also drives the [PDF report](#pdf-export)):
* **To improve** *(default)* — worst first, highest fail rate. Subtitle: *"To improve first · average score and fail %"*
* **Best** — best first.
The sort is not persisted; it resets to **To improve** on reload.
Each row shows: rank circle → criterion name → type badge (**Scale** / **Yes/No** / **Strict**) → average-score badge *(scale criteria only)* → progress bar. On the right, the **fail rate** large (one decimal), with **"fails · of N evaluated"** beneath.
The large number is the **fail** %, computed over the calls where the criterion was actually evaluated — the *"of N evaluated"* count — **not** over total calls. A criterion evaluated in 5 of your 25 calls with 2 fails shows 40%, not 8%.
**Color tiers.** The fail rate drives the rank circle, the progress bar, and the big number — a fuller bar is always a **worse** criterion:
| Fail rate | Color |
| --------- | ------ |
| ≥ 50% | Red |
| 30–50% | Orange |
| 15–30% | Yellow |
| \< 15% | Green |
The **average-score badge** has its own scale (higher = greener), because a low fail rate and a good score are different things — a criterion can pass every call and still score mediocre:
| Average score | Badge |
| ------------- | ------ |
| ≥ 3.5 | Green |
| 3.0–3.5 | Yellow |
| 2.5–3.0 | Orange |
| \< 2.5 | Red |
The average badge is deliberately hidden for Yes/No and Strict criteria: for those, the average is just the mirror of the fail rate already shown.
Criteria with **no data** in the range are hidden entirely — not shown as perfect, not shown at all. This typically means their only calls in range were evaluated before per-criterion tracking was released (no backfill). If your evaluator has 12 criteria and you only see 9, this is why.
***
## Weekly Heatmap
A day-of-week × hour-of-day grid showing the **average quality score** for each time slot across all evaluated calls.
| Color | Score range |
| ------- | ----------- |
| Grey | No data |
| Emerald | ≥ 80 |
| Green | 60–79 |
| Amber | 40–59 |
| Red | \< 40 |
Two auto-generated insights are shown above the grid:
| Insight | Content |
| ------------------------- | ----------------------------------------------- |
| **Best Performing Hour** | The day and hour with the highest average score |
| **Worst Performing Hour** | The day and hour with the lowest average score |
Hover over any cell to see the exact average score for that time slot.
The hour axis comes from each call's recording time. Recordings uploaded with a **date-only** recording date land at midnight UTC and pile into that column — [add the time at upload](/sandbox/transcribe#the-time-field) for an accurate heatmap.
***
## Participants Ranking
A table of the **top 8 participants** evaluated under this evaluator, sorted by average score.
| Column | Detail |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **#** | Rank number. Top 3 show Trophy / Medal / Award icons. Rows where the critical fail rate exceeds 30% have a red-tinted background. |
| **Participant** | Name + ID (monospace) + link to their [Participant Analysis](/sandbox/analytics-participant) |
| **Calls** | Total calls evaluated for this participant |
| **Score** | Average score — color-coded badge |
| **Criticals** | Critical fail count. Red badge if >0; *"—"* if none. |
| **Failed** | Failed criteria count. Tooltip lists the names of all failing criteria. |
| **Consistency** | Standard deviation of scores as a badge |
**Consistency thresholds:**
| Badge | Threshold |
| --------------- | ------------ |
| Very consistent | σ ≤ 5 |
| Consistent | 6 \< σ ≤ 10 |
| Variable | 11 \< σ ≤ 15 |
| Very variable | σ > 15 |
A low standard deviation means predictable, stable performance. A high value means the participant's scores vary significantly from call to call.
This section is only shown when participant data is present.
***
## Transcription Details
A full paginated table of every transcription included in the analysis (10 records per page).
### Group tabs
Filter the table by workflow group with a single click:
| Tab | Color |
| ------------------ | ------- |
| **All** | Default |
| **No Group** | Grey |
| **Pending Review** | Amber |
| **Under Review** | Blue |
| **Archived** | Purple |
Each tab shows a count badge.
### Filters
Click **Filters** to open the filter panel:
| Filter | Type |
| ----------------- | ----------------------------------------------------------- |
| **Search** | Text — matches by transcription ID or participant name |
| **Critical only** | Checkbox — shows only calls where a Strict criterion failed |
| **Min Score** | Number input |
| **Max Score** | Number input |
### Table columns
| Column | Detail |
| --------------- | -------------------------------------------------------------- |
| **Date** | Full date and time |
| **Score** | Color-coded badge (same thresholds as KPI grid) |
| **Duration** | `MM:SS` formatted |
| **Participant** | Name + ID (monospace) |
| **Critical** | Red alert icon if a Strict criterion failed |
| **Failed** | Failure count. Tooltip lists the names of all failed criteria. |
| **Group** | Group badge + pencil icon to update the group inline |
Click any row to open the full transcription detail in a new tab.
Group changes made from this table are applied immediately and reflected without requiring a full page re-fetch.
***
## PDF Export
The **Download PDF** button in the header is available once analysis data is loaded.
Report filename: `Evaluator_Report_[name]_[date].pdf`
**Report contents:**
* Evaluator name and evaluation date
* Period range — only when a date range is applied, e.g. `Period: 2026-01-01 → 2026-03-31` (a missing side prints an ellipsis; dates print raw as `YYYY-MM-DD`)
* KPIs: Total Calls · Average Score · Pass Rate · Critical Fails
* Critical fails warning (if applicable)
* Monthly comparison — **omitted when a date range is applied**
* Performance by criterion — color-coded bars
* Evolution by criterion — table
* Participants ranking table
* Transcription detail table (ID · Date · Score · Duration · Participant · Critical · Group)
The period line reflects the range that was **applied** — what the report's numbers actually cover — not whatever is typed in the From/To boxes. Typing dates and downloading without pressing **Apply** produces a report with no period line and full-history figures.
With a date range applied, **Monthly Comparison** disappears from the PDF: it compares the current calendar month against the previous one, which is meaningless — and misleading — inside an arbitrary range. Its absence is intentional.
**Performance by criterion** mirrors the on-screen section: every criterion with data (no cap — the report paginates), following the sort you selected on screen, with the same four fail-rate color tiers on the bars and % labels, and `avg 3.4/5` shown for scale criteria only.
**Evolution by criterion** is printed as a **table**, not a chart — one row per criterion, one column per month. It deliberately differs from the on-screen chart in three ways: it is always monthly (the Quarter toggle has no effect), period labels are raw keys (`2026-01`, not `Jan 2026`), and it includes **every** criterion with data regardless of which pills are selected. Cells show `3.4` for scale criteria, `78%` for Yes/No and Strict, and `—` where a month has no data.
***
Manage your evaluators
Browse individual transcription results
# Participant Analysis
Source: https://docs.heify.com/sandbox/analytics-participant
Generate a personal performance dashboard for a participant — date-range filtering, degradation alerts, KPIs with score sparkline, per-criterion performance and evolution, evaluator rankings with click-to-filter, heatmap, and a filterable transcription table.
The Participant Analysis page aggregates all transcriptions evaluated for a selected participant and organizes the results into a degradation alert, KPI summaries, trend charts, a per-criterion evolution chart, a per-criterion performance ranking, a weekly quality heatmap, an evaluator-based ranking with click-to-filter, and a filterable transcription detail table. A period filter scopes the whole dashboard to a date range.
Each analysis generation consumes **one unit** of your account's `analytics_participant` quota. Loading a previously cached result for the same participant ID does not consume quota.
***
## Generating an analysis
Select a participant from the searchable selector at the top of the page and click **Generate Analysis**. The selector shows each participant's name, ID, and evaluated calls count.
Navigating directly to a URL that includes a participant ID (e.g. from a bookmark or shared link) auto-loads the last cached result — no quota consumed.
The **Generate Analysis** button is disabled when no participant is selected or when your quota is exhausted.
***
## Dashboard header
Once data is loaded, the dashboard header shows:
| Control | Behavior |
| -------------------- | ---------------------------------------------------------------------------------- |
| **Participant name** | Displayed below the title once a participant is active |
| **Last updated** | Relative time since the last fetch |
| **Download PDF** | Generates and downloads a structured PDF report. Only visible when data is loaded. |
| **Refresh** | Re-fetches the analysis for the current participant |
***
## Period filter
The first card of the dashboard — shown once an analysis has been run — scopes **everything below it** to a date range.
| Element | Behavior |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| **From** / **To** | Native date pickers. Neither accepts a future date; **From** is capped by **To** and vice versa. |
| **Apply** | Re-runs the analysis server-side over the selected range. Empty fields are omitted — full history. |
| **Clear** | Blanks both dates and re-runs unfiltered. Only rendered when at least one date is set. |
If the start date is after the end date, **Apply** is disabled and a message appears: *"The start date can't be after the end date."*
Both **Apply** and **Clear** issue a real API call and consume one unit of analytics quota, exactly like **Generate Analysis**. This is the key difference from the [evaluator click-to-filter](#click-to-filter) further down, which is a free client-side recalculation — the two filters sit on the same page and behave completely differently.
Clicking **Apply** also clears any active evaluator filter.
Navigating to another page and back keeps both the data and the From/To inputs. A **browser reload does not** — the dashboard re-queries unfiltered and the inputs come back empty.
There is no "quarter" mode — a quarter is just a range. For Q2 2026, set From `2026-04-01` and To `2026-06-30`.
The filter matches on each transcription's **recording date** (which the [recording date controls](/sandbox/transcribe#recording-date) set at upload) — so calls uploaded late but dated correctly land in the right period.
***
## Degradation Banner
A red alert banner appears **directly above the KPI grid** when the participant's recent performance has dropped significantly.
**Trigger condition:** The average score of the last 20 evaluated records is more than **10 points lower** than the average of the previous 20 records.
A **Degradation Alert** indicates a statistically significant performance decline. Review recent transcriptions to identify contributing factors before it becomes a deeper trend.
This banner is purely informational and has no interactive actions. It is hidden when no significant decline is detected. If an evaluator filter is active (see [Evaluators Ranking](#evaluators-ranking)), the trend comparison also applies only to records from that evaluator.
***
## KPI Grid
Four metric cards summarize this participant's overall performance across all their evaluations.
| KPI | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **Total Calls** | Count of all transcriptions evaluated for this participant |
| **Average Score** | Score out of 100. Color: Emerald ≥91 · Green 71–90 · Amber 51–70 · Orange 31–50 · Red \<31. Includes a **sparkline** and a **trend badge**. |
| **Pass Rate** | Percentage of calls that passed. Badge: **Excellent** ≥80% · **Good** 60–79% · **Fair** 40–59% · **Low** \<40% |
| **Criticals** | Calls where a Strict criterion failed. Red badge if >0; *"No critical incidents"* if 0 |
When **Average Score** or **Pass Rate** has no value, the card shows **—** (an em dash) with its `/100` or `%` suffix, rather than a misleading `0`.
### Average Score — sparkline and trend badge
The **Average Score** card includes two extra elements not found in the Evaluator Analysis:
**Sparkline** — an inline chart of the last 20 scores plotted in chronological order (left to right). The line color matches the current score color. Shown when at least 2 data points exist; a progress bar is shown otherwise.
**Trend badge** — compares the last 20 records against the previous 20:
| Direction | Color | Meaning |
| ----------------- | ----- | --------------------------------- |
| Improving | Green | Recent average is higher |
| Declining | Red | Recent average is lower |
| Stable | Grey | No significant change |
| Insufficient data | Grey | Not enough records for comparison |
### Critical alert banner
If pending critical calls exist, a clickable amber/orange banner appears above the KPI grid:
> *"N pending critical calls — Pending review · Click for details"*
Clicking it jumps directly to the [Transcription Details](#transcription-details) table with the *Pending Critical* filter pre-applied.
***
## Charts
When an evaluator filter is active, all chart data is sourced from the filtered dataset.
### Monthly Comparison
Two side-by-side period cards — **This Month** and **Previous Month** — each showing Calls · Average Score · Pass Rate.
A delta summary below the cards shows the score change and pass rate change between periods.
| State | Display |
| ------------------------- | -------------------------------------------------------- |
| One period has no data | Amber warning on that card |
| Both periods have no data | *"Data in both months is needed to show the comparison"* |
### Duration Analysis
Three tiles grouping calls by duration using p25/p75 thresholds:
| Tier | Threshold |
| ---------- | ------------------- |
| **Short** | Below p25 |
| **Medium** | Between p25 and p75 |
| **Long** | Above p75 |
Each tile shows call count and average score for that tier.
Duration thresholds are dynamically calculated from p25 and p75 percentiles of the dataset — they adapt to the actual distribution of this participant's calls.
### Timeline Evolution
A dual-axis chart overlaying:
* **Left Y-axis** — Average Score (line)
* **Right Y-axis** — Call Volume (bars)
Hover over any point to see both metrics simultaneously.
### Score Distribution
A bar chart grouping transcriptions into 20-point score ranges: **0–20 · 20–40 · 40–60 · 60–80 · 80–100**.
***
## Evolution by criterion
A multi-line chart — one line per criterion, plotted per period — showing whether each criterion is getting better or worse over time. Rendered only when the analysis contains per-period criteria data. The header shows the covered range (e.g. `Jan 2026 - Jun 2026`) and an **"N periods"** pill.
| Control | Options | Notes |
| ------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Grouped by** | **Month** *(default)* · **Quarter** | Quarter aggregation happens in the browser — no extra query, no quota consumed. |
| **View mode** | **Native** *(default)* · **%** | **Native** plots each criterion's real 1–5 score — scale criteria only. **%** plots everything as a compliance percentage (0–100%), comparable across any criterion type. |
| **Criterion pills** | One per criterion, plus **All** / **None** | The first 5 criteria are selected by default; click a pill to toggle its line. |
In **Native** mode, the pills for Yes/No and Strict criteria are disabled (*"Yes/No criteria are only shown in %"*) — a 0–1 compliance rate and a 1–5 score cannot share a Y axis. If there are no scale criteria at all, the **Native** button itself is disabled and the chart opens in **%**.
When the same criterion name occurs on more than one evaluator — common on this page, since several campaigns score the same person — the series is labelled **`Name · evaluator`** to disambiguate. Hovering a data point shows `85% (4.25/5)` in % mode, `4.25/5` in Native mode, and *"No data"* where a period has none.
**Empty states:**
* No criterion selected → *"Select at least one criterion to see its evolution."*
* No evolution data at all → **"No criterion evolution yet"** — *"There's no per-period criteria data yet. Assign a period when uploading recordings to see their evolution."*
Per-criterion data (this chart and the section below) only accumulates for transcriptions evaluated **after this feature was released** — older calls are not backfilled. If all your calls predate it, the chart stays empty until new recordings are evaluated. Set the [recording date](/sandbox/transcribe#recording-date) at upload so each call lands in the right period.
***
## Performance by criterion
A ranked list of **every criterion with data in the analysed range**, across all the participant's evaluators — scrollable, with no top-5 cut-off. Two sort buttons in the section header (the choice also drives the [PDF report](#pdf-export)):
* **To improve** *(default)* — worst first, highest fail rate. Subtitle: *"To improve first · average score and fail %"*
* **Best** — best first.
The sort is not persisted; it resets to **To improve** on reload.
Each row shows: rank circle → criterion name → type badge (**Scale** / **Yes/No** / **Strict**) → **evaluator badge** → average-score badge *(scale criteria only)* → progress bar. On the right, the **fail rate** large (one decimal), with **"fails · of N evaluated"** beneath.
The evaluator badge matters here: several evaluators score the same person and criterion names repeat across them — two campaigns can both define a "Manejo de Objeciones". The badge tells you which evaluator each row belongs to.
The large number is the **fail** %, computed over the calls where the criterion was actually evaluated — the *"of N evaluated"* count — **not** over total calls. A criterion evaluated in 5 of the participant's 25 calls with 2 fails shows 40%, not 8%.
**Color tiers.** The fail rate drives the rank circle, the progress bar, and the big number — a fuller bar is always a **worse** criterion:
| Fail rate | Color |
| --------- | ------ |
| ≥ 50% | Red |
| 30–50% | Orange |
| 15–30% | Yellow |
| \< 15% | Green |
The **average-score badge** has its own scale (higher = greener), because a low fail rate and a good score are different things — a criterion can pass every call and still score mediocre:
| Average score | Badge |
| ------------- | ------ |
| ≥ 3.5 | Green |
| 3.0–3.5 | Yellow |
| 2.5–3.0 | Orange |
| \< 2.5 | Red |
The average badge is deliberately hidden for Yes/No and Strict criteria: for those, the average is just the mirror of the fail rate already shown.
Criteria with **no data** in the range are hidden entirely — not shown as perfect, not shown at all. This typically means their only calls in range were evaluated before per-criterion tracking was released (no backfill). If the participant is scored on 12 criteria and you only see 9, this is why.
***
## Weekly Heatmap
A day-of-week × hour-of-day grid showing the **average quality score** per time slot. This heatmap is built **client-side** from the raw data — it updates automatically when an evaluator filter is active, without consuming quota.
| Color | Score range |
| ------- | ----------- |
| Grey | No data |
| Emerald | ≥ 80 |
| Green | 60–79 |
| Amber | 40–59 |
| Red | \< 40 |
Two auto-generated insights are shown above the grid:
| Insight | Content |
| ------------------------- | ----------------------------------------------- |
| **Best Performing Hour** | The day and hour with the highest average score |
| **Worst Performing Hour** | The day and hour with the lowest average score |
Hover over any cell to see the exact average score for that time slot.
The hour axis comes from each call's recording time. Recordings uploaded with a **date-only** recording date land at midnight UTC and pile into that column — [add the time at upload](/sandbox/transcribe#the-time-field) for an accurate heatmap.
***
## Evaluators Ranking
A table of the **top 8 evaluators** this participant has been evaluated under, sorted by average score. Only rendered when evaluator data is present.
| Column | Detail |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **#** | Rank number. Top 3 show Trophy / Medal / Award icons. Rows where the critical fail rate exceeds 30% have a red-tinted background. |
| **Evaluator** | Tag + ID (monospace) |
| **Calls** | Total evaluated calls under this evaluator |
| **Score** | Average score — color-coded badge |
| **Criticals** | Critical fail count. Red badge if >0; *"—"* if none. |
| **% Criticals** | Critical fail rate — red if >30%, amber if >15%, grey otherwise |
| **Consistency** | Standard deviation badge |
| **Duration** | Average call duration (MM:SS) |
| **Link** | Opens the evaluator's analysis dashboard in a new tab |
**Consistency thresholds:**
| Badge | Threshold |
| --------------- | ------------ |
| Very consistent | σ ≤ 5 |
| Consistent | 6 \< σ ≤ 10 |
| Variable | 11 \< σ ≤ 15 |
| Very variable | σ > 15 |
### Click-to-filter
Clicking any row in the Evaluators Ranking activates a **client-side filter** for that campaign:
* The clicked row is highlighted; all other rows are dimmed.
* An **active filter tag** (evaluator name + X button) appears in the table header.
* Clicking the X button, or clicking the same row again, clears the filter.
* While a filter is active, the KPIs, charts, heatmap, and the transcription table are instantly recalculated using only records from that evaluator. No new API call is made.
* The two criteria sections — [Evolution by criterion](#evolution-by-criterion) and [Performance by criterion](#performance-by-criterion) — **disappear entirely** while the filter is active.
Click-to-filter is a purely client-side operation. Exploring a specific campaign does not consume any quota — all recalculations happen locally from the already-loaded data.
The criteria sections cannot follow the evaluator filter: their data (per-criterion rates, average score, criterion type) is computed **server-side** and returned pre-aggregated — it cannot be re-sliced per evaluator in the browser. Recomputing it client-side is exactly what used to produce misleading fail rates, so correct-but-absent was chosen over present-but-wrong. The evaluator badge on each row of the unfiltered list already tells you which evaluator a criterion belongs to. Applying or clearing the [period filter](#period-filter), refreshing, or re-running the analysis all reset the evaluator filter, bringing both sections back.
***
## Transcription Details
A full paginated table (10 per page) of every transcription included in the analysis.
### Group tabs
Filter by workflow group with a single click:
| Tab | Color |
| ------------------ | ------- |
| **All** | Default |
| **No Group** | Grey |
| **Pending Review** | Amber |
| **Under Review** | Blue |
| **Archived** | Purple |
### Filters
Click **Filters** to open the filter panel:
| Filter | Type |
| ----------------- | ----------------------------------------------------------- |
| **Search** | Text — matches by transcription ID or evaluator tag |
| **From** | Date — lower bound for transcription date |
| **To** | Date — upper bound for transcription date |
| **Min Score** | Number input |
| **Max Score** | Number input |
| **Critical only** | Checkbox — shows only calls where a Strict criterion failed |
### Table columns
| Column | Detail |
| ------------- | ------------------------------------------------------------------------ |
| **Date** | Full date and time |
| **Score** | Color-coded badge (same thresholds as KPI grid) |
| **Duration** | `MM:SS` formatted |
| **Evaluator** | Evaluator tag — clickable link opens the evaluator analysis in a new tab |
| **Critical** | Red alert icon if a Strict criterion failed |
| **Failed** | Failure count. Tooltip lists the names of all failed criteria. |
| **Group** | Group badge + pencil icon to update the group inline |
Click any row to open the full transcription detail in a new tab.
Group changes made from this table are applied immediately and reflected without requiring a full page re-fetch.
***
## PDF Export
The **Download PDF** button in the header is available once analysis data is loaded.
Report filename: `Participant_Report_[name]_[date].pdf`
**Report contents:**
* Participant name and evaluation date
* Period range — only when a date range is applied, e.g. `Period: 2026-01-01 → 2026-03-31` (a missing side prints an ellipsis; dates print raw as `YYYY-MM-DD`)
* KPIs: Total Calls · Average Score · Pass Rate · Critical Fails
* Critical fails warning (if applicable)
* Monthly comparison — **omitted when a date range is applied**
* Performance by criterion — color-coded bars
* Evolution by criterion — table
* Evaluators ranking table
* Transcription detail table (ID · Date · Score · Duration · Evaluator · Critical · Group)
The period line reflects the range that was **applied** — what the report's numbers actually cover — not whatever is typed in the From/To boxes. Typing dates and downloading without pressing **Apply** produces a report with no period line and full-history figures.
With a date range applied, **Monthly Comparison** disappears from the PDF: it compares the current calendar month against the previous one, which is meaningless — and misleading — inside an arbitrary range. Its absence is intentional.
**Performance by criterion** mirrors the on-screen section: every criterion with data (no cap — the report paginates), following the sort you selected on screen, with the same four fail-rate color tiers on the bars and % labels, and `avg 3.4/5` shown for scale criteria only. Each criterion name is prefixed with its **evaluator tag in blue** (truncated), since names repeat across evaluators.
**Evolution by criterion** is printed as a **table**, not a chart — one row per criterion, with an **Evaluator** column (printing `—` when a criterion has no evaluator tag) and one column per month. It deliberately differs from the on-screen chart in three ways: it is always monthly (the Quarter toggle has no effect), period labels are raw keys (`2026-01`, not `Jan 2026`), and it includes **every** criterion with data regardless of which pills are selected. Cells show `3.4` for scale criteria, `78%` for Yes/No and Strict, and `—` where a month has no data.
With an [evaluator filter](#click-to-filter) active, one PDF can mix scopes: the KPIs, evaluators ranking, score distribution, comparison, duration, timeline, and transcription sections use the **evaluator-filtered** values, while the two criteria sections are always built from the **whole participant's** server data — they ignore the evaluator filter, exactly like their on-screen counterparts. The Evaluator column and the blue evaluator prefixes keep the scope legible on the page.
***
Manage your participants
View evaluator-level dashboards
# API Keys & Team
Source: https://docs.heify.com/sandbox/api-keys
Manage API keys, check Sandbox connection status, and invite team members.
This page groups three independent blocks: the Sandbox connection status, your API keys, and team member management. Everything related to API access is managed from here.
***
## Sandbox status
When you log in, the Sandbox automatically creates or reuses a reserved API key and validates it against the Heify API — no manual action is required under normal conditions.
| State | When it appears |
| -------------------------- | --------------------------------------------------- |
| **Validating connection…** | Page load or a manual validation is in progress |
| **Sandbox ready** | The reserved key is active and responding correctly |
| **Sandbox not configured** | Auto-configuration failed or has not completed |
Two actions are available depending on the current state:
* **Test Connection** — visible only when the Sandbox is *Ready*. Launches a manual validation of the reserved key.
* **Retry Auto-Configuration** — visible only when the Sandbox is *Not configured*. Re-runs the full provisioning process.
If you see **Sandbox not configured** after login, click **Retry Auto-Configuration**. If it still fails, go to the API Keys section and ensure at least one valid key exists, then retry.
***
## API keys
All API keys for your account are listed here — including the auto-provisioned Sandbox reserved key, which always appears pinned at the top.
| Column | Description |
| -------------- | ---------------------------------------------------------------------------------------------- |
| **Name** | The label assigned when the key was created. The Sandbox key is marked with a *Sandbox* badge. |
| **Created on** | Key creation date. |
| **Actions** | Delete the key. |
The account limit is **10 keys**. The **New API Key** button is disabled when the limit is reached.
### Creating a key
Click **New API Key** in the API Keys section header.
Give the key a descriptive name that identifies where it will be used — for example `iOS App`, `Zapier Integration`, or `Backend Staging`. The name is required.
Click **Create Key**. The key viewing modal opens automatically.
**This is the only time the full key value is shown.** Once you close this modal, it cannot be retrieved from the platform. Copy it now and store it somewhere safe.
1. Click the **eye** icon to reveal the key value.
2. Click **Copy key** to copy it to your clipboard.
3. Check **"I have copied and saved this key in a safe place."**
4. Click **Got it, I saved it** to close.
### Deleting a key
Click the trash icon on any key's row and confirm deletion. This action cannot be undone. If you delete the Sandbox reserved key, the Sandbox will show as **not configured** until auto-provisioning runs again.
***
## Team
This section is only visible to users with the **Admin** role. Members cannot see or manage team access.
Team management lets you invite collaborators to the platform under your account — without creating separate accounts. Each invited member gets their own login and access to all pipeline features.
The section header shows the current member count (`X / Y`). The **Add member** button is disabled when the account limit is reached.
### Inviting a member
Click **Add member**.
Type the email address of the person you want to invite. The email must be valid and not already registered.
Click **Send invitation**. The user receives an email and can log in immediately via OTP — no password setup required.
Members can access all transcription, configuration, evaluator, participant, and analytics features. They **cannot** add or remove other members, manage API keys, or see who else has access to the account.
### Revoking access
Click the trash icon on a member's row and confirm. Access is revoked immediately — the user will not be able to log in again until they are re-invited.
***
Learn about the Sandbox workflow and quota
How OTP login works
# Authentication
Source: https://docs.heify.com/sandbox/authentication
How to log in to the Heify Sandbox using email OTP.
The Sandbox uses a **passwordless, OTP-based** authentication flow. Enter your email, receive a 6-digit one-time code, and log in — no password required.
sandbox.heify.com
Access is restricted to accounts registered by an administrator. If you don't have access yet, use the **Don't have an account?** link on the login screen or [contact us](https://heify.com/contact).
***
## Login flow
Type your registered email address and accept the Terms & Conditions. Click **Send Code** to receive your one-time code.
The button is disabled while the request is in progress or while the T\&C checkbox is unchecked.
Check your inbox for a 6-digit numeric code. Enter each digit in the verification inputs — focus advances automatically as you type.
The code is valid for **3 minutes**. A live countdown timer is shown on screen.
You can paste the full 6-digit code directly into the input field — it will be distributed automatically across all boxes.
If the timer runs out before you verify, click **Resend code** to receive a new one. Use **Change email** to go back to Step 1.
After successful verification, the Sandbox loads your workspace and redirects you to the dashboard. Your API key is provisioned automatically in the background.
***
## OTP keyboard shortcuts
When entering the 6-digit code, the following keyboard interactions are supported:
| Action | Result |
| --------------------------- | --------------------------------------------- |
| Type a digit | Fills the box and moves focus to the next one |
| `Backspace` on an empty box | Moves focus to the previous box |
| `←` / `→` | Moves focus between boxes |
| Paste (`Ctrl+V` / `Cmd+V`) | Distributes clipboard digits across all boxes |
***
## Error reference
| Error | When it occurs |
| -------------------------------- | --------------------------------------------------------------------- |
| User not found | The email is not registered in the system |
| Error sending the code | Generic failure when requesting the OTP |
| Incorrect code | The entered code does not match the one sent |
| Too many attempts | Excessive failed attempts — the system resets to Step 1 automatically |
| Code expired | More than 3 minutes have passed since the code was sent |
| Error verifying the code | Generic failure when checking the OTP |
| Must accept Terms and Conditions | Submission attempted without checking the T\&C checkbox |
***
## After login
Once the code is verified:
1. Your session is established.
2. Your account and organization information is loaded.
3. The **Sandbox API key is auto-provisioned** — a brief *"Setting up the sandbox…"* notice may appear.
4. You are redirected to `/sandbox`.
***
## Language
A globe icon in the top-right corner of the login screen toggles the interface language between **English** and **Spanish**. The preference is saved in the browser and persists across sessions.
***
Learn about the Sandbox and its core workflow
Manage API keys and invite team members
# Configurations
Source: https://docs.heify.com/sandbox/configurations
Create and manage reusable configurations that define how audio is processed, summarized, and analyzed.
A **Configuration** is a reusable template that defines how the system processes an audio file: whether to generate an AI summary, which language to use, what custom vocabulary to apply, which data fields to extract from the transcript, and where to send webhook notifications on completion. Every transcription must be associated with a configuration.
For the full data model, see [Configuration](/core/configuration).
***
## Your configurations
The list view shows all your configurations with search, sort, and view controls. You can filter by **tag**, **configuration ID**, or **vocabulary words** in real time, toggle between a card grid and a table layout, and sort by creation date.
Each configuration displays **capability badges** at a glance:
| Badge | What it means |
| -------------- | -------------------------------------- |
| **AI Summary** | Summary generation is enabled |
| **Webhooks** | At least one webhook URL is configured |
| **N field(s)** | Number of extraction fields defined |
| **N word(s)** | Number of custom vocabulary words |
To delete multiple configurations at once, hover over any card or row to reveal its checkbox, select the items you want to remove, and click **Delete selection** in the action bar that appears.
***
## Creating a configuration
Click **New Configuration** to open the creation form.
### Tag *(required)*
A descriptive name for the configuration — for example `Customer Support`, `Sales Calls Q4`, or `Selection Interviews EN`. This name is shown throughout the platform.
The tag **cannot be changed after creation**. Choose a meaningful name before saving.
### AI Summary
Toggle to enable automatic transcript summarization. Off by default.
When enabled, two additional options appear:
* **Summary Language** — output language for the summary. Default: auto-detected from the audio. Supports 60+ languages.
* **Custom Summary Instructions** — optional textarea (max 300 characters) with specific directives for the AI summarizer.
Seven preset templates are available to fill the instructions field instantly:
| Preset | Best for |
| -------------------- | ---------------------------------------------------------------- |
| Selection interviews | Experience, strengths, weaknesses, hire/no-hire recommendation |
| Meetings & minutes | Attendees, topics, agreements, assigned tasks, next steps |
| Customer follow-up | Client status, issues, satisfaction, pending follow-up actions |
| Sales | Prospect needs, objections, value proposition, close probability |
| Team meetings | Decisions, blockers, task assignments, next meeting date |
| Training sessions | Topics covered, key concepts, questions, areas to reinforce |
| Custom | Blank — write your own instructions |
Selecting a preset fills the textarea with a ready-to-edit prompt. Customize it before saving.
### Analytics Language
Sets the output language for analysis reports (Configuration Analysis, Evaluator Analysis, Participant Analysis). Always enabled, regardless of the Summary toggle. Default: inferred from the audio.
### Custom Vocabulary
Type a word and press **Enter** to add it. Each word appears as a tag that can be removed individually. Use this for brand names, technical jargon, acronyms, or any term the transcription engine should recognize accurately.
Examples: `Kubernetes`, `OAuth2`, `GDPR`, `SaaS`, `B2B`.
### Webhook Notifications
Two independent, optional URLs:
| URL | When it fires |
| --------------- | ------------------------------------------------------------------ |
| **Success URL** | A `POST` request is sent when transcription completes successfully |
| **Error URL** | A `POST` request is sent when transcription fails |
Example webhook payloads
**Success**
```json theme={null}
{
"transcription_id": "f0e9d8c7-b6a5-4321-fedc-ba9876543210",
"status": "COMPLETED",
"configuration_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"duration": 125.5,
"completed_at": "2025-10-03T10:02:15.456Z"
}
```
**Error**
```json theme={null}
{
"transcription_id": "f0e9d8c7-b6a5-4321-fedc-ba9876543210",
"status": "FAILED",
"error": {
"message": "Unsupported audio format",
"code": 400
}
}
```
### Extraction Fields
Define structured data the AI will extract from each transcript. Up to **20 fields** per configuration.
Each field requires:
* **Name** — machine-readable key, e.g. `customer_name`, `sentiment`
* **Type** — see [field types](#extraction-field-types) below
* **Description for the AI** *(required, max 1000 chars)* — natural-language instruction telling the AI what to extract
#### Quick Actions
The right panel offers 8 pre-built templates you can add with a single click:
| Template | Field name | Type | What it extracts |
| --------------------- | -------------------------- | ------- | ------------------------------------------ |
| Sentiment Analysis | `sentiment_analysis` | String | `POSITIVE`, `NEGATIVE`, or `NEUTRAL` |
| Classification | `classification_tag` | String | A category from a list you define |
| Quality Score | `quality_score` | Number | 0–10 quality rating based on your criteria |
| Next Action | `next_action` | String | Recommended next step from a defined list |
| Feature Extraction | `feature_extraction` | Array | Features present from a defined list |
| Extract Specific Data | `specific_data_extraction` | String | Any custom data point you specify |
| Resolution | `first_call_resolution` | Boolean | Whether the customer's issue was resolved |
| Customer Satisfaction | `customer_satisfaction` | Boolean | Whether the customer was satisfied |
Quick Action descriptions contain **placeholders in brackets** — for example `[CATEGORY 1]`, `[YOUR CRITERIA]`. **Replace them with your actual values before saving**, or the AI will produce inconsistent results.
#### Writing good descriptions
**Use bounded responses.** Define a fixed set of valid values so the AI's output is consistent and easy to process downstream.
Good: `"Classify the sentiment. Must be one of: POSITIVE, NEGATIVE, or NEUTRAL."`
Poor: `"What is the sentiment?"`
**Provide context and examples.** The clearer your instructions, the more accurate the results.
Good: `"Extract the customer's order number. Usually a 6–8 digit code starting with 'ORD-'. Examples: ORD-123456, ORD-78945."`
Poor: `"Get the order number."`
***
## Extraction field types
| Type | Use for |
| --------- | ------------------------------------------ |
| `string` | Free text, short answers, named categories |
| `number` | Scores, counts, numeric ratings |
| `boolean` | Yes/no, true/false outcomes |
| `array` | Lists of items, multiple selections |
| `date` | Dates and timestamps |
| `object` | Nested structured data |
***
## Viewing and editing
Click any configuration in the list to open its detail view. It shows five sections: **General Information** (ID with copy button), **AI Summary & Analysis**, **Custom Vocabulary**, **Extraction Fields**, and **Webhooks** (URLs are hidden by default — click **Show URLs** to reveal them).
Click **Edit** to enter edit mode. You can update:
* AI Summary toggle, languages, and custom instructions
* Custom Vocabulary (add or remove words)
* Extraction Fields (edit descriptions, add new fields, remove existing ones)
* Webhook URLs
**Tag** is read-only in edit mode and cannot be changed after creation.
Use the sticky **Save changes** bar at the bottom to confirm, or **Cancel** to discard all edits.
To delete a configuration, use the trash icon in the detail view header. **This cannot be undone.** Deleting a configuration does not affect transcriptions that were already processed with it.
***
## Cloning a configuration
Click the **Clone** button (copy icon, in the detail view header alongside Edit and Delete) to duplicate an existing configuration.
You are taken directly to the creation form with all fields pre-filled:
| Field | Pre-filled value |
| --------------------------------- | -------------------------------------------------------------------------- |
| **Tag** | Original tag + `(Copy)` suffix — truncated if over the 100-character limit |
| **AI Summary** toggle | Copied from original |
| **Summary & Analytics Languages** | Copied from original |
| **Custom Summary Instructions** | Copied from original — the `Custom` preset is automatically selected |
| **Custom Vocabulary** | All words copied |
| **Extraction Fields** | All fields copied with their names, types, and descriptions |
| **Webhooks** | Copied from original |
The clone is a new draft — nothing is saved until you click **Create Configuration**. Edit any field before saving, or save immediately to create an exact copy.
***
Full data model and field reference
Submit audio using a configuration
# Evaluators
Source: https://docs.heify.com/sandbox/evaluators
Create and manage reusable quality-control rubrics that the AI uses to score transcriptions.
An **Evaluator** is a reusable quality-control template that defines the criteria the AI uses to audit a call transcript. You specify a set of scored criteria, assign weights, and the AI produces a structured pass/fail report for every transcription associated with this evaluator.
For the full data model and criteria reference, see [Evaluator](/core/evaluator).
***
## Your evaluators
Search, filter, and sort in real time by **name**, **description**, or **evaluator ID**. Sort by name or creation date (ascending/descending). Toggle between a card grid and a table layout.
Each evaluator displays **capability badges** at a glance:
| Badge | What it means |
| -------------- | -------------------------------------------------------- |
| Language name | Feedback language configured for the evaluator |
| **N Criteria** | Total number of evaluation criteria defined |
| **Critical** | Shown only when at least one Strict criterion is defined |
To delete multiple evaluators at once, hover over any card or row to reveal its checkbox, select the items you want to remove, and click **Delete selection** in the action bar that appears. The table header includes a **Select all** checkbox for the current filtered results.
Single-item deletion is done from the detail view, not from the list.
***
## Creating an evaluator
Click **New Evaluator** to open the creation form.
### General parameters
| Field | Required | Limit | Notes |
| --------------------- | -------- | --------- | -------------------------------------------------------------------------------- |
| **Evaluator Name** | Yes | 100 chars | Human-readable label — e.g. `Outbound Sales Audit 2025` |
| **Feedback Language** | No | — | Language the AI writes feedback in. Default: inferred from audio. 60+ languages. |
| **Description** | No | 250 chars | Short summary of the evaluator's purpose |
The **Name** cannot be changed after creation. Choose a meaningful, descriptive name before saving.
### Evaluation context *(optional)*
Provides situational context so the AI understands who is being evaluated and under what circumstances. Max 1000 characters.
Six preset templates are available to fill the field instantly:
| Preset | Context provided to the AI |
| -------------------- | -------------------------------------------------------------------------- |
| Call center | Support agent on an inbound call following internal protocols |
| Job interview | Candidate being assessed for role suitability and communication clarity |
| Training or coaching | Participant in a training session; comprehension and engagement |
| Sales meeting | Salesperson on a sales call; needs identification and objection handling |
| Customer follow-up | Account manager in a follow-up meeting; relationship quality and proposals |
| Custom | Blank — write your own |
Selecting a preset fills the textarea with a ready-to-edit prompt. Selecting **Custom** clears it.
### Evaluation criteria
Define up to **10 criteria**. A `N / 10 criteria used` counter is shown at the top of the section.
Each criterion requires:
* **Name** *(required, max 100 chars)* — human-readable label, e.g. `Corporate Greeting`
* **Type** — how the AI scores this criterion (see [Criteria types](#criteria-types))
* **Weight** *(0–100%)* — contribution to the final score. Strict criteria are always 0%
* **AI Instructions** *(required, max 2000 chars)* — natural-language description of exactly what to look for in the transcript
Weights across all non-Strict criteria **must sum to exactly 100%** before the evaluator can be saved. Use the **Balance Weights** button in the sticky bottom bar to distribute them evenly with a single click.
### Suggested Criteria panel
The right panel offers 8 pre-built criteria you can add with a single click:
| Criteria | Type | Default weight | What it evaluates |
| --------------------- | ------- | -------------- | --------------------------------------------------------------------- |
| Corporate Greeting | Boolean | 10% | Name + company + welcome at call start |
| Identity Verification | Strict | 0% | Two personal data points verified before sensitive info is shared |
| Empathy | Scale | 20% | Acknowledgement phrases used; no condescending tone |
| Active Listening | Scale | 15% | No interruptions; paraphrases key points; asks clarifying questions |
| Resolution | Boolean | 25% | Concrete solution offered or escalation with a defined next step |
| Objection Handling | Scale | 15% | Objections answered with data; proposal adapted to client needs |
| Sales Close | Boolean | 15% | Explicit close attempt made (e.g. "Shall we proceed?") |
| Formal Farewell | Boolean | 0% | Actions summarized; further help offered; waits for client to hang up |
The 7 non-Strict suggested criteria already sum to **100%** — they form a complete, immediately usable evaluator without any manual weight adjustment.
Once a criterion has been added, its button shows a checkmark and "Already added". It cannot be added twice.
### Quality tips
Describe concrete, verifiable actions the AI can detect in the transcript. Avoid subjective criteria.
**Poor:** `"Be friendly."`
**Good:** `"Greet the client by name and thank them for the call."`
Include negative examples in the AI instructions to detect violations and reduce false positives.
Example: *"The agent must not interrupt the client while they are speaking."*
Include expected phrases, scripts, or specific business situations the AI should recognize.
Example: *"The agent must mention the Premium Plus plan (\$29.99/month) and at least two of its core benefits."*
Split complex criteria into simpler, focused ones. Each criterion should evaluate a single observable behavior.
**Poor:** `"Greeted AND verified identity AND offered a solution."`
**Good:** Three separate criteria — one for each action.
***
## Criteria types
| Type | How the AI scores | Weight |
| ----------- | ------------------------------------------------------ | --------------------------------- |
| **Boolean** | Pass or Fail | Contributes to score via weight % |
| **Scale** | 1–5 based on degree of compliance | Contributes to score via weight % |
| **Strict** | Pass or Fail — failure **fails the entire evaluation** | Always 0% |
**Strict criteria are automatic disqualifiers.** A single failed Strict criterion marks the entire evaluation as failed, regardless of how well all other criteria were met. Use them only for non-negotiable compliance requirements — such as identity verification or required legal disclosures.
***
## Viewing and editing
Click any evaluator in the list to open its detail view.
**Read mode** shows three sections:
* **General Information** — name, feedback language, description, evaluation context, creation date
* **Critical Criteria** *(shown only when Strict criteria exist)* — red-bordered card listing each Strict criterion with its name and description
* **Evaluation Criteria** — each Boolean and Scale criterion with its name, type badge, weight, and AI instructions
Click **Edit** to enter edit mode (amber borders and header). You can update:
* Feedback language, description, and evaluation context
* Criteria: edit AI instructions, type, and weight; add new criteria; remove existing ones
**Name** is read-only in edit mode and cannot be changed after creation.
Use the sticky **Save changes** bar at the bottom to confirm, or **Cancel** to discard all edits. The bar also shows the current weight status and the **Balance Weights** button.
To delete an evaluator, use the trash icon in the detail view header. **This cannot be undone.** Deleting an evaluator does not affect transcriptions that were already processed with it.
***
## Cloning an evaluator
Click the **Clone** button (copy icon, in the detail view header alongside Edit and Delete) to duplicate an existing evaluator.
You are taken directly to the creation form with all fields pre-filled:
| Field | Pre-filled value |
| ---------------------- | --------------------------------------------------------------------------- |
| **Name** | Original name + `(Copy)` suffix — truncated if over the 100-character limit |
| **Feedback Language** | Copied from original |
| **Description** | Copied from original |
| **Evaluation Context** | Copied from original |
| **Criteria** | All criteria copied — names, types, weights, and AI instructions |
The clone is a new draft — nothing is saved until you click **Create Evaluator**. Edit any field before saving, or save immediately to create an exact copy.
***
Full data model and criteria reference
View performance insights for this evaluator
# Sandbox
Source: https://docs.heify.com/sandbox/index
A fully browser-based workspace for building transcription pipelines, managing evaluations, and analyzing results — without writing any code.
The Heify Sandbox is a personal, isolated workspace. Each authenticated user gets their own instance — a self-contained environment where you can build transcription pipelines, manage quality evaluation criteria, upload audio, and explore performance analytics without affecting any other user or any production system.
No code required. The Sandbox provides full access to Heify's pipeline — from configuration to analytics — entirely through the browser.
***
## Getting started
Access to the Sandbox requires an account. Login is **OTP-based**: enter your email, receive a one-time code, and log in — no password required.
After login you are redirected to the main dashboard. See [Authentication](/sandbox/authentication) for the full flow.
When the Sandbox loads for the first time, it automatically **provisions a reserved API key** on your behalf. This key is validated against the Heify API and stored in the browser's local storage.
A *"Setting up the sandbox…"* notice appears during provisioning. On success, a *"Sandbox ready"* notification is shown. If automatic provisioning fails, you are directed to the [API Keys & Sandbox](/sandbox/api-keys) page to configure the connection manually.
The Sandbox is considered **ready** when two conditions are both true:
* An API key is stored in the browser.
* That key has been validated against the Heify API.
If the Sandbox is not ready, a **SandboxSetupWarning** banner appears at the top of affected pages with a *Configure* button. Transcription, configurations, evaluators, and analytics features are disabled until setup is complete.
***
## Navigation
The Sandbox is organized into seven areas accessible from the collapsible sidebar on desktop and a bottom navigation bar on mobile.
Overview of the pipeline, summary counts, and setup status.
Create and manage transcription configurations — the foundation of every pipeline.
Define AI quality criteria to score transcriptions automatically.
Register the people who appear in the audio — agents, candidates, salespeople, or any individual.
Upload audio files or submit URLs for transcription.
View, filter, and inspect all processed transcriptions and their results.
AI-powered analytics dashboards by Configuration, Evaluator, or Participant.
The desktop sidebar also provides quick links to the **AI Assistant**, **Docs**, and **Support**. The user menu at the bottom shows your role, remaining transcription minutes, and analytics quota counts. A **dark/light mode toggle** (sun/moon pill) is also available in the sidebar footer — your preference is saved automatically.
***
## Core workflow
A typical Heify pipeline follows this sequence:
Every transcription must be associated with a [Configuration](/sandbox/configurations). It defines what features are enabled (summaries, field extraction, conversation output) and which evaluator to apply.
[Evaluators](/sandbox/evaluators) add a quality scoring layer — a set of criteria applied to transcriptions to produce a score and flag critical failures. [Participants](/sandbox/participants) identify the individuals in the recordings — support agents, job candidates, salespeople — enabling per-person performance tracking across analytics dashboards.
Go to [Transcribe](/sandbox/transcribe) to upload files or paste a public URL. Associate the transcription with a configuration and optionally link an evaluator and participant.
Open any transcription in [Transcriptions](/sandbox/transcriptions) to view the full transcript, AI summary, extracted fields, evaluation score, and criterion-level breakdown.
Use the [Analysis](/sandbox/analytics) dashboards to discover patterns across all transcriptions — by configuration, evaluator, or participant.
***
## Quota
The Sandbox operates under two types of quota:
Total audio duration that can be processed. Consumed when a transcription completes successfully.
Number of analysis reports that can be generated per module (configurations, evaluators, participants). Consumed on every request, even if no data is returned.
Remaining quota is shown in the sidebar user menu. When an analytics quota is exhausted, the **Generate Analysis** button is disabled on the corresponding dashboard.
***
## API keys and team
All API keys are managed from the [API Keys & Sandbox](/sandbox/api-keys) page. The auto-provisioned reserved key appears pinned at the top of the list. You can also create additional keys with custom names — up to **10 keys** per account.
Team members can be invited from the same page. Each member gets their own access to the Sandbox under the same account.
| Role | Capabilities |
| ---------- | ---------------------------------------------------------------------- |
| **Admin** | Full access: pipeline, analytics, API key management, team management |
| **Member** | Pipeline and analytics access. Cannot manage API keys or team settings |
# Participants
Source: https://docs.heify.com/sandbox/participants
Register and manage the people linked to your transcriptions and track their performance over time.
A **Participant** is a named individual linked to transcription jobs — a support agent, job candidate being interviewed, salesperson, or any person whose performance you want to track. Attaching a participant to a transcription lets the platform attribute quality metrics to that individual and track their performance over time.
For the full data model, see [Participant](/core/participant).
Unlike Configurations and Evaluators, there are no separate create or detail pages. All creation and editing happens in a **side drawer** that slides in from the right without leaving the list.
***
## Your participants
Search, sort, and filter in real time by **name** or **participant ID**. Sort by name or last updated date (ascending/descending). Toggle between card grid and table layout.
### Metadata filters
Click the **sliders icon** (to the right of the search bar) to expand the metadata filter panel. The panel generates filter dropdowns **dynamically** from the actual metadata keys present in your participants — no pre-configuration needed.
Each dropdown shows all values found for that key across your participants. Selecting a value narrows results to participants that have that exact value for that key.
| Behavior | Detail |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Active filter badge** | A number bubble on the sliders icon shows how many filters are currently active — no need to open the panel to check |
| **AND logic** | All filters are combined as an intersection — results must match every active filter simultaneously |
| **Cross-filter with search** | Text search and metadata filters apply together — type a name and set a role filter at the same time |
| **Clear filters** | A *"Clear filters"* link appears in the panel header when any filter is active |
| **No results state** | When no participants match the active combination, a *"No results"* message includes a button to clear all filters and the search text at once |
Each participant has a **gradient avatar** showing their initials. The color is determined by their name — the same name always produces the same gradient.
Metadata is shown as **pills** directly on each card: grid view shows the first 4 entries, table view shows the first 2, with a **+N more** badge when there are additional fields.
To delete participants, hover over a card or row to reveal a **checkbox** in place of the avatar. Select one or more items, then click **Delete** in the blue action bar that appears. A confirmation modal is shown before deleting.
Single-item deletion works the same way — select its checkbox and use the **Delete** button. There is no delete option inside a drawer.
***
## Creating a participant
Click **New Participant** to open the drawer.
### Name *(required)*
The participant's display name — their label throughout the platform. As you type, a live **gradient avatar preview** updates in real time showing the initials and color that will be assigned to this participant.
### Metadata *(optional)*
Structured key-value pairs that describe the participant. Max **10 fields**, shown with a `N / 10` counter.
Use the preset quick-add chips to add common fields instantly:
| Chip | Key | Example value |
| ----------- | ------------- | ------------------------------------------------- |
| Email | `email` | `agent@company.com`, `candidate@example.com` |
| Role | `role` | `Supervisor`, `Agent`, `Candidate`, `Interviewer` |
| Phone | `phone` | `+1 600 000 000` |
| Department | `department` | `Customer Support` |
| Region | `region` | `New York`, `London` |
| Language | `language` | `EN`, `ES`, `FR` |
| Timezone | `timezone` | `UTC+1` |
| Skills | `skills` | `SQL`, `Customer Service` |
| Employee ID | `employee_id` | `EMP-001` |
Clicking a chip adds a row with the key pre-filled. A chip is disabled once that key is already in the form. For any field not listed above, click **+ Add custom field** to add a blank row.
Keys must be unique within a participant. Rows left completely empty are silently ignored on save.
Click **Create Participant** in the sticky footer to save. The drawer closes automatically on success.
***
## Editing a participant
Click any card or table row to open the **Edit Drawer**. The drawer header shows the participant's ID with a copy button.
The same name and metadata fields are available. The participant **name can be changed** after creation.
Updating metadata replaces the **entire** metadata object — keys not included in the update will be lost. To preserve existing fields, include them in the update. See [Participant — metadata](/core/participant#metadata).
Click **Save Changes** in the sticky footer to confirm, or close the drawer to discard.
***
## Metadata keys reference
| Key | Common use |
| ------------- | ------------------------------------------------- |
| `email` | `agent@company.com`, `candidate@example.com` |
| `role` | `Supervisor`, `Agent`, `Candidate`, `Interviewer` |
| `phone` | `+1 600 000 000` |
| `department` | `Customer Support` |
| `region` | `New York` |
| `language` | `EN`, `ES` |
| `timezone` | `UTC+1` |
| `skills` | `SQL, Customer Service` |
| `employee_id` | `EMP-001` |
Any key not listed above is stored normally and displayed with a generic icon.
***
Full data model and metadata reference
View performance insights for this participant
# Transcribe
Source: https://docs.heify.com/sandbox/transcribe
Submit audio and video files for transcription and AI quality scoring.
The Transcribe page is where you submit audio or video content for processing. Select a **Configuration** (required) that defines how the content will be handled, optionally attach an **Evaluator** to score the transcript against quality criteria, optionally link a **Participant** to attribute the result to a specific person, then provide the media as a local file upload or a public URL.
***
## Transcription parameters
The three parameters are arranged as a tree to communicate their relationship:
```mermaid theme={null}
flowchart TD
A[Configuration\nRequired]
B[Evaluator\nOptional]
C[Participant\nOptional]
A --> B
A --> C
```
### Configuration *(required)*
A searchable dropdown listing all your configurations. Selecting one associates all of its settings — language, summary, extraction fields, and webhooks — with this transcription job. The collapsed selector displays the configuration's **name**, truncated if long (it falls back to the raw ID while the list is loading or if the configuration has no name).
Don't have a configuration yet? [Create one first →](/sandbox/configurations)
### Evaluator *(optional)*
Opens a selector modal listing your evaluators with name, ID, and criteria count. Selecting one causes the AI to score the transcript against that evaluator's criteria once processing completes.
### Participant *(optional)*
Opens a selector modal listing your participants with name, ID, and department (if set). Selecting one links the transcription result to that person for analytics and performance reporting.
Selecting both an **Evaluator** and a **Participant** gives you quality scores attributed to a specific person — the foundation of individual performance tracking.
### Pinned defaults
If you upload batches all day with the same setup, you can **pin** your Configuration, Evaluator, and Participant selections as defaults for future uploads. Each of the three cards has a small pin icon at the far right of its header, after the REQUIRED/OPTIONAL pill — filled and tinted (blue / violet / emerald) when pinned, grey outline when not.
| Aspect | Behavior |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Pinning** | Select a value first — the pin is disabled while the field is empty — then click it (*"Set as default for future uploads"*). Click a lit pin to remove that default (*"Remove default"*). |
| **When it applies** | On page load, and **only into fields that are still empty** — a default never overwrites a selection you've already made. |
| **Never blocking** | A pinned value is a starting point, not a lock. Change it freely for any single upload. |
| **Where it's stored** | Your browser's `localStorage` — per browser and per device. It survives reloads but is **not synced to your account**: pinning on your laptop does not pin on your phone. |
The three pins are independent, and the [recording date](#recording-date) is not pinnable — a date is per-batch by nature.
Defaults are **opt-in and never imposed**: nothing is pinned until you pin it, and fields start empty for every user. This matters if several departments share one account — no configuration is ever guessed for you.
The pin renders as "pinned" only while the field's current value equals the saved default. After changing the selection the pin looks un-pinned even though the old default is still saved — clicking it then **overwrites** the default with the new value.
***
## Upload method
A toggle below the parameter selectors switches between two modes:
| Mode | Use when |
| ----------------- | ------------------------------------------------------- |
| **Local File(s)** | You have files on your device to upload |
| **Public URL** | The file is already hosted at a publicly accessible URL |
***
## Local File(s) mode
### Drop zone
Drag & drop files onto the zone or click anywhere on it to open a file picker.
**Supported formats:** `aac` · `aiff` · `amr` · `asf` · `flac` · `mp3` · `ogg` · `wav` · `webm` · `m4a` · `mp4`
For file size and duration limits, see [Rate Limits & Quotas](/platform/rate-limits).
Files with unsupported formats are automatically filtered out and a warning shows how many were rejected. You can add up to **25 files** per submission.
### Processing Queue
The queue appears as soon as files are added. Each item shows:
| Element | Description |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Status icon** | Queued · Uploading · Success · Error |
| **File name & size** | Truncated filename and size in MB |
| **Name** | Optional free-text label for the transcription (e.g. `Q3 team meeting`) |
| **Recording date** | Optional date the call was recorded — see [Recording date](#recording-date). Only shown when **"Same date for all"** is off. |
| **Recording time** | Optional. Enabled once a recording date is set. |
| **Status pill** | **Queued** · **Uploading...** · **Started** · **Error** |
| **Transcription ID** | Shown after success — monospace with copy button and link to the detail view |
Files are processed **sequentially** with a short pause between each. This is intentional and prevents API rate issues — parallel uploads are not supported.
Re-submitting a batch only retries files in **Queued** or **Error** state. Files that already completed successfully are not re-processed.
***
## Recording date
Both upload modes let you optionally state **when the recording actually happened**. Heify's dashboards group everything by the transcription's creation date — the timeline, the monthly comparison, the criteria evolution, the heatmap, and the analytics [period filter](/sandbox/analytics-evaluator#period-filter) — so a batch of April calls uploaded in June would otherwise all report as June. Setting the recording date fixes all of that at once.
Leaving it empty is the previous behavior: the transcription is stamped with the **upload** time. All date inputs are capped at today — a recording cannot be in the future.
### In Local File(s) mode
A bordered card — calendar icon · **Recording date** · an **Optional** pill — sits directly below the drop zone, above the Processing Queue (it appears even before any file is added). It contains a **"Same date for all"** switch, off by default:
| Switch | Where the date lives |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **OFF** *(default)* | Each file in the queue gets its own date + time inputs below its Name field, both starting empty — for batches spanning several days. |
| **ON** | A single date + time pair inside the card applies to every file in the upload. The per-file inputs disappear; the date is pre-filled with today, the time starts empty. |
Toggling the switch on and back off does not wipe the per-file values.
### In Public URL mode
No switch — a URL submission is a single audio, so there is nothing to apply "to all". A labelled field block below **Transcription Name** offers a date input and a narrower time input side by side.
### The time field
The time is a separate, optional input in all three places — leaving it empty is a fully supported end state, not an incomplete form.
| Behavior | Detail |
| ----------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Disabled until its date is set** | There is no time-without-date; clearing the date also clears the time. |
| **Capped to "now" — only on today** | If the chosen date is today, the time can't be in the future. On a past date, any time is allowed. |
**Why supply a time at all?** It feeds the hour axis of the [Evaluator Analysis heatmap](/sandbox/analytics-evaluator#weekly-heatmap): date-only calls are stored at midnight UTC and all pile into the midnight column. Everything else — month, timeline, evolution, comparison, date filters, weekday — is correct either way. Add the time when you know it; the date-only form is the normal choice for historical batches.
### How dates are stored
| What you fill | What is sent | Stored as |
| ------------- | -------------------------------- | ------------------------------------ |
| Nothing | *field omitted* | Upload timestamp (previous behavior) |
| Date only | The raw date, **not converted** | That day at `00:00:00` UTC |
| Date + time | Your local time converted to UTC | The exact instant chosen |
A date-only value is deliberately sent unconverted: converting local midnight to UTC would shift the recording into the previous day for users east of Greenwich — and therefore into the previous reporting month. Sending the bare date preserves the calendar day for every user.
Because a date-only upload is stored at midnight UTC and timestamps are displayed in your local timezone, a call uploaded as "1 July" shows as `01/07/2026, 02:00` in Spain (UTC+2 in summer) — the date is right, the `02:00` is just midnight UTC rendered locally. In a negative-offset timezone the same value renders on the **previous day** (`30/06/2026, 20:00` in New York). The stored data and all analytics grouping are still correct; supply the actual time if the display bothers you.
The recording date **cannot be changed after submission**. If a recording was submitted with the wrong date, delete the transcription and submit the audio again with the correct date.
***
## Public URL mode
| Field | Required | Notes |
| ------------------------- | -------- | --------------------------------------------------------------------------------------- |
| **File URL** | Yes | Direct public link to the audio/video file (e.g. `https://my-server.com/recording.mp3`) |
| **Transcription Name** | No | Optional human-readable label |
| **Recording date / time** | No | When the recording was made — see [Recording date](#recording-date) |
After successful submission, a result card appears showing the transcription ID with a copy button and a link to the detail view.
The URL must be **publicly accessible** with no authentication required. The file must be reachable at the time the pipeline processes it — the download has a **60-second timeout**. Use a stable, fast URL.
***
## After submission
Once a batch finishes or a URL submission succeeds, the submit button changes to **Process More Files**. Clicking it clears the file queue (or URL fields) so you can start a new batch — your **Configuration, Evaluator, and Participant selections are retained** (and [pinned defaults](#pinned-defaults) re-apply them on future visits).
In Public URL mode, **Process More Files** also clears the recording date and time. In Local File(s) mode, the global date and the "Same date for all" switch are **not** reset — convenient when the next batch is from the same day.
***
## Best practices
Better audio quality produces more accurate transcriptions.
**Recommended:**
* Minimize background noise
* Use quality recording equipment
* Keep audio levels consistent — not too quiet, not too loud
* 16 kHz+ sample rate (higher is better)
* Mono or stereo both work; mono files are smaller
**Avoid:**
* Heavy background music or ambient noise
* Multiple speakers talking over each other
* Very low bitrate compression
* Heavily processed audio with effects
Before submitting a URL, verify that:
* The URL opens directly in an **incognito browser window** (no login required)
* It is a **direct link to the file**, not a media player page
* The file format is one of the supported extensions
* The URL uses **HTTPS** (HTTP also accepted)
* The server can respond within **60 seconds**
You can test the URL quickly with:
```bash theme={null}
curl -I https://your-url.com/audio.mp3
```
Expect a `200 OK` response with `Content-Type: audio/...`.
***
Set up your processing templates
Create quality scoring rubrics
Manage agent profiles
# Transcriptions
Source: https://docs.heify.com/sandbox/transcriptions
Browse and manage all your transcription jobs, then dive into the full results of any individual transcription.
This section covers two connected views: the **Transcriptions list** where you manage all your jobs, and the **Detail view** where you inspect the full results of a single transcription — transcript, AI summary, quality audit, and extracted fields.
Transcriptions are kept for **one year from upload**, then deleted automatically and permanently. Use **Export** (or the API) to keep anything you need longer — see [Data Retention](/platform/data-retention).
***
## Part 1 — Transcriptions list
### Header
| Button | What it does |
| --------------------- | ---------------------------------------------------------------------------------- |
| **Export** | Downloads the currently filtered list as a CSV file |
| **Filters** | Toggles the advanced filter panel — a blue badge shows how many filters are active |
| **Refresh** | Reloads the list from the API |
| **New Transcription** | Opens the [Transcribe](/sandbox/transcribe) page |
### Group tabs
A tab bar below the header lets you filter by group with a single click:
| Tab | Color |
| ------------------ | ------ |
| **All** | Slate |
| **No group** | Grey |
| **Pending Review** | Amber |
| **Under Review** | Blue |
| **Archived** | Purple |
Each tab shows a count badge reflecting how many transcriptions are in that group. See [Groups & workflow](#groups--workflow) for guidance on using groups to organize your review process.
### Filters panel
Click **Filters** to slide open the advanced panel.
| Filter | Options |
| -------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **Search** | Free-text match on ID, name, or configuration tag. Autocomplete shows up to 5 suggestions after 2+ characters. |
| **Status** | All · Completed · In Progress · Failed |
| **Configuration** | Searchable selector |
| **Participant** | Limited to participants present in loaded results |
| **Evaluator** | Limited to evaluators present in loaded results |
| **Critical Failures Only** | Shows only transcriptions where a Strict criterion failed |
| **Date range** | From / To — quick presets: Today · This week · This month · This quarter |
Active filters are synced to the URL query string. Copy the browser URL to share or bookmark an exact filtered view.
### Grid and list view
Toggle between a card grid and a resizable table. Both views show:
* **Status badge** — Completed (green) · In Progress (blue, spinning) · Failed (red)
* **Quality score** — `X.X / 100`, green when ≥ 50, red when \< 50. A red octagon icon appears if a Critical Failure occurred.
* **Group badge** — with an inline pencil button to change the group without opening the detail view
* **Configuration tag** — clickable, opens configuration detail in a new tab
* **Evaluator tag** — clickable (if linked), opens evaluator detail in a new tab
* **Participant tag** — shown if a participant was linked
The **table layout** has resizable columns — drag the thin handle at the right edge of any column header to resize it.
### Bulk actions
Hover over any card or row to reveal a checkbox. Selecting at least one item shows a blue action bar:
| Action | What it does |
| ---------------- | ----------------------------------------------------------- |
| **Change group** | Applies a new group to all selected transcriptions at once |
| **Delete** | Deletes all selected transcriptions (confirmation required) |
The table header includes a **select all** checkbox for the current page.
### Export CSV
The **Export** button downloads the currently filtered list as `transcriptions_YYYY-MM-DD.csv`.
CSV columns: `ID` · `Name` · `Configuration` · `Status` · `Group` · `Duration (s)` · `Date`
***
## Part 2 — Transcription detail
Click any transcription to open the detail view. You can also load any transcription directly by entering its ID in the search bar at the top of the page.
### Header
| Element | Behavior |
| ---------------------- | ----------------------------------------------------------------------- |
| **Transcription name** | Primary title — shows the ID if no name was given |
| **Copy ID** | Copies the transcription ID to clipboard |
| **Export PDF** | Downloads a quality report. Only shown when a Quality Audit is present. |
| **Delete** (trash) | Opens the delete confirmation modal |
### Metadata
A two-column grid showing: Status · Group (editable via pencil button) · Duration · Creation Date · Language detected · Number of speakers. The Configuration tag is shown below with a link that opens its detail in a new tab.
The **Applied Modules** tree mirrors the Transcribe form and shows which Configuration, Evaluator, and Participant were applied to this job. Each applied node is clickable. Nodes that were not used show "Not applied."
```mermaid theme={null}
flowchart TD
A[Configuration]
B[Evaluator]
C[Participant]
A --> B
A --> C
```
### AI Summary
A collapsible card with the AI-generated summary rendered as formatted markdown (headings, bullets, bold text). A **Copy summary** button copies the raw text.
Hidden if the configuration did not have summary enabled.
### Quality Audit
A collapsible card. Only shown when an evaluator was applied and the audit has completed.
#### Score and KPIs
A circle gauge displays the overall score (0–100). Ring color reflects performance:
| Score | Color |
| ----- | ----- |
| ≥ 80 | Green |
| 50–79 | Amber |
| \< 50 | Red |
Three chips summarize the audit results:
| Chip | Meaning |
| ------------ | ---------------------------------------------------------------- |
| **Passed** | Number of criteria scored as a pass |
| **Critical** | Strict criteria that failed — triggers automatic overall failure |
| **Failures** | Weighted (non-strict) criteria that failed |
#### Criteria Breakdown
All criteria are listed as expandable rows showing: sequential number, criterion name, type badge (`Yes/No` · `Scale` · `Critical`), pass/fail icon, and weight %.
**Focus Mode** toggle — when enabled, the list shows only **failed** criteria.
Use Focus Mode to zero in on improvement areas without scrolling through all passing criteria.
Expand any criterion to see:
| Section | Content |
| --------------------------- | ------------------------------------------------------------- |
| **Reasoning** | The AI's explanation of why this criterion passed or failed |
| **Quote** | The exact phrase from the transcript the AI cited as evidence |
| **Improvement Opportunity** | Specific coaching advice for the evaluated participant |
The **Quote** section has a **Search in transcription** button. Clicking it scrolls the page down to the matching conversation segment and highlights it with a ring. A floating **Back** button appears at the bottom-right corner to return you to the audit section.
### Extracted Fields
A collapsible card showing all structured data the AI extracted. Only shown if the configuration had extraction fields enabled and values were found.
Each field shows its name and value — arrays as pill tags, booleans as **Yes** / **No** badges, text as plain string — with a per-field copy button.
### Conversation
A collapsible card showing the full transcript as conversation segments. Only shown if the transcription produced speaker-diarized output.
* **Search** — type to filter visible segments and highlight matching terms in yellow
* Each segment shows: a colored speaker avatar (consistent per speaker), speaker label, timestamp range (`MM:SS – MM:SS`), and transcript text
Clicking **Search in transcription** from a Quality Audit criterion scrolls directly to the relevant segment and highlights it.
### PDF Export
The **Export PDF** button (header) downloads `Report_[name]_[date].pdf` containing:
* Metadata: file name · participant · evaluator · evaluation date
* Score summary: overall score · passed / critical / failed counts
* Executive feedback
* Criteria breakdown table: Criteria · Status · Points · Feedback · Quote · Advice
**Export PDF** is only available when a Quality Audit is present. Transcriptions without an evaluator do not have this option.
### What can and cannot be changed
Transcription results are **read-only** after processing. Only the **name**, **group**, and **deletion** are mutable from these views.
Deleting a transcription is **permanent** and removes all associated results — AI summary, extracted fields, audit data, and conversation segments.
***
## Groups & workflow
Groups give your team a shared signal for where each transcription stands in the review workflow.
| Group | Suggested use |
| ------------------ | --------------------------------------------------------------------------------------------------- |
| **No group** | Default — newly processed transcriptions that haven't been triaged yet |
| **Pending Review** | Flagged for manual inspection — quality check needed, score seems off, or a Strict criterion failed |
| **Under Review** | Actively being reviewed by a team member |
| **Archived** | Review complete — data extracted, no further action needed |
**Benefits:** clear visibility of work status, easy handoffs between team members, and a reliable way to track progress over time.
A clean list makes it easier to find what matters.
**Suggested cadence:**
* **Weekly** — archive completed work
* **Monthly** — delete test transcriptions and resolved failures
* **Quarterly** — export important batches as CSV for backup
**Good candidates for deletion:** test uploads, duplicate submissions, failed jobs after root cause is identified, and outdated data no longer needed for reporting.
***
Submit a new transcription
Full data model reference