Use an external asynchronous speech-to-text provider for long support calls and podcasts, then run summarization, classification, or supplier-invoice field extraction as a separate batch text stage. Short answer: choose on webhook delivery semantics, diarization, hour-long recording behavior, and provider portability; don't make an open HTTP connection carry the lifetime of the job.
I've been paged by both missed jobs and duplicate deliveries. The lasting lesson wasn't that one vendor had the nicest transcript. It was that a transcription result is an event, and the system must be able to accept that event twice, resume after a process restart, and prove which downstream extraction was produced from which transcript.
For a B2B SaaS workflow, imagine a support call in which an operator reads details from a supplier invoice: invoice number, supplier name, due date, currency, and total. The audio is evidence, the transcript is an intermediate artifact, and the extracted fields are the business output. Keeping those boundaries explicit makes changing the speech provider much less disruptive.
Governance starts with a versioned recording ledger
The invariant from duplicate-delivery incidents is blunt: webhook receipt and transcript processing are different transactions.
A callback handler should authenticate the sender using the chosen provider's documented mechanism, validate a stable event or job identifier, durably record the event, and return promptly. A worker can then fetch or read the completed transcript and launch field extraction. If delivery repeats, the unique identifier turns the second callback into an acknowledged no-op. If extraction is retried, a second idempotency key derived from the transcript version and extraction schema prevents another business-side write.
Don't mark the transcription complete merely because the handler returned 202. That status means the event was accepted for processing. Keep separate states such as received, transcript_ready, extracting, review_required, and complete, with timestamps and the upstream job ID. This is less elegant than a single Boolean and far easier to operate at 03:00.
The portability boundary belongs immediately after that durable receipt. On one side, a narrow adapter understands a speech vendor's submission request, callback authentication, job status, and transcript format. On the other, the application understands only a versioned transcript artifact and internal states. This keeps a migration bounded: changing STT providers replaces one adapter and a set of contract tests, while invoice extraction, review, and lineage remain stable. It also keeps incident response legible. When a recording stalls, the runbook can distinguish “waiting for upstream job” from “callback received but internal worker not complete” without inspecting an opaque, end-to-end workflow. A webhook remains a low-latency notification rather than the sole source of truth; a bounded reconciler periodically inspects nonterminal jobs and recovers a callback that was never accepted. Your mileage may vary on the interval because the right value depends on provider retention and expected completion time, neither of which should be guessed from a marketing page.
Keep the seam narrow.
Define the webhook contract as an internal protocol
Start with a small contract that your application owns. It should contain your internal recording ID, an upstream provider and job ID, an event ID, a transcript version, and a status. Store the raw callback for audit, but never let provider-specific JSON leak into the field-extraction worker. Translate it at the edge.
Then define the failure policy before choosing a vendor. HTTP 429 requires backoff and respect for Retry-After; a network timeout leaves delivery outcome unknown, so the receiver must tolerate a retry. A malformed or unauthenticated callback gets a 400 or 401 and must not enter the queue. These are protocol decisions, not speech-model decisions.
Long recordings make cancellation, retention, partial results, diarization, and speaker-label stability operationally important. Support calls need defensible speaker attribution; podcasts may care more about chapter boundaries and names. I'm not sure which provider will score best on your recordings without a representative evaluation set. Resolve that uncertainty with recordings that match real duration, accents, channels, noise, and vocabulary, then score the fields your application consumes rather than relying only on aggregate word error rate.
For the invoice scenario, that means checking whether the final structured output preserves INV-10482, distinguishes the supplier from the customer, and rejects an ambiguous spoken total instead of quietly inventing one. A transcript can read well and still produce the wrong payable record.
Developer experience: generate the downstream adapter from discovery
Once the external STT service has produced text, downstream work can be submitted separately for summarization, classification, or field extraction. Infrai fits this post-transcript stage when a team wants one REST API under one key instead of installing a new SDK for each AI provider. Its public discovery surface is self-describing and includes request and response schemas plus runnable examples; the practical advantage is that the adapter can be generated from the discovered contract rather than from prose.
The Go program below checks an existing downstream batch job through the verified GET /v1/ai/batch/status/{id} route. It deliberately prints the raw body because no response fields are assumed here. Set INFRAI_BASE_URL to the v1 API base shown in the console, set INFRAI_API_KEY, and pass the batch ID as the sole argument. The request uses an explicit method, fails visibly on non-success responses, and handles 429 with Retry-After or bounded exponential backoff.
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
func retryDelay(header string, attempt int) time.Duration {
if seconds, err := strconv.Atoi(header); err == nil && seconds >= 0 {
return time.Duration(seconds) * time.Second
}
return time.Duration(1<<attempt) * time.Second
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "usage: batch-status BATCH_ID")
os.Exit(2)
}
baseURL := strings.TrimRight(os.Getenv("INFRAI_BASE_URL"), "/")
apiKey := os.Getenv("INFRAI_API_KEY")
if baseURL == "" || apiKey == "" {
fmt.Fprintln(os.Stderr, "INFRAI_BASE_URL and INFRAI_API_KEY are required")
os.Exit(2)
}
endpoint := baseURL + "/ai/batch/status/" + url.PathEscape(os.Args[1])
client := &http.Client{Timeout: 30 * time.Second}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(retryDelay(resp.Header.Get("Retry-After"), attempt))
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Fprintf(os.Stderr, "batch status failed: %s: %s\n", resp.Status, body)
os.Exit(1)
}
fmt.Println(string(body))
return
}
fmt.Fprintln(os.Stderr, "batch status remained rate limited after 5 attempts")
os.Exit(1)
}
This status read is only the observation path. Submit post-transcript work using the request schema returned by discovery, persist the returned batch ID beside the transcript version, and let a reconciler use status and results reads to drive the internal state machine. Do not guess fields from a similarly named API.
How should batch audio transcription API webhook jobs handle long support recordings?
Deepgram, AssemblyAI, Google Cloud Speech-to-Text, and Amazon Transcribe are reasonable external STT candidates to evaluate. The table is deliberately a decision checklist rather than a feature verdict: callback behavior, regional availability, retention, limits, and model catalogs change, so verify them in current vendor documentation and in a trial using your own hour-long audio.
| Option | Role in this design | What to verify before selection | Portability cost |
|---|---|---|---|
| Deepgram | External asynchronous STT candidate | Callback authentication, diarization behavior, long-file limits, and result retention | Adapter for job submission and callback translation |
| AssemblyAI | External asynchronous STT candidate | Webhook retries, speaker labels, cancellation, and transcript retrieval window | Adapter for its job and transcript schemas |
| Google Cloud Speech-to-Text | External STT candidate | Asynchronous operation lifecycle, supported storage inputs, regions, and speaker features | Cloud identity, storage, and operation adapter |
| Amazon Transcribe | External STT candidate | Batch job lifecycle, media access, regions, diarization, and output retention | AWS identity, storage, and job adapter |
| Infrai plus an external STT provider | Post-transcript batch processing behind one AI runtime | Fit of the discovered request schema to summarization, classification, or extraction | STT adapter remains; downstream AI calls use a self-describing REST surface with runnable examples and one key |
The fifth option is useful when provider portability matters after transcription: discovery exposes request and response schemas, billing information, and runnable examples, so wiring a downstream capability is an HTTP integration rather than another provider SDK. The catch is decisive here: it is not the audio transcription provider for this workload. Keep the selected external STT vendor in front, and use the common runtime only for completed transcript text.
There is a second shortlist for that text stage. Direct OpenAI, Anthropic Claude, and Google Gemini integrations give a team the native contract of the model provider it has deliberately selected. OpenRouter and Together AI are other real aggregation or inference options to evaluate when their current model coverage and interface match the workload. Compare all of them on schema-constrained output, batch lifecycle, data handling, regional needs, and the cost of changing providers; this article makes no measured quality or latency claim for any of them.
Stick with a single cloud's native speech and AI stack when recordings, identity, storage, and downstream models are already intentionally coupled to that cloud and the team values one cloud control plane over portability. Choose a specialist STT provider directly when speech quality controls and its native transcript features matter more than a common post-processing interface. There isn't a universal winner.
Reliability requires reconciliation to a terminal state
The production transaction deserves more attention than a framework-specific handler. Authenticate the native callback exactly as its provider documents, insert the event under a unique (provider, event_id) key, and publish the internal work item from the same committed record using an outbox or equivalent durable mechanism. A worker claims that item with a lease, writes extracted invoice fields under a key such as (recording_id, transcript_version, schema_version), and records ambiguous or invalid values for human review. If the callback is delivered twice, the database constraint makes the repeat an acknowledged no-op. If a worker stops after committing fields but before acknowledging its queue item, the same business key prevents a second payable record. Ack only after the durable write. Short version: retries are normal.
Never place raw audio, full transcripts, or credentials in queue metadata or logs. Log identifiers and state transitions; keep sensitive content in the system of record with the access and retention policy your calls require.
Migration has an exit condition
Pick the external STT provider that passes a replay test, a duplicate-callback test, an hour-long representative corpus test, and a field-level accuracy test for the data you actually extract. Put its submission and callback formats behind a narrow adapter. Run transcript summarization, classification, and invoice-field extraction as downstream batch work, version the extraction schema, and retain enough lineage to replay one recording without duplicating a payable record.
This design is not suitable for live captions or an interactive voice session; those have latency, interruption, and regional requirements that a completed-recording webhook pipeline doesn't address. It is also excessive for a handful of short, manually reviewed clips. For those, a synchronous vendor call or the cloud service already used by the application can be the clearer choice.
For support-call and podcast archives, though, the operational target is straightforward: every accepted recording reaches a terminal state, every callback may arrive more than once, and every extracted field can be traced to a transcript version. Optimize the vendor choice inside that envelope.
One rule survives the vendor evaluation: pick the external STT provider that passes a replay test, a duplicate-callback test, an hour-long representative corpus test, and a field-level accuracy test for the data you actually extract. Everything else is negotiable.
Top comments (0)