Scriptivox logoScriptivox

    Get started

    OverviewQuickstartPricing

    API Reference

    TranscribeFile UploadGet ResultBalanceError CodesRate LimitsFormatsLanguages

    Guides

    Webhooks

    Use Cases

    Folder Watcher
    Scriptivox logoScriptivoxAPI Documentation

    Webhooks

    Receive real-time notifications when transcriptions complete or fail instead of polling.


    Overview

    When you provide a webhook_url in your transcription request, we'll send a POST request to that URL when the job finishes. Webhooks are signed with HMAC-SHA256 so you can verify they came from Scriptivox.

    Webhooks fire for both upload-based and URL-based transcription flows.

    You can also set a default webhook URL in your account settings, which will be used for all transcriptions that don't specify one.

    Setting up webhooks

    Pass a webhook_url when starting a transcription:

    requests.post("https://api.scriptivox.com/v1/transcribe",
    headers={"Authorization": "sk_live_YOUR_KEY"},
    json={
    "upload_id": "abc-123",
    "webhook_url": "https://your-server.com/webhook"
    })

    Webhook events

    transcription.downloading

    Sent when the file download begins from the provided URL. URL flow only — not sent for upload-based transcriptions since the file is already uploaded.

    json
    {
    "event": "transcription.downloading",
    "transcription_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "status": "downloading"
    }

    transcription.processing

    Sent when the audio file has been downloaded and validated, and the transcription job has been submitted for processing. Includes the detected duration and reserved cost.

    json
    {
    "event": "transcription.processing",
    "transcription_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "status": "processing",
    "duration_seconds": 120,
    "cost_cents": 0.5
    }

    Both URL-based and upload-based transcriptions receive this event after the file has been validated and the job is submitted.

    transcription.completed

    Sent when a transcription finishes successfully. Includes the full result.

    json
    {
    "event": "transcription.completed",
    "transcription_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "status": "completed",
    "duration_seconds": 120,
    "cost_cents": 0.5,
    "result": {
    "full_transcript": "Hello, thanks for joining...",
    "language": "en",
    "duration_seconds": 120,
    "speakers": ["SPEAKER 1", "SPEAKER 2"],
    "utterances": [
    {
    "start": 0.5,
    "end": 3.2,
    "text": "Hello, thanks for joining the call today.",
    "speaker": "SPEAKER 1",
    "confidence": 0.95,
    "words": [...]
    }
    ]
    }
    }

    transcription.failed

    Sent when a transcription fails. Reserved balance is automatically released. This can fire from the download/validation stage or the transcription processing stage.

    json
    {
    "event": "transcription.failed",
    "transcription_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "status": "failed",
    "error": {
    "code": "PROCESSING_ERROR",
    "message": "Failed to process audio file"
    }
    }

    Events summary

    EventWhenFlows
    transcription.downloadingFile download started from URLURL only
    transcription.processingFile validated, job submittedBoth
    transcription.completedTranscription finished successfullyBoth
    transcription.failedDownload, validation, or processing failedBoth

    Verifying webhook signatures

    Every webhook request includes two headers for signature verification:

    HeaderDescription
    X-Scriptivox-SignatureHMAC-SHA256 hex digest of the signed payload
    X-Scriptivox-TimestampUnix timestamp (seconds) of when the webhook was sent

    Signing formula

    secret = SHA256(api_key)

    signature = HMAC-SHA256(secret, timestamp + "." + body)

    import hmac
    import hashlib
    import time
    def get_signing_secret(api_key: str) -> str:
    """Your signing secret is SHA256 of your API key."""
    return hashlib.sha256(api_key.encode()).hexdigest()
    def verify_webhook(body: bytes, signature: str, timestamp: str, api_key: str) -> bool:
    # Check timestamp is recent (within 5 minutes)
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
    return False
    # Compute expected signature using SHA256(api_key) as secret
    signing_secret = get_signing_secret(api_key)
    message = f"{timestamp}.{body.decode()}"
    expected = hmac.new(
    signing_secret.encode(),
    message.encode(),
    hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
    # In your webhook handler:
    # signature = request.headers["X-Scriptivox-Signature"]
    # timestamp = request.headers["X-Scriptivox-Timestamp"]
    # is_valid = verify_webhook(request.body, signature, timestamp, API_KEY)

    Delivery behavior

    Webhooks are delivered on a best-effort, fire-and-forget basis. If your endpoint is unreachable or returns an error, the webhook is not retried. The transcription still completes normally regardless of webhook delivery status.

    For reliable delivery, we recommend using webhooks as a speed optimization alongside polling GET /v1/transcribe/{id} as a fallback.

    Best practices

    • Return 200 quickly — Process the webhook payload asynchronously. Respond with 200 before doing heavy processing.
    • Always verify signatures — Check the HMAC-SHA256 signature before trusting any payload.
    • Check timestamps — Reject webhooks with timestamps older than 5 minutes to prevent replay attacks.
    • Be idempotent — The same webhook may be delivered more than once. Use the transcription ID to deduplicate.
    • Use HTTPS — Always use HTTPS for production webhook URLs. HTTP is allowed for local development only.
    • Use polling as fallback — Since webhooks are not retried, poll GET /v1/transcribe/{id} as a backup to ensure you don't miss completions.

    API Reference

    Full endpoint documentation

    Pricing

    Pay-as-you-go at $0.20/hour