Skip to content

AWS Serverless Transcriber — architecture review

This page assembles sections relevant to architecture review: goals, requirements, and constraints; system model; architecture and integrations; security, quality, and operations; and decisions, trade-offs, and risks.

Contents

Summary

Status

In production

Role

System Designer

Stack

Python, AWS Lambda, API Gateway, DynamoDB, AWS S3, Terraform, LLM API integration

Project value

For the user:

  • fast transcription of long audio recordings without uploading to heavy SaaS services;
  • controlled access to results via Cognito and presigned URLs;
  • minimal ongoing costs through a serverless model.

For the professional profile:

  • practical design of an AWS serverless workflow;
  • working with async processing, webhooks, polling UI, and job lifecycle;
  • applying Infrastructure-as-Code through Terraform;
  • choosing solutions based on episodic load and cost constraints (cost-driven architecture).

Goals, Requirements, and Constraints

Goals and Non-Goals

Primary project goals

  • Implement cost-optimal audio transcription for personal use

Out of scope

  • Not a publicly accessible open system
  • Not a paid or monetizable platform

Business requirements

  • BR-001. The service is accessible over the internet.
  • BR-002. Access is limited to authorized users.
  • BR-003. The system supports transcription of long audio.
  • BR-004. The system supports speaker diarization.
  • BR-005. The user can download the completed transcript.

Functional requirements

  • FR-001. The user receives a presigned upload URL.
  • FR-002. The system creates a transcription job whose current status the user can check without refreshing the page.
  • FR-003. The system updates job status as processing progresses.
  • FR-004. The UI displays a list of jobs and their current status.
  • FR-005. The user receives a presigned URL for the completed transcript.
  • FR-006. The webhook handler accepts a callback from the transcription provider.

Constraints

  • CON-001. Files up to 300 MB.
  • CON-002. Recording duration up to 6 hours.
  • CON-003. Up to 5 files transcribed concurrently.
  • CON-004. 1–3 users.
  • CON-005. An on-premises model is not considered.
  • CON-006. Ongoing infrastructure costs must be minimal.

Non-functional requirements

  • NFR-001. Access control.
  • NFR-002. Cost efficiency.
  • NFR-003. Portability.
  • NFR-004. Operability.
  • NFR-005. Resilience of async workflow.

Requirements (detailed)

  • BR-001. Internet accessibility The system must be accessible from any device connected to the internet.
  • BR-002. Restricted access The system must provide user authorization and authentication. New user registration is not planned, but user management must be supported.
  • BR-003. Speaker diarization The system must support splitting the transcript by speakers.
  • BR-004. File formats The system must support the most common voice recorder formats — MP3, AAC.

Rules and constraints (detailed)

  • CON-001. Deployment region: for users in Europe, the Caucasus, and Turkey. An AWS region with acceptable availability. Low latency is not a critical requirement because the primary scenario is asynchronous.

  • CON-002. Recording file size — up to 300 MB.

  • CON-003. Single recording duration — up to 6 hours. The system must operate reliably on long recordings.

  • CON-004. Final transcription cost The cost per minute at 40 hours per month must not exceed $0.3/minute including infrastructure costs (reference: https://speech2text.ru/my/rate).

  • CON-005. Mandatory access restriction 1–2 accounts for personal use is sufficient.

  • CON-006. Running an open-source transcription model is not planned Due to lack of hardware at the required level, and the economic impracticality of renting such equipment.

  • CON-007. Solution portability The solution must be easy to deploy and, if needed, tear down when the need for it is temporarily gone.

  • CON-008. Minimal maintenance costs The solution must require minimal resources for maintenance (security updates, etc.).

System Model

Data Model

TranscriptionJob

The core entity of the transcription process.

Attributes:

  • fileId
  • ownerUserId
  • inputS3Key
  • transcriptS3Key
  • status
  • providerTranscriptId
  • createdAt
  • updatedAt
  • errorReason
  • speakerMode
  • fileSize
  • durationEstimate

System invariants

  • a job belongs to one user;
  • a download URL is issued only to the job owner;
  • a transcript can be downloaded only in READY status;
  • the webhook must be idempotent;
  • a repeated callback from the provider must not create a new transcript;
  • a failed job must not block the list of other jobs;
  • presigned URLs have a limited lifetime.

DynamoDB — job statuses

Each transcription job is stored as a DB record with a lifecycle:

State Machine Meaning
UPLOADING Presigned URL issued; client upload in progress
TRANSMITTING Audio uploaded to S3; submission to service initiated
PROCESSING Service is transcribing (transcript_id stored)
READY Transcript saved to S3; available for download
ERROR AssemblyAI or pipeline failure (reason logged)

Amazon S3

  • File bucket: audio uploads and generated transcripts (Transcript.txt).
  • Static website bucket: SPA served via CloudFront.

API Contracts

API Gateway (authentication)

Method Path Purpose
GET /upload-url Create record; return Presigned POST URL and fileId
GET /jobs List user's jobs (UI polling)
GET /download-url?fileId=... Verify access; Presigned GET URL for transcript
POST /webhook Transcription provider callback (transcript_id)

All API Gateway routes require JWT (Amazon Cognito) except /webhook (transcription provider callback).

Amazon Cognito (Hosted UI / PKCE)

Method Path Purpose
POST /oauth2/token Exchange authorization code for Access & ID tokens

External transcription provider API

Method Path Purpose
POST /v2/transcript Submit audio URL + webhook URL; returns transcript_id
GET /v2/transcript/{id} Fetch completed transcription text

Amazon S3 (direct client access)

Method Target Purpose
POST Presigned POST URL Direct audio upload (bypasses API Gateway limits)
GET Presigned GET URL Direct transcript download

Architecture and Integrations

Architecture

Architectural concept

The system is built on an event-driven model.

The static frontend is served via S3/CloudFront. The user authenticates through Cognito Hosted UI and calls API Gateway using the obtained JWT. API Gateway routes requests to Lambda functions.

Large audio files do not pass through API Gateway and Lambda. The backend issues a presigned POST URL, after which the browser uploads the file directly to S3. An S3 ObjectCreated event triggers a handler that creates a temporary URL for the transcription provider and submits a new transcription request. The external provider performs long transcription asynchronously and returns the result via webhook. The final text transcript is saved to S3, and the job status is updated and stored in DynamoDB.

architecture-beta
    service dynamo(aws:dynamodb)[AWS DynamoDB]
    service lambda(aws:lambda)[AWS Lambda] 
    service api(aws:api-gateway)[AWS API Gateway]
    service static(aws:simple-storage-service)[Static website at Amazon S3]
    service storage(aws:simple-storage-service)[File storage at Amazon S3]
    service browser(logos:chrome)[Browser]
    service cognito(aws:cognito)[AWS Cognito]
    service ai(logos:webhooks)[Transcriber API]
    service front(aws:cloudfront)[AWS CloudFront]

    front:T --> B:static
    browser:T --> B:api
    browser:L --> R:front
    browser:B --> T:cognito

    api:R --> L:lambda
    api:T <-- B:ai
    lambda:T --> R:ai
    lambda:R --> L:dynamo
    lambda:B --> T:storage
    storage:L <-- R:browser

Integration flows

Sequence diagrams

Audio file submission

sequenceDiagram
    autonumber

    actor U as Browser (SPA)
    participant API as API Gateway
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB

    U->>API: GET /upload-url (+JWT in Header)
    activate API
    API->>L: Invoke get_upload_url
    deactivate API
    activate L
    L->>DB: Create record (Status: UPLOADING)
    L->>S3: Generate Presigned POST URL
    activate S3
    S3-->>L: Upload link
    deactivate S3
    L-->>U: JSON: { uploadUrl, fileId }
    deactivate L

Direct upload and asynchronous trigger (event-driven)

sequenceDiagram
    autonumber

    actor U as Browser (SPA)
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB
    participant AI as TranscribeProvider

    U->>S3: POST Upload audio file (Bypass API Gateway)
    activate S3
    S3-->>U: 204 No Content (Success)
    deactivate S3

    S3-)L: Event: ObjectCreated (Async invoke s3_trigger)
    activate L
    L->>DB: Update status (TRANSMITTING)
    L->>S3: Generate temporary GET Presigned URL for AI
    L->>AI: POST /v2/transcript (Audio URL + Webhook URL)
    activate AI
    alt TranscribeProvider accepts request
        AI-->>L: 201 Created (transcript_id)
        L->>DB: Status = PROCESSING (save ID)
    else API error (e.g. HTTP 400/500)
        AI-->>L: 4xx / 5xx Error
        deactivate AI
        L->>DB: Status = ERROR (Log reason)
    end
    deactivate L

AI processing and webhook (up to several minutes)

sequenceDiagram
    autonumber

    actor U as Browser (SPA)
    participant API as API Gateway
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB
    participant AI as TranscribeProvider

    loop Every 15 seconds (Polling)
        U->>API: GET /jobs
        activate API 
        API->>L: Invoke get_jobs
        deactivate API
        activate L
        L->>DB: Request user's file list
        activate DB
        DB-->>L: Data (Status: PROCESSING)
        deactivate DB
        L-->>U: Update UI
        deactivate L
    end

    Note over AI, DB: TranscribeProvider completes processing
    AI->>API: POST /webhook (pass transcript_id)
    activate API
    API->>L: Invoke webhook_TranscribeProvider
    deactivate API
    activate L
    L->>AI: GET /v2/transcript/{id}
    activate AI
    AI-->>L: Completed transcription text
    deactivate AI
    L->>S3: PUT Save text (Transcript.txt)
    L->>DB: Update status (READY)
    deactivate L

Result retrieval (download)

sequenceDiagram
    autonumber

    actor U as Browser (SPA)
    participant API as API Gateway
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB

    U->>API: GET /jobs (Next polling interval)
    activate API
    API-->>U: Status: READY (Download button active)
    deactivate API

    U->>API: GET /download-url?fileId=...
    activate API
    API->>L: Invoke get_download_url
    deactivate API
    activate L
    L->>DB: Verify user access rights to file
    L->>S3: Generate Presigned GET URL (with Content-Disposition)
    L-->>U: JSON: { downloadUrl }
    deactivate L
    U->>S3: Direct download of Transcript.txt
    activate S3
    S3-->>U: Transcription file
    deactivate S3

Security, Quality, and Operations

Security and access model

Zone Risk Control
Authentication unauthorized user access Cognito Hosted UI, JWT validation
Authorization downloading another user's transcript owner check in DynamoDB before issuing presigned GET URL
Upload uploading an oversized/unsupported file client-side and backend-side validation, content-type/size constraints
S3 access direct public access to files private buckets, presigned URLs only
Webhook forged callback shared secret / provider verification
Secrets provider API key leak Secrets Manager / encrypted env, no secrets in code
Logs private data in logs do not log transcript/audio content, only status/error metadata
Cost abuse mass launch of expensive jobs user limits, quotas, AWS budget alerts

Access requirements

  • Access to the service must be restricted.
  • User data must be isolated.

Authentication — AWS Cognito

  • User data isolation: AWS Cognito provides built-in account management, registration, 2FA, brute-force protection, etc. The service integrates seamlessly into the AWS ecosystem.
  • Per-file access check on download (get_download_url verifies access in DynamoDB before issuing a Presigned URL).

Authentication

sequenceDiagram
    autonumber

    actor U as Browser (SPA)
    participant API as API Gateway
    participant C as Amazon Cognito

    U->>API: API request (no JWT / expired JWT)
    activate API
    API-->>U: 401 Unauthorized
    deactivate API
    U->>U: SPA clears local data
    U->>C: Redirect to Hosted UI login form
    activate C
    C->>U: Login form 
    deactivate C
    U->>C: Enter credentials and attempt authentication
    activate C
    alt Invalid credentials
        C-->>U: Return error (Invalid credentials)
    else Valid credentials
        C-->>U: Return Auth Code (via redirect_uri)
        deactivate C
        U->>C: POST /oauth2/token (Exchange Code for JWT)
        activate C
        C-->>U: Access & ID Tokens
        deactivate C
    end

Failure modes

Failure mode Impact Detection Mitigation / recovery
Lambda timeout during synchronous transcription job does not complete Lambda logs async workflow + webhook
Provider API error 4xx/5xx job moves to ERROR provider logs save reason, show status to user
Webhook not received task stuck in PROCESSING UI retry via a new job
Repeated webhook possible result overwrite duplicate callback idempotent write via transcriptId
User attempts to download another user's transcript data leak access check fails owner verification before issuing presigned URL
Presigned URL expired user cannot upload/download file client error generate a new presigned URL
S3 upload not completed task remains in UPLOADING stuck UPLOADING status TTL cleanup / retry upload
API key leak unauthorized spend AWS quota notifications key rotation, quota reset, transcription provider budget limit + overdraft disabled
Cost growth from mass jobs unexpected bill AWS Budgets / CloudWatch quotas, concurrency limit, transcription provider budget limit + overdraft disabled
Transcript saved but status not updated UI does not show ready result UI does not show ready result integrity check / debug job logic

Sizing and cost estimate

Expected workload

Parameter Value
Users 1–3
Audio per month up to 40 hours
Average file length 1.5+ hours
Maximum file length 6 hours
Maximum file size 300 MB
Concurrent jobs up to 5

Cost drivers

Component What affects cost
Transcription provider audio minutes/hours
S3 volume of audio and transcript files
DynamoDB number of job/status reads
Lambda number of invocations and handler duration
API Gateway number of API requests
CloudFront/S3 static hosting frontend traffic
Secrets Manager provider API key storage
CloudWatch logs retention

Cost logic

The main cost is not AWS compute but the external transcription provider. AWS Lambda, API Gateway, DynamoDB, and S3 remain secondary cost drivers at the given workload profile. Therefore the architecture is optimized not for high load but for minimal ongoing costs and no idle infrastructure.

Decisions, Trade-offs, and Risks

Key decisions

Full event-driven model

Transcription is a long-running process that may exceed Lambda function execution time. * Decision: Implement an event-driven model. Temporally decouple audio file submission from text result retrieval. Find a transcription provider that offers webhook notification functionality.

Working with 300 MB files

File size may exceed API Gateway limits and increase Lambda function runtime. * Decision: Use Presigned URL functionality provided by Amazon S3 out of the box. This allows the client to access S3 directly, bypassing API Gateway and Lambda.

Architectural trade-offs (ADR)

1. Optimal architectural style

Context

The episodic nature of tool usage, a small number of use cases, broad availability zone, and strict infrastructure and maintenance cost requirements must be accounted for.

Decision

Use a Serverless approach and AWS Lambda infrastructure.

Rejected alternative

VPS, Telegram bot

Rationale
  • Lambda functions are billed for execution time; with episodic usage they can fit within the Free tier (1M requests or 400 TB-s per month), i.e. business logic is free under given conditions. AWS Lambda also handles infrastructure security on Amazon's side, eliminating maintenance costs.

  • VPS require periodic payment for capacity (even when the service is unused this can be $3–7 per month), and require resources to maintain the required security level (OS security updates, fresh packages, etc.).

  • A Telegram bot as a channel is convenient enough, but unsuitable due to audio file size limits (50 MB) + it does not eliminate the need to host bot logic somewhere (VPS with all that entails).

Trade-offs
  • Serverless requires more careful design, maximizing offloading of heavy operations outside functions (Presigned URLs in S3). Function stability requirements must be higher. Quotas on invocations and spend alerts must be configured.
  • Serverless is harder to debug.

2. Optimal transcription infrastructure

Context

Budget for running an open-source model on owned hardware is not planned.

Decision

Use a third-party transcription API service.

Rejected alternative

Locally run open-source model, rented hardware for running an open-source model.

Rationale
  • A third-party transcription API service works fast and requires no maintenance. AssemblyAI was chosen as optimal quality-to-price ratio — $0.15 per hour. Cost per minute is $0.0025 when using Amazon cloud and serverless approach, which is 120 times less than required. The limit of 5 concurrent transcription processes is also met.

  • No hardware available for local model deployment (minimum requirements — 16 GB RAM and 8 GB video memory on an external GPU). Purchasing additional hardware is outside the digital nomad paradigm.

  • Renting compatible hardware costs $5–10 per month with hourly billing, requires separate resources for deployment, launch, and security (security updates). Running such hardware continuously costs $40–60 per month. The solution is suboptimal.

Trade-offs
  • Recordings are sent to a third-party service; the privacy requirement is not met. Agreed with the stakeholder.

  • The third-party service may change pricing or shut down.

  • API keys must be stored securely. Storing in code is insecure and bad practice. AWS Secrets Manager costs $0.40 per month + $0.05 per 10,000 requests — this must be factored into the final cost per transcription minute.

3. User data isolation

Context

Access restriction and data separation requirements must be met, and the service must support multiple users without open registration.

Decision

AWS Cognito

Rejected alternative

Password protection of a static HTML page

Rationale
  • AWS Cognito has built-in account management, registration, 2FA, brute-force protection, etc. The service integrates seamlessly into the AWS ecosystem. Project needs fit within the Free tier (<10,000 MAU).

  • HTML password protection is poorly adapted to brute-force attacks; changing the password via code changes is not best practice. No ability to separate access between multiple users.

  • A custom access management system is over-engineering on top of a single business process.

Trade-offs
  • Free tier terms may change and user management functionality may become paid. Solution cost must be re-evaluated.

... Key ADRs are presented only partially for demonstration purposes