Base URL https://heartotext.com/api/public/v1. Every request needs Authorization: Bearer h2t_live_… unless the operation says otherwise. This page is generated from the OpenAPI 3.1 document, which is the same file our contract test validates real responses against — if something here is wrong, the build is red.
GET/me
The calling key, its plan and its limits
A projection of the account's entitlements. The numbers here are the ones that actually bind a request -- the same ceilings the apps are held to -- so a client can decide whether to send a recording before sending it.
| Status | Meaning |
|---|---|
200 | The account behind this key. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
GET/languages
The language codes a create accepts
ISO codes only. Numeric identifiers never cross this boundary, in either direction, so a row being renumbered in our database cannot break your integration.
| Status | Meaning |
|---|---|
200 | Every language this API accepts. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
GET/openapi.json
This document, as JSON
Unauthenticated -- the description of the API is not a secret, and a code generator should not need a key to read it.
| Status | Meaning |
|---|---|
200 | The OpenAPI 3.1 description of this API. |
GET/postman.json
This API as a Postman collection
The same endpoints as a Postman v2.1 collection, generated from this document. Unauthenticated, for the same reason the document is.
| Status | Meaning |
|---|---|
200 | A Postman v2.1 collection. |
POST/transcriptions
Start a recording
Send either a `file` (multipart) or a `url` (JSON or multipart), never both-or-neither. The recording is queued immediately and the response describes it in its `queued` state; poll it until it is `completed`. Send an `Idempotency-Key`. A retry of the same key with the same body replays the stored answer instead of starting a second recording.
| Parameter | In | Required | Description |
|---|---|---|---|
Idempotency-Key | header | no | A unique string you choose per logical create, 255 characters or fewer. A replay with the same body returns the first answer and `Idempotency-Replayed: true`; the same key with a different body is a 409. Keys are remembered for 24 hours and are scoped to one API key. |
| Status | Meaning |
|---|---|
201 | The recording, queued. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
409 | The request cannot be answered in the account's current state: the concurrency ceiling is full, an `Idempotency-Key` was reused or is still in flight, or the recording has not finished yet. |
413 | The request body is over what the server accepts at all. A file that arrives but is over the per-file ceiling is a 422 `validation_failed` naming `file` instead. |
422 | The request could not be accepted as sent. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
GET/transcriptions
The recordings this key can see
Newest first. A key pinned to a workspace sees that workspace only; an unpinned key sees the personal space.
| Parameter | In | Required | Description |
|---|---|---|---|
limit | query | no | Page size. 1-1000. |
cursor | query | no | The `next_cursor` of the previous page. Opaque -- do not parse it, and do not construct one. |
status | query | no | Keep only recordings in this state. |
| Status | Meaning |
|---|---|
200 | One page of recordings. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
422 | The request could not be accepted as sent. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
GET/transcriptions/{id}
One recording, with its progress
The endpoint you poll. `progress` is filled in while the recording is being worked on and is the only endpoint that reports it per-recording.
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | yes | The recording's public identifier, as returned on create. |
| Status | Meaning |
|---|---|
200 | The recording. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
404 | No such recording, or not one this key can see. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
DELETE/transcriptions/{id}
Delete a recording
Permanent, and it takes the audio, the transcript and the embeddings with it. Only a recording that has completed or failed can be deleted: one still queued or processing answers 409 `transcription_still_processing`, so wait for it to finish or fail.
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | yes | The recording's public identifier, as returned on create. |
| Status | Meaning |
|---|---|
204 | Deleted. No body. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
404 | No such recording, or not one this key can see. |
409 | The request cannot be answered in the account's current state: the concurrency ceiling is full, an `Idempotency-Key` was reused or is still in flight, or the recording has not finished yet. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
GET/transcriptions/{id}/segments
The transcript, a page at a time
Ordered by offset -- reading order. `speaker` is a diarization label (`A`, `B`, ...) and is stable within one recording only; it does not identify a person and does not carry across recordings.
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | yes | The recording's public identifier, as returned on create. |
limit | query | no | Page size. 1-1000. |
cursor | query | no | The `next_cursor` of the previous page. Opaque -- do not parse it, and do not construct one. |
| Status | Meaning |
|---|---|
200 | One page of transcript. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
404 | No such recording, or not one this key can see. |
422 | The request could not be accepted as sent. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
GET/transcriptions/{id}/export
The transcript as a file
The bytes are streamed back with the right content type -- there is no signed URL to follow, because the files are rendered on demand rather than stored. Only a `completed` recording can be exported.
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | yes | The recording's public identifier, as returned on create. |
format | query | yes | The file to build. |
language | query | no | An ISO code, when the transcript exists in more than one language. Defaults to the recording's own. |
| Status | Meaning |
|---|---|
200 | The file. |
401 | No key, an unreadable key, a revoked key, or a token from somewhere else. |
403 | The key is real but may not do this: no active subscription, a missing scope, a restricted account, a workspace the key is not pinned to. |
404 | No such recording, or not one this key can see. |
409 | The request cannot be answered in the account's current state: the concurrency ceiling is full, an `Idempotency-Key` was reused or is still in flight, or the recording has not finished yet. |
422 | The request could not be accepted as sent. |
429 | Too many requests in one of the windows. Wait `Retry-After` seconds. The daily fair-use allowance is not one of them: a recording over it is accepted and then fails with `failure_reason: quota_exceeded`. |
Objects
The shapes the endpoints above return.
Transcription
| Field | Type | Description |
|---|---|---|
id | string | The public identifier. Use it in every other path. |
status | string | Where the recording is. Four words, deliberately fewer than the pipeline has internally: an integration should not have to know which of our jobs currently holds the row. |
title | null | string | Yours if you sent one, ours once the recording has been read. |
language | null | string | The ISO code, once known. Null while auto-detection is pending. |
duration_seconds | null | integer | Length of the recording, once measured. |
created_at | string | ISO 8601 with offset. |
updated_at | string | ISO 8601 with offset. |
source | object | |
workspace | null | string | The workspace's name when the key is pinned to one, else null. |
progress | object | How far along the recording is, when we can say. `percent` is null and `indeterminate` is true for the stretches we cannot measure. |
failure_reason | null | string | Why it failed, or null. |
Segment
| Field | Type | Description |
|---|---|---|
index | integer | Position in the recording, zero-based and continuous across pages. Not a database identifier. |
start | null | number | Seconds from the start of the recording. |
end | null | number | Seconds from the start of the recording. |
speaker | null | string | A diarization label, stable within this recording only. |
text | null | string |
Me
| Field | Type | Description |
|---|---|---|
plan | null | string | |
subscription_status | null | string | |
limits | object | |
usage | object | |
rate_limits | object | |
key | object |
Language
| Field | Type | Description |
|---|---|---|
code | string | |
name | string |
Progress
| Field | Type | Description |
|---|---|---|
step | null | string | A coarse name for what is happening. |
percent | null | number | 0-100, or null when it cannot be measured. |
indeterminate | boolean |
Error
| Field | Type | Description |
|---|---|---|
error | string | A stable machine-readable code from the catalogue. |
message | string | |
errors | object | Field-by-field detail, on `validation_failed` only. |
docs | string | Where to read about this refusal, on the gating 403s. |
required | string | The scope the route wanted, on `insufficient_scope`. |
scope | string | |
limit | integer | |
seconds | integer | |
workspace | string |
Generating a client
Point any OpenAPI generator at the document and it will build you a typed client.
openapi-generator-cli generate \
-i https://heartotext.com/api/public/v1/openapi.json \
-g python \
-o ./hear2text-client