curl --request POST \
--url https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/quotes/{quote_id}/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '{}'{
"group_id": "grp_9k2fA7bQ3xzM1LpN",
"status": "complete",
"quote_id": "qte_9k2fA7bQ3xzM1LpN",
"member_count": 3,
"members": [
{
"kind": "cached_read",
"read_id": "job_2c1d4e9fA7bQ3xzM",
"episode_id": "ep_rwg4do2imnjyhaj7",
"credits_charged": 1,
"created_at": "2026-09-08T09:01:00Z"
},
{
"kind": "job",
"job_id": "job_4e9f2c1dB8cR4yaN",
"episode_id": "ep_3iicyxg6ymbv75gy",
"status": "queued",
"estimated_credits": 60,
"reserved_credits": 75,
"created_at": "2026-09-08T09:01:00Z"
},
{
"kind": "job",
"job_id": "job_9f2c1d4eC9dS5zbP",
"episode_id": "ep_uy4pqvjsmeyrhavz",
"status": "queued",
"estimated_credits": 45,
"reserved_credits": 57,
"created_at": "2026-09-08T09:01:00Z"
}
],
"member_counts": {
"validating": 0,
"queued": 2,
"downloading": 0,
"transcribing": 0,
"merging": 0,
"completed": 0,
"failed": 0,
"cancelled": 0,
"cached_read": 1
},
"credits_reserved": 132,
"credits_settled": 1,
"credits_released": 0,
"created_at": "2026-09-08T09:01:00Z",
"completion_deadline": "2026-09-08T09:06:00Z",
"completed_at": "2026-09-08T09:01:00Z",
"abandoned_at": null
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}Spend what a quote priced, as one job group
Turns a recorded quote into a job group: one member per quoted entry, each either a transcription job reserved at the entry’s ceiling or, for an entry priced as cached, a settled read at the price the quote gave it. This is the operation that spends.
Every guard runs before the first credit moves. The quote must be the caller’s own and unexpired (quote_not_found, quote_expired); an expected_total_credits in the body must agree with the quote’s recorded total_ceiling_credits, checked before anything is probed (expected_total_mismatch); every entry’s enclosure is re-probed and a republished one refuses the whole confirm naming the episode (quote_mismatch — take a new quote), while entries the probe budget did not reach refuse it unverified (quote_unverified — send the same confirm again); the group’s job-bearing members are checked once against the tier’s open-job cap (concurrency_limited, naming the cap and the room left) and the whole group’s ceiling against the balance (payment_required). A refusal here writes nothing at all.
Idempotency-Key is required. The group is created under it before any member is reserved, so a repeat with the same key and body returns the original group — whatever state it is in — with 200 and charges nothing further. The same key with a different body is 409 idempotency_conflict.
A confirm that dies half way is recoverable without you. The group is written pending first and marked complete only when every member has landed; a group still pending past its completion_deadline is swept — its reservations returned, its status abandoned. Reading the group tells you which.
curl --request POST \
--url https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/quotes/{quote_id}/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '{}'{
"group_id": "grp_9k2fA7bQ3xzM1LpN",
"status": "complete",
"quote_id": "qte_9k2fA7bQ3xzM1LpN",
"member_count": 3,
"members": [
{
"kind": "cached_read",
"read_id": "job_2c1d4e9fA7bQ3xzM",
"episode_id": "ep_rwg4do2imnjyhaj7",
"credits_charged": 1,
"created_at": "2026-09-08T09:01:00Z"
},
{
"kind": "job",
"job_id": "job_4e9f2c1dB8cR4yaN",
"episode_id": "ep_3iicyxg6ymbv75gy",
"status": "queued",
"estimated_credits": 60,
"reserved_credits": 75,
"created_at": "2026-09-08T09:01:00Z"
},
{
"kind": "job",
"job_id": "job_9f2c1d4eC9dS5zbP",
"episode_id": "ep_uy4pqvjsmeyrhavz",
"status": "queued",
"estimated_credits": 45,
"reserved_credits": 57,
"created_at": "2026-09-08T09:01:00Z"
}
],
"member_counts": {
"validating": 0,
"queued": 2,
"downloading": 0,
"transcribing": 0,
"merging": 0,
"completed": 0,
"failed": 0,
"cancelled": 0,
"cached_read": 1
},
"credits_reserved": 132,
"credits_settled": 1,
"credits_released": 0,
"created_at": "2026-09-08T09:01:00Z",
"completion_deadline": "2026-09-08T09:06:00Z",
"completed_at": "2026-09-08T09:01:00Z",
"abandoned_at": null
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}Authorizations
Authorization: Bearer hk_live_... for live keys or Authorization: Bearer hk_test_... for test-mode keys. hk_test_ keys resolve real public catalog metadata but return deterministic committed fixtures, never call inference, and never mutate live credits. This is the only transport for the credential: the x-api-key alias once documented was removed in 0.2.0, because the edge authorizer reads Authorization as its single identity source and a request on any other header is refused before it is authenticated.
Never accepted on a dashboardJwt operation, and there are no exceptions. GET /v1/usage and GET /v1/limits briefly declared both schemes (0.8.0); that was withdrawn in 0.8.1 because no deployed route could honor it — both operations are served by the control-plane API, whose authorizer verifies a Cognito token and refuses an hk_live_ credential on shape, and the customer API does not route either path. An API-key holder reads its balance and reservation from QuoteResponse, which carries balance_credits and reserved_credits on every quote. Every operation in this document takes one scheme or the other and refuses the wrong one as unauthenticated.
Headers
As IdempotencyKeyHeader, but required: a confirm spends up to a whole selection's ceiling, and a retry that could not be recognized as one would spend it twice. Missing is 400 invalid_request.
1 - 255^[!-~]{1,255}$Path Parameters
Opaque, server-generated quote identifier.
40^qte_[A-Za-z0-9]{16,32}$Body
The quote is in the path and the key is in the header, so the body carries at most the spending fence below. An empty object, or no body at all, is accepted; any other property is refused, so a further field can be added without an old client having silently sent it.
The quote's total_ceiling_credits as the caller read it. Sent, and disagreeing with the quote's own total, the API refuses the confirm with 409 expected_total_mismatch before an enclosure is probed or a credit is reserved: a caller that has misread the price cannot spend it. Omitted, the confirm proceeds on the quote's recorded total alone.
0 <= x <= 9007199254740991Response
The job group this confirm created, complete, with every member named — or, on a repeat under the same Idempotency-Key, the original group as it stands.
One group's rollup. member_count is how many members the confirm set out to create; members is how many exist, which is fewer only while the group is pending (or after it was abandoned part way). credits_reserved is what the group still holds against the balance — the reservations of members not yet terminal; credits_settled is what it has been charged, job members and cache reads together; credits_released is what came back from members that failed, were cancelled, or settled below their ceiling. None of the three is a bill: each job member carries its own figures, and the ledger is the record.
Opaque, server-generated job group identifier.
40^grp_[A-Za-z0-9]{16,32}$pending while a confirm is still fanning out (or died doing so); complete once every member landed; abandoned once the sweeper returned a pending group's reservations past its deadline. A cancelled group is complete with cancelled members.
pending, complete, abandoned The quote this group confirmed, or null for a single-episode submission.
40^qte_[A-Za-z0-9]{16,32}$x >= 1100One member of a group — a job, or a settled cache read. kind discriminates.
- Option 1
- Option 2
Show child attributes
Show child attributes
How many members are in each job state, plus how many were settled cache reads (which have no state — they were delivered at confirm). The nine sum to member_count on a complete group.
Show child attributes
Show child attributes
A non-negative JavaScript-safe integer credit amount. The upper bound matches Number.MAX_SAFE_INTEGER, which is enforced by the domain ledger so arithmetic and JSON round-trips cannot silently lose cents worth of credit precision.
0 <= x <= 9007199254740991A non-negative JavaScript-safe integer credit amount. The upper bound matches Number.MAX_SAFE_INTEGER, which is enforced by the domain ledger so arithmetic and JSON round-trips cannot silently lose cents worth of credit precision.
0 <= x <= 9007199254740991A non-negative JavaScript-safe integer credit amount. The upper bound matches Number.MAX_SAFE_INTEGER, which is enforced by the domain ledger so arithmetic and JSON round-trips cannot silently lose cents worth of credit precision.
0 <= x <= 9007199254740991Past this, a still-pending group is swept.
An ISO 8601 timestamp, or null.
An ISO 8601 timestamp, or null.

