{"openapi":"3.1.0","info":{"title":"C-elo API","version":"1.0.0","summary":"Streaming ASR for African languages","description":"Create short-lived WebSocket sessions for C-elo Kikuyu and Dholuo ASR. Permanent API keys are server-side credentials and must never be embedded in browser or mobile client code.","contact":{"name":"C-elo Labs","url":"https://c-elo.com/developers"},"license":{"name":"Proprietary","identifier":"LicenseRef-Celo-Proprietary"}},"servers":[{"url":"https://api.c-elo.com","description":"Production"}],"tags":[{"name":"Models","description":"Available ASR models."},{"name":"ASR sessions","description":"Create authenticated streaming sessions."},{"name":"Usage","description":"Credit balance and recent usage."}],"paths":{"/v1/models":{"get":{"operationId":"listModels","tags":["Models"],"summary":"List models","security":[],"responses":{"200":{"description":"Available models.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}}},"required":["object","data"]}}}}}}},"/v1/asr/sessions":{"post":{"operationId":"createAsrSession","tags":["ASR sessions"],"summary":"Create a streaming session","description":"Uses a server-side API key to reserve up to the requested audio limit and returns a 60-second, one-use WebSocket credential. If at least one second remains but the available balance is lower than requested, the response reports the shorter reserved limit. Send the returned values as WebSocket subprotocols in the listed order.","security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A unique 1–128 character key. Repeating it before the returned session expires returns the same reservation.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionRequest"},"examples":{"kikuyu":{"value":{"model":"celo-asr-kikuyu-v1","max_audio_seconds":60}}}}}},"responses":{"201":{"description":"Session reserved. Use the returned audio.max_audio_seconds and credits.reserved values as the actual limit.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsrSession"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/v1/usage":{"get":{"operationId":"getUsage","tags":["Usage"],"summary":"Get credit balance","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current balance and recent sessions.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditBalance"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"C-elo API key","description":"Use a server-side key beginning with celo_live_v1_. Never expose it in browser code."}},"schemas":{"Model":{"type":"object","properties":{"id":{"type":"string","enum":["celo-asr-kikuyu-v1","celo-asr-dholuo-v1"]},"object":{"const":"model"},"capability":{"const":"asr.streaming"},"language":{"type":"string"},"language_code":{"type":"string"},"max_audio_seconds":{"type":"integer","const":300},"audio":{"type":"object","properties":{"encoding":{"const":"pcm_s16le"},"sample_rate_hz":{"const":16000},"channels":{"const":1}},"required":["encoding","sample_rate_hz","channels"]}},"required":["id","object","capability","language","language_code","audio","max_audio_seconds"]},"CreateSessionRequest":{"type":"object","additionalProperties":false,"properties":{"model":{"type":"string","enum":["celo-asr-kikuyu-v1","celo-asr-dholuo-v1"]},"max_audio_seconds":{"type":"integer","minimum":1,"maximum":300,"default":60,"description":"Requested upper bound. The returned reservation may be lower when fewer credits are available."}},"required":["model"]},"AsrSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"const":"asr.session"},"status":{"const":"reserved"},"model":{"type":"string","enum":["celo-asr-kikuyu-v1","celo-asr-dholuo-v1"]},"websocket_url":{"type":"string","format":"uri","pattern":"^wss://"},"websocket_protocols":{"type":"array","minItems":2,"maxItems":2,"prefixItems":[{"const":"celo.realtime.v1"},{"type":"string","pattern":"^celo\\.auth\\.","description":"One-use, short-lived session credential. Treat it as a secret."}]},"expires_at":{"type":"string","format":"date-time"},"audio":{"type":"object","properties":{"encoding":{"const":"pcm_s16le"},"sample_rate_hz":{"const":16000},"channels":{"const":1},"max_audio_seconds":{"type":"number","minimum":1,"maximum":300,"description":"Actual reserved audio limit for this session."},"context_size":{"type":"array","minItems":2,"maxItems":2,"prefixItems":[{"const":56},{"const":13}]}},"required":["encoding","sample_rate_hz","channels","max_audio_seconds","context_size"]},"credits":{"type":"object","properties":{"unit":{"const":"audio_second"},"reserved":{"type":"number","minimum":1,"description":"Actual audio seconds reserved for this session."},"remaining":{"type":"number","minimum":0}},"required":["unit","reserved","remaining"]}},"required":["id","object","status","model","websocket_url","websocket_protocols","expires_at","audio","credits"]},"CreditBalance":{"type":"object","properties":{"object":{"const":"credit_balance"},"unit":{"const":"audio_millisecond"},"available_ms":{"type":"integer","minimum":0},"reserved_ms":{"type":"integer","minimum":0},"consumed_ms":{"type":"integer","minimum":0},"recent_sessions":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/UsageSession"}}},"required":["object","unit","available_ms","reserved_ms","consumed_ms","recent_sessions"]},"UsageSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"model":{"type":"string","enum":["celo-asr-kikuyu-v1","celo-asr-dholuo-v1"]},"status":{"type":"string","enum":["reserved","active","completed","failed","expired"]},"audio_ms":{"type":"integer","minimum":0},"billable_ms":{"type":"integer","minimum":0},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"}},"required":["id","model","status","audio_ms","billable_ms","created_at","completed_at"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"request_id":{"type":"string","format":"uuid"}},"required":["type","code","message","request_id"]}},"required":["error"]}},"headers":{"RequestId":{"description":"C-elo support and troubleshooting identifier.","schema":{"type":"string","format":"uuid"}},"RetryAfter":{"description":"Seconds to wait before retrying the request.","schema":{"type":"integer","minimum":0}}},"responses":{"BadRequest":{"description":"The request was invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key was missing or invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InsufficientCredits":{"description":"The account has no available audio credits.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key or account lacks permission.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The model was not found.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The request or idempotency key conflicts.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeded 16 KiB.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnsupportedMediaType":{"description":"The request did not use application/json.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"A session or concurrency limit was reached.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The service is temporarily unavailable.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}