Whisper API Developer Reference

Base URL: https://api.whisper.aspicho.me/api/v1

Authentication

Most endpoints require authentication via Bearer token (API key) or session ID:

API Key (Bearer Token)

Authorization: Bearer <your-api-key>

Session ID (Basic Auth)

Authorization: Basic <session-id>

Note: API keys are UUIDs without hyphens (32 hex characters). Sessions expire 24 hours after creation.

Health Check

GET /health

Authentication: None

Check server and database status.

Response (200 OK)

{
  "status": "ok"
}

User Management

POST /user/register

Register new user account

Authentication: None

Request Body

{
  "username": "john_doe",
  "password": "SecureP@ssw0rd!"
}

Validation Rules

Response (200 OK)

{
  "message": "User registered and logged in successfully",
  "username": "john_doe",
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f"
}

Error Responses

400 Bad Request: "Invalid username. Must be 3-20 characters..."
400 Bad Request: "Password must be at least 12 characters long"
400 Bad Request: "Password must contain at least 1 uppercase letter(s)"
409 Conflict: "Username already exists"

POST /user/login

Login with credentials

Authentication: None

Request Body

{
  "username": "john_doe",
  "password": "SecureP@ssw0rd!"
}

Response (200 OK)

{
  "message": "Login successful",
  "username": "john_doe",
  "is_admin": false,
  "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f"
}

Error Responses

400 Bad Request: "Missing username"
401 Unauthorized: "Authentication failed"

POST /user/logout

End current or specified session

Authentication: Basic (session only, not API key)

Request Body (Optional)

{
  "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f"
}

Response (200 OK)

{
  "message": "Logged out successfully"
}

Error Responses

400 Bad Request: "Cannot logout with API key. Use session authentication"
403 Forbidden: "Cannot logout from another user's session"
404 Not Found: "Session not found or already ended"

GET /user/me

Get current user profile

Authentication: Bearer or Basic

Response (200 OK)

{
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "username": "john_doe",
  "is_admin": false,
  "registered_at": "2025-01-15 10:30:45",
  "token_balance": 1000,
  "avatar_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70"
}

Data Model: User

PATCH /user/me

Update user profile

Authentication: Bearer or Basic

Content-Type: multipart/form-data

Form Fields (all optional)

Response (200 OK)

{
  "message": "Profile updated successfully",
  "updated_fields": ["username", "avatar"]
}

Error Responses

400 Bad Request: "No fields to update..."
400 Bad Request: "old_password is required to update password"
400 Bad Request: "Avatar file too large. Maximum size is 10MB"
401 Unauthorized: "Incorrect old password"
409 Conflict: "Username already taken"

DELETE /user/me

Delete account and all associated data

Authentication: Bearer or Basic

Request Body

{
  "password": "SecureP@ssw0rd!"
}

Response (200 OK)

{
  "message": "Account deleted successfully"
}

Error Responses

400 Bad Request: "Password is required to delete account"
401 Unauthorized: "Incorrect password"

API Tokens

GET /user/token

List all API tokens for current user

Authentication: Bearer or Basic

Response (200 OK)

{
  "tokens": [
    {
      "token_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
      "token": "018c...5d6f",
      "name": "Production API Key",
      "created_at": "2025-01-15 10:30:45"
    },
    {
      "token_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70",
      "token": "018c...5d71",
      "name": "Development Key",
      "created_at": "2025-01-16 14:22:10"
    }
  ]
}

Data Model: API Token (List)

POST /user/token

Create new API token

Authentication: Bearer or Basic

Request Body

{
  "name": "Production API Key"
}

Validation

Response (200 OK)

{
  "message": "Token issued successfully",
  "token_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "token": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f",
  "name": "Production API Key",
  "note": "Save this token securely. You won't be able to see it again."
}

⚠️ Important: The full token is only shown once. Store it securely!

Error Responses

400 Bad Request: "Missing 'name' field in request body"
400 Bad Request: "Token name must be between 1 and 255 characters"

DELETE /user/token/:token_id

Revoke API token

Authentication: Bearer or Basic

URL Parameters

Response (200 OK)

{
  "message": "Token revoked successfully",
  "token_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "token_name": "Production API Key"
}

Error Responses

400 Bad Request: "Invalid token ID format"
404 Not Found: "Token not found or access denied"

Sessions

GET /user/sessions

List active sessions for current user

Authentication: Bearer or Basic

Response (200 OK)

{
  "sessions": [
    {
      "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f",
      "started_at": "2025-01-15 10:30:45",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)..."
    },
    {
      "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70",
      "started_at": "2025-01-16 08:15:22",
      "user_agent": "curl/7.84.0"
    }
  ]
}

Data Model: Session

Note: Only returns sessions that haven't ended and are within 24 hours of creation.

File Management

POST /file

Upload audio/video files for transcription

Authentication: Bearer or Basic

Content-Type: multipart/form-data

Limits

Supported Formats

Audio: mp3, wav, flac, m4a, aac, ogg, opus, webm, wma, aiff, alac

Video: mp4, mov, avi, wmv, mpeg, 3gp, mkv, webm, flv, ts, mts, vob, rmvb, divx

Processing

Response (200 OK)

{
  "files": [
    {
      "message": "File uploaded successfully",
      "filename": "interview.mp3",
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
      "hash": "abc123def456...",
      "status": {
        "existed": false,
        "uploaded": true
      }
    },
    {
      "message": "File already exists",
      "filename": "podcast.wav",
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70",
      "hash": "def789ghi012...",
      "status": {
        "existed": true,
        "uploaded": false
      }
    },
    {
      "message": "Unsupported content type",
      "filename": "document.pdf",
      "status": {
        "existed": false,
        "uploaded": false,
        "error": "unsupported_content_type"
      }
    }
  ]
}

Data Model: Upload Result

GET /file

List user's files with pagination

Authentication: Bearer or Basic

Query Parameters

Response (200 OK)

{
  "files": [
    {
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
      "filename": "interview.mp3",
      "content_type": "audio/mpeg",
      "size_bytes": 5242880,
      "uploaded_at": "2025-01-15 10:30:45"
    },
    {
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70",
      "filename": "podcast_episode_1.mp3",
      "content_type": "audio/mpeg",
      "size_bytes": 12582912,
      "uploaded_at": "2025-01-14 08:22:10"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total_count": 25,
    "total_pages": 3
  }
}

Data Model: File

GET /file/:uuid

Download file by UUID

Authentication: None (public access via UUID)

URL Parameters

Response Headers

Content-Type: audio/mpeg (or appropriate MIME type)
Content-Length: 5242880
Content-Disposition: inline; filename="interview.mp3"
Accept-Ranges: bytes

Response

Binary file data with appropriate content type.

Error Responses

400 Bad Request: "Invalid UUID format"
404 Not Found: "File not found or access denied"
404 Not Found: "File not found on disk"

PATCH /file/:uuid

Update filename

Authentication: Bearer or Basic

Request Body

{
  "filename": "new_interview_name.mp3"
}

Validation

Response (200 OK)

{
  "message": "Filename updated successfully",
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "old_filename": "interview.mp3",
  "new_filename": "new_interview_name.mp3"
}

Error Responses

400 Bad Request: "Missing 'filename' field in request body"
400 Bad Request: "Filename must be between 1 and 255 characters"
404 Not Found: "File not found or access denied"

DELETE /file/:uuid

Delete file and WAV conversion

Authentication: Bearer or Basic

Response (200 OK)

{
  "message": "File deleted successfully",
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "filename": "interview.mp3"
}

Error Responses

400 Bad Request: "Invalid UUID format"
404 Not Found: "File not found or access denied"

Note: Deletes both the converted file (MP3/MP4) and the WAV version used for transcription.

Transcription

GET /model

List available Whisper models

Authentication: Bearer or Basic

Response (200 OK) - Regular User

{
  "models": [
    {
      "name": "ggml-base.en"
    },
    {
      "name": "ggml-medium"
    },
    {
      "name": "ggml-large-v3"
    }
  ]
}

Response (200 OK) - Admin User

{
  "models": [
    {
      "name": "ggml-base.en",
      "size": 147964211
    },
    {
      "name": "ggml-medium",
      "size": 1533833503
    },
    {
      "name": "ggml-large-v3",
      "size": 3094625823
    }
  ]
}

Data Model: Model

POST /transcribe

Queue transcription job(s)

Authentication: Bearer or Basic

Request Body

{
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "model": "ggml-base.en",
  "language": "en",
  "format": ["verbose_json", "srt", "vtt"]
}

Request Fields

Token Cost

Cost = ceil(duration_seconds) × price_multiplier

Response (200 OK)

{
  "message": "Transcription(s) queued successfully",
  "transcriptions": [
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
      "format": "verbose_json",
      "status": "pending"
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e30",
      "format": "srt",
      "status": "pending"
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e31",
      "format": "vtt",
      "status": "pending"
    }
  ]
}

Error Responses

400 Bad Request: "Missing file uuid"
400 Bad Request: "Invalid format: txt. Must be one of: verbose_json, srt, vtt"
400 Bad Request: "At least one format must be specified"
404 Not Found: "File not found or access denied"

GET /transcribe/:uuid

Get transcription status and result

Authentication: None (public access)

URL Parameters

Behavior:

Response - Single Transcription (200 OK)

{
  "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
  "file_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "model": "ggml-base.en",
  "language": "en",
  "format": "verbose_json",
  "status": "completed",
  "processed_at": "2025-01-15 10:35:20",
  "data": {
    "text": "Hello, this is a test transcription.",
    "segments": [
      {
        "start": 0.0,
        "end": 3.5,
        "text": "Hello, this is a test transcription."
      }
    ],
    "language": "en"
  }
}

Response - Multiple Transcriptions (200 OK)

{
  "transcriptions": [
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
      "file_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
      "model": "ggml-base.en",
      "language": "en",
      "format": "verbose_json",
      "status": "completed",
      "processed_at": "2025-01-15 10:35:20",
      "data": { ... }
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e30",
      "file_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
      "model": "ggml-base.en",
      "language": "en",
      "format": "srt",
      "status": "processing",
      "processed_at": "1970-01-01 00:00:00",
      "data": null
    }
  ]
}

Status Values

Response - Failed Transcription (200 OK)

{
  "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
  "file_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "model": "ggml-base.en",
  "language": "en",
  "format": "verbose_json",
  "status": "failed",
  "error": "Insufficient token balance. Required: 120, Available: 50",
  "processed_at": "1970-01-01 00:00:00"
}

Data Formats

verbose_json format:

{
  "text": "Full transcription text here...",
  "segments": [
    {
      "start": 0.0,
      "end": 3.5,
      "text": "First segment text."
    },
    {
      "start": 3.5,
      "end": 8.2,
      "text": "Second segment text."
    }
  ],
  "language": "en"
}

srt format:

{
  "text": "1\n00:00:00,000 --> 00:00:03,500\nFirst segment text.\n\n2\n00:00:03,500 --> 00:00:08,200\nSecond segment text."
}

vtt format:

{
  "text": "WEBVTT\n\n00:00:00.000 --> 00:00:03.500\nFirst segment text.\n\n00:00:03.500 --> 00:00:08.200\nSecond segment text."
}

Error Responses

400 Bad Request: "Invalid UUID format"
404 Not Found: "Transcription not found"

DELETE /transcribe/:uuid

Delete transcription record

Authentication: Bearer or Basic

Response (200 OK)

{
  "message": "Transcription deleted successfully",
  "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
  "model": "ggml-base.en",
  "format": "verbose_json"
}

Error Responses

400 Bad Request: "Invalid UUID format"
404 Not Found: "Transcription not found or access denied"

Admin Endpoints

GET /admin/users

List all users with detailed statistics

Authentication: Bearer or Basic (admin only)

Query Parameters

Response (200 OK)

{
  "users": [
    {
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
      "username": "john_doe",
      "is_admin": false,
      "registered_at": "2025-01-15 10:30:45",
      "token_balance": 1000,
      "price_multiplier": 1.0,
      "avatar_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70",
      "stats": {
        "last_30_days": {
          "transcriptions_count": 15,
          "total_audio_seconds": 3600,
          "files_uploaded": 12
        },
        "total_storage_bytes": 524288000,
        "active_sessions": 2,
        "api_keys_count": 3
      }
    },
    {
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d71",
      "username": "jane_smith",
      "is_admin": false,
      "registered_at": "2025-01-10 08:15:22",
      "token_balance": 500,
      "price_multiplier": 0.8,
      "avatar_uuid": null,
      "stats": {
        "last_30_days": {
          "transcriptions_count": 8,
          "total_audio_seconds": 1800,
          "files_uploaded": 6
        },
        "total_storage_bytes": 157286400,
        "active_sessions": 1,
        "api_keys_count": 1
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total_count": 42,
    "total_pages": 5
  }
}

Data Model: Admin User Stats

Error Responses

403 Forbidden: "Access denied. Admin privileges required"

PATCH /admin/users/:uuid

Update user settings

Authentication: Bearer or Basic (admin only)

URL Parameters

Request Body

{
  "token_balance": 5000,
  "price_multiplier": 0.5
}

Request Fields (all optional)

Response (200 OK)

{
  "message": "User updated successfully",
  "updated_fields": ["token_balance", "price_multiplier"],
  "user": {
    "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
    "username": "john_doe",
    "token_balance": 5000,
    "price_multiplier": 0.5
  }
}

Error Responses

400 Bad Request: "Invalid UUID format"
400 Bad Request: "token_balance must be an integer"
400 Bad Request: "price_multiplier must be non-negative"
400 Bad Request: "price_multiplier must be at most 1000"
400 Bad Request: "No valid fields to update..."
403 Forbidden: "Access denied. Admin privileges required"
404 Not Found: "User not found"

Error Responses

All error responses return plain text with appropriate HTTP status codes.

HTTP Status Codes

CodeMeaningCommon Causes
200OKRequest succeeded
400Bad RequestInvalid input, validation errors, missing required fields
401UnauthorizedMissing/invalid authentication, incorrect password
403ForbiddenInsufficient permissions (admin required)
404Not FoundResource doesn't exist or access denied
409ConflictResource already exists (username taken, etc.)
500Internal Server ErrorServer-side error, database failures

Error Format

Errors are returned as plain text strings (not JSON).

Examples

HTTP/1.1 400 Bad Request
Invalid username. Must be 3-20 characters and contain only alphanumeric characters, underscores, or hyphens
HTTP/1.1 401 Unauthorized
Missing valid Authorization header with session or API key
HTTP/1.1 403 Forbidden
Access denied. Admin privileges required
HTTP/1.1 404 Not Found
File not found or access denied
HTTP/1.1 409 Conflict
Username already exists

Technical Details

UUIDs

All UUIDs use UUIDv7 format (time-ordered for better database indexing):

Timestamps

All timestamps use ISO 8601 format in UTC:

File Processing Pipeline

Audio Files

Upload → MP3 Conversion → WAV Conversion → Storage
         (192kbps, 44.1kHz)  (16kHz, mono, PCM s16le)

Video Files

Upload → MP4 Conversion → WAV Conversion (audio) → Storage
         (H.264, AAC 192kbps)  (16kHz, mono, PCM s16le)

Processing Specifications

Duplicate Detection

Files are deduplicated per user using BLAKE3 hash:

Session Management

API Key Management

Token Economy

Queue System

Example Workflows

1. Basic Registration & Transcription

# Step 1: Register new account
POST https://api.whisper.aspicho.me/api/v1/user/register
Content-Type: application/json

{
  "username": "developer",
  "password": "SecureP@ssw0rd123!"
}

# Response:
{
  "message": "User registered and logged in successfully",
  "username": "developer",
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
  "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f"
}

# Step 2: Create API token (using session)
POST https://api.whisper.aspicho.me/api/v1/user/token
Authorization: Basic 018c5f9a7b2e7a8c9d0e1f2a3b4c5d6f
Content-Type: application/json

{
  "name": "My First API Key"
}

# Response:
{
  "message": "Token issued successfully",
  "token_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d70",
  "token": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d71",
  "name": "My First API Key",
  "note": "Save this token securely. You won't be able to see it again."
}

# Step 3: Upload audio file (using API token)
POST https://api.whisper.aspicho.me/api/v1/file
Authorization: Bearer 018c5f9a7b2e7a8c9d0e1f2a3b4c5d71
Content-Type: multipart/form-data

file=@podcast_episode.mp3

# Response:
{
  "files": [
    {
      "message": "File uploaded successfully",
      "filename": "podcast_episode.mp3",
      "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d72",
      "hash": "abc123...",
      "status": {
        "existed": false,
        "uploaded": true
      }
    }
  ]
}

# Step 4: Queue transcription
POST https://api.whisper.aspicho.me/api/v1/transcribe
Authorization: Bearer 018c5f9a7b2e7a8c9d0e1f2a3b4c5d71
Content-Type: application/json

{
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d72",
  "model": "ggml-base.en",
  "language": "en",
  "format": "verbose_json"
}

# Response:
{
  "message": "Transcription(s) queued successfully",
  "transcriptions": [
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
      "format": "verbose_json",
      "status": "pending"
    }
  ]
}

# Step 5: Check transcription status (public, no auth needed)
GET https://api.whisper.aspicho.me/api/v1/transcribe/018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f

# Response (when completed):
{
  "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
  "file_uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d72",
  "model": "ggml-base.en",
  "language": "en",
  "format": "verbose_json",
  "status": "completed",
  "processed_at": "2025-01-15 10:35:20",
  "data": {
    "text": "Welcome to episode 42 of our podcast...",
    "segments": [...]
  }
}

2. Multiple Format Transcription

# Request transcription in multiple formats at once
POST https://api.whisper.aspicho.me/api/v1/transcribe
Authorization: Bearer <api-key>
Content-Type: application/json

{
  "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d72",
  "model": "ggml-medium",
  "language": "en",
  "format": ["verbose_json", "srt", "vtt"]
}

# Response: 3 separate transcription jobs created
{
  "message": "Transcription(s) queued successfully",
  "transcriptions": [
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
      "format": "verbose_json",
      "status": "pending"
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e30",
      "format": "srt",
      "status": "pending"
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e31",
      "format": "vtt",
      "status": "pending"
    }
  ]
}

# Check all transcriptions for a file (using file UUID)
GET https://api.whisper.aspicho.me/api/v1/transcribe/018c5f9a7b2e7a8c9d0e1f2a3b4c5d72

# Response: Returns all transcriptions for that file
{
  "transcriptions": [
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e2f",
      "format": "verbose_json",
      "status": "completed",
      "data": {...}
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e30",
      "format": "srt",
      "status": "completed",
      "data": {...}
    },
    {
      "uuid": "018c5f9b1a2b3c4d5e6f7a8b9c0d1e31",
      "format": "vtt",
      "status": "processing",
      "data": null
    }
  ]
}

3. Admin User Management

# Step 1: Login as admin
POST https://api.whisper.aspicho.me/api/v1/user/login
Content-Type: application/json

{
  "username": "admin",
  "password": "admin_password"
}

# Response:
{
  "message": "Login successful",
  "username": "admin",
  "is_admin": true,
  "session_id": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d80"
}

# Step 2: Create admin API token
POST https://api.whisper.aspicho.me/api/v1/user/token
Authorization: Basic 018c5f9a7b2e7a8c9d0e1f2a3b4c5d80
Content-Type: application/json

{
  "name": "Admin API Key"
}

# Step 3: List all users
GET https://api.whisper.aspicho.me/api/v1/admin/users?page=1&limit=20
Authorization: Bearer <admin-api-key>

# Response includes detailed stats for each user

# Step 4: Grant tokens to user
PATCH https://api.whisper.aspicho.me/api/v1/admin/users/018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e
Authorization: Bearer <admin-api-key>
Content-Type: application/json

{
  "token_balance": 10000,
  "price_multiplier": 0.5
}

# Response:
{
  "message": "User updated successfully",
  "updated_fields": ["token_balance", "price_multiplier"],
  "user": {
    "uuid": "018c5f9a7b2e7a8c9d0e1f2a3b4c5d6e",
    "username": "developer",
    "token_balance": 10000,
    "price_multiplier": 0.5
  }
}

4. cURL Examples

# Register user
curl -X POST https://api.whisper.aspicho.me/api/v1/user/register \
  -H "Content-Type: application/json" \
  -d '{"username":"myuser","password":"SecureP@ss123!"}'

# Upload file
curl -X POST https://api.whisper.aspicho.me/api/v1/file \
  -H "Authorization: Bearer <api-key>" \
  -F "file=@audio.mp3"

# Start transcription
curl -X POST https://api.whisper.aspicho.me/api/v1/transcribe \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "uuid":"<file-uuid>",
    "model":"ggml-base.en",
    "format":"verbose_json"
  }'

# Check status
curl https://api.whisper.aspicho.me/api/v1/transcribe/<transcription-uuid>

# Download file (no auth needed)
curl -O https://api.whisper.aspicho.me/api/v1/file/<file-uuid>

5. Python Example

import requests
import time

# Configuration
API_BASE = "https://api.whisper.aspicho.me/api/v1"
API_KEY = "your-api-key-here"

# Upload file
with open("audio.mp3", "rb") as f:
    response = requests.post(
        f"{API_BASE}/file",
        headers={"Authorization": f"Bearer {API_KEY}"},
        files={"file": f}
    )
    file_uuid = response.json()["files"][0]["uuid"]
    print(f"Uploaded file: {file_uuid}")

# Queue transcription
response = requests.post(
    f"{API_BASE}/transcribe",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "uuid": file_uuid,
        "model": "ggml-base.en",
        "format": "verbose_json"
    }
)
transcription_uuid = response.json()["transcriptions"][0]["uuid"]
print(f"Transcription queued: {transcription_uuid}")

# Poll for completion
while True:
    response = requests.get(f"{API_BASE}/transcribe/{transcription_uuid}")
    data = response.json()
    status = data["status"]
    print(f"Status: {status}")
    
    if status == "completed":
        print("Transcription:", data["data"]["text"])
        break
    elif status == "failed":
        print("Error:", data.get("error"))
        break
    
    time.sleep(5)  # Wait 5 seconds before checking again