API reference
Fjalim API reference
Fjalim turns Albanian speech into text. Upload a file, start a transcription, and read the transcript as JSON, plain text, SRT or WebVTT.
Prices. €0.012 per minute, billed per started second (200 µ€ per second). All amounts in this API are integer micro-euros (µ€, 1 € = 1,000,000 µ€).
Errors. Every error has the same shape: {"error": {"code": "insufficient_credits", "message": "..."}}.
Webhooks. Set webhook_url when you create a transcription. Fjalim sends a POST with {"id", "event", "created_at", "data"} where data is the transcription object. The Fjalim-Signature header is t=<unix seconds>,v1=<hex HMAC-SHA256>; the HMAC key is your webhook secret and the signed text is "<t>.<raw body>". A 2xx response confirms delivery. Otherwise Fjalim retries after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h, then gives up. Each request times out after 10 s.
Servers
https://api.fjalim.com: Productionhttp://localhost:8787: Local development
Authentication
bearerKey: An API key:Authorization: Bearer fj_live_.... Create keys in the web app.sessionCookie: Web app session. State-changing requests must come from an allowedOrigin.trialToken:
Uploads
/v1/uploads Start an upload
Auth: bearerKey, sessionCookie
Returns a presigned URL (valid for one hour). PUT exactly size_bytes bytes to it with the returned headers; the URL is signed for that length, so a different size is refused by storage. Then call the complete route.
Request body application/json
| Name | Type | Description |
|---|---|---|
filenamerequired | string | |
content_typerequired | string | |
size_bytesrequired | integer |
{
"filename": "intervista-kryeministri.mp3",
"content_type": "audio/mpeg",
"size_bytes": 18874368
} Responses
201Upload created.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
upload_urlrequired | string (uri) | |
methodrequired | "PUT" | |
headersrequired | object | Send these headers with the PUT. |
expires_atrequired | string (date-time) |
400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} 401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 429More than 60 requests in a minute (rate_limited). See Retry-After.
- Header
Retry-Afterinteger
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/v1/uploads/{id}/complete Confirm that the file was uploaded
Auth: bearerKey, sessionCookie
Fjalim checks that the object exists and that its size matches size_bytes.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200The upload is ready to transcribe.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
statusrequired | "uploaded" | |
size_bytesrequired | integer |
400The file is not there yet (upload_incomplete) or has a different size (size_mismatch).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} Transcriptions
/v1/transcriptions List transcriptions, newest first
Auth: bearerKey, sessionCookie
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer | |
cursor | query | string (uuid) | The |
Responses
200One page.
application/json
| Name | Type | Description |
|---|---|---|
datarequired | Transcription[] | |
data[].idrequired | string (uuid) | |
data[].statusrequired | "queued" | "processing" | "completed" | "failed" | "canceled" | |
data[].created_atrequired | string (date-time) | |
data[].completed_atrequired | string (date-time) | null | |
data[].duration_secondsrequired | number | null | |
data[].billed_secondsrequired | integer | null | |
data[].cost_micro_eurrequired | integer | null | |
data[].errorrequired | object | null | |
data[].error.code | string | |
data[].error.message | string | |
data[].metadatarequired | object | |
data[].versionrequired | integer | Latest transcript version. 0 until the transcript exists. |
data[].filenamerequired | string | null | |
data[].content_typerequired | string | null | |
next_cursorrequired | string (uuid) | null |
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} /v1/transcriptions Start a transcription
Auth: bearerKey, sessionCookie
Send exactly one of upload_id and audio_url (https only). The balance must be at least 200 µ€, otherwise the answer is 402 insufficient_credits. Send an Idempotency-Key header to make retries safe: the same key with the same body returns the same transcription. The same key with a different body is refused with 422 idempotency_conflict. Billing happens when the worker has measured the audio duration. The uploaded object is checked again here: 400 upload_incomplete if it is missing, 400 size_mismatch if its size changed.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-Key | header | string |
Request body application/json
| Name | Type | Description |
|---|---|---|
upload_id | string (uuid) | |
audio_url | string (uri) | https only; must not point to a private address. |
webhook_url | string (uri) | |
metadata | object | Your own data, at most 4 KB as JSON. |
options | object | Reserved. Send nothing or an empty object. |
{
"upload_id": "0b6f1c2e-7b0a-4a52-9c1f-3c5d1a8e2f10",
"webhook_url": "https://shembull.al/webhooks/fjalim",
"metadata": {
"emisioni": "Studio Live",
"episodi": 42
}
}{
"audio_url": "https://shembull.al/podcast/episodi-42.mp3"
} Responses
202The transcription is queued.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
statusrequired | "queued" | "processing" | "completed" | "failed" | "canceled" | |
created_atrequired | string (date-time) | |
completed_atrequired | string (date-time) | null | |
duration_secondsrequired | number | null | |
billed_secondsrequired | integer | null | |
cost_micro_eurrequired | integer | null | |
errorrequired | object | null | |
error.code | string | |
error.message | string | |
metadatarequired | object | |
versionrequired | integer | Latest transcript version. 0 until the transcript exists. |
filenamerequired | string | null | |
content_typerequired | string | null |
{
"id": "5f4a3c2b-1d0e-4f9a-8b7c-6d5e4f3a2b1c",
"status": "queued",
"created_at": "2026-11-03T09:12:40.000Z",
"completed_at": null,
"duration_seconds": null,
"billed_seconds": null,
"cost_micro_eur": null,
"error": null,
"metadata": {
"emisioni": "Studio Live",
"episodi": 42
},
"version": 0,
"filename": "intervista-kryeministri.mp3",
"content_type": "audio/mpeg"
} 400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} 401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 402The balance is below 200 µ€ (insufficient_credits).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "insufficient_credits",
"message": "Your balance is too low. Add credit to start a transcription."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} 422The Idempotency-Key was already used with a different request body (idempotency_conflict).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "idempotency_conflict",
"message": "This Idempotency-Key was already used with a different request body."
}
} 429More than 60 requests in a minute (rate_limited). See Retry-After.
- Header
Retry-Afterinteger
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/v1/transcriptions/{id} Get a transcription
Auth: bearerKey, sessionCookie
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200The transcription.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
statusrequired | "queued" | "processing" | "completed" | "failed" | "canceled" | |
created_atrequired | string (date-time) | |
completed_atrequired | string (date-time) | null | |
duration_secondsrequired | number | null | |
billed_secondsrequired | integer | null | |
cost_micro_eurrequired | integer | null | |
errorrequired | object | null | |
error.code | string | |
error.message | string | |
metadatarequired | object | |
versionrequired | integer | Latest transcript version. 0 until the transcript exists. |
filenamerequired | string | null | |
content_typerequired | string | null |
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} /v1/transcriptions/{id} Delete the audio and every transcript version
Auth: bearerKey, sessionCookie
The billing fields stay on the record. This cannot be undone.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
204Deleted.
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} /v1/transcriptions/{id}/transcript Read the transcript
Auth: bearerKey, sessionCookie
The response carries ETag and X-Transcript-Version. Caption options apply only when the transcript has no edited captions.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
format | query | "json" | "txt" | "srt" | "vtt" | |
version | query | integer | Defaults to the latest version. |
timestamps | query | boolean | txt only. Put [hh:mm:ss] before each segment. |
speakers | query | boolean | txt only. Put the speaker name before each segment. |
max_chars_per_line | query | integer | |
max_lines | query | integer | |
max_cps | query | number | |
min_duration | query | number | |
max_duration | query | number | |
min_gap | query | number |
Responses
200The transcript in the requested format.
- Header
ETagstringThe version, for example `"2"`. - Header
X-Transcript-Versioninteger - Header
Content-Dispositionstring`attachment` with a file name made from the upload's name and the format, for example `attachment; filename="intervista.srt"; filename*=UTF-8''intervist%C3%AB.srt`. The plain `filename` is ASCII; `filename*` (RFC 6266 / RFC 5987) keeps characters such as ë and ç. Without an upload name the file is called `transcript.<format>`.
application/json
| Name | Type | Description |
|---|---|---|
versionrequired | 1 | |
languagerequired | "sq" | |
modelrequired | string | |
durationrequired | number | |
textrequired | string | |
segmentsrequired | Segment[] | |
segments[].idrequired | integer | |
segments[].startrequired | number | |
segments[].endrequired | number | |
segments[].speakerrequired | string | null | |
segments[].textrequired | string | |
segments[].wordsrequired | Word[] | |
segments[].words[].textrequired | string | |
segments[].words[].startrequired | number | |
segments[].words[].endrequired | number | |
segments[].words[].confidencerequired | number | null | |
captionsrequired | Cue[] | null | Edited caption cues. |
text/plain · string
Mirëdita. Sot flasim për zgjedhjet.
application/x-subrip · string
1
00:00:00,320 --> 00:00:02,900
Mirëdita.
Sot flasim për zgjedhjet.
text/vtt · string
WEBVTT
1
00:00:00.320 --> 00:00:02.900
Mirëdita.
Sot flasim për zgjedhjet.
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} 409The transcript is not ready (not_ready) or the job failed (transcript_unavailable).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/v1/transcriptions/{id}/transcript Save an edited transcript as a new version
Auth: bearerKey, sessionCookie
Send the version you edited in If-Match. If someone saved in between, the answer is 409 version_conflict.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
If-Matchrequired | header | string |
Request body application/json
| Name | Type | Description |
|---|---|---|
versionrequired | 1 | |
languagerequired | "sq" | |
modelrequired | string | |
durationrequired | number | |
textrequired | string | |
segmentsrequired | Segment[] | |
segments[].idrequired | integer | |
segments[].startrequired | number | |
segments[].endrequired | number | |
segments[].speakerrequired | string | null | |
segments[].textrequired | string | |
segments[].wordsrequired | Word[] | |
segments[].words[].textrequired | string | |
segments[].words[].startrequired | number | |
segments[].words[].endrequired | number | |
segments[].words[].confidencerequired | number | null | |
captionsrequired | Cue[] | null | Edited caption cues. |
Responses
200Saved. version is the new version.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
statusrequired | "queued" | "processing" | "completed" | "failed" | "canceled" | |
created_atrequired | string (date-time) | |
completed_atrequired | string (date-time) | null | |
duration_secondsrequired | number | null | |
billed_secondsrequired | integer | null | |
cost_micro_eurrequired | integer | null | |
errorrequired | object | null | |
error.code | string | |
error.message | string | |
metadatarequired | object | |
versionrequired | integer | Latest transcript version. 0 until the transcript exists. |
filenamerequired | string | null | |
content_typerequired | string | null |
400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} 401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} 409version_conflict (stale If-Match) or not_ready.
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "version_conflict",
"message": "The transcript changed. Reload it and apply your edit again."
}
} 428If-Match is missing (precondition_required).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/v1/transcriptions/{id}/audio Play the original audio
Auth: bearerKey, sessionCookie
Redirects to a presigned URL that works for one hour.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
302Redirect to the audio file.
- Header
Locationstring (uri)
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} Account
/v1/balance Current balance
Auth: bearerKey, sessionCookie
Responses
200The balance in micro-euros. It can be negative after a refund.
application/json
| Name | Type | Description |
|---|---|---|
balance_micro_eurrequired | integer | |
currencyrequired | "EUR" |
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} /v1/usage Usage per day
Auth: bearerKey, sessionCookie
Parameters
| Name | In | Type | Description |
|---|---|---|---|
from | query | string (date) | First day (UTC), default 30 days before |
to | query | string (date) | Last day (UTC), default today. |
Responses
200Billed seconds and cost for each day that had usage.
application/json
| Name | Type | Description |
|---|---|---|
fromrequired | string (date) | |
torequired | string (date) | |
daysrequired | object[] | |
days[].daterequired | string (date) | |
days[].secondsrequired | integer | |
days[].cost_micro_eurrequired | integer | |
days[].transcriptionsrequired | integer |
400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} 401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} /auth/signup Create an account (web app)
Auth: none
Sets the fj_session cookie. The email must be verified to receive the 15 free minutes. The answer is the same whether or not the email is already registered, so it cannot be used to find out who has an account. If the account exists, its owner gets an email instead, and the cookie you receive is not a valid session.
Request body application/json
| Name | Type | Description |
|---|---|---|
emailrequired | string (email) | |
passwordrequired | string | |
name | string | |
locale | "sq" | "en" |
{
"email": "arta@shembull.al",
"password": "një-fjalëkalim-i-gjatë",
"name": "Arta Krasniqi",
"locale": "sq"
} Responses
201Account created.
application/json
| Name | Type | Description |
|---|---|---|
account | Account | |
account.id | string (uuid) | |
account.email | string (email) | |
account.name | string | null | |
account.locale | "sq" | "en" | |
account.email_verified | boolean | |
account.balance_micro_eur | integer | |
account.created_at | string (date-time) |
400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} /auth/login Sign in (web app)
Auth: none
Request body application/json
| Name | Type | Description |
|---|---|---|
emailrequired | string (email) | |
passwordrequired | string |
Responses
200Signed in. Sets the fj_session cookie (30 days).
application/json
| Name | Type | Description |
|---|---|---|
account | Account | |
account.id | string (uuid) | |
account.email | string (email) | |
account.name | string | null | |
account.locale | "sq" | "en" | |
account.email_verified | boolean | |
account.balance_micro_eur | integer | |
account.created_at | string (date-time) |
401Wrong email or password (invalid_credentials).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/auth/logout Sign out (web app)
Auth: bearerKey, sessionCookie
Responses
204Signed out.
/auth/me The signed-in account (web app)
Auth: sessionCookie
Responses
200The account.
application/json
| Name | Type | Description |
|---|---|---|
account | Account | |
account.id | string (uuid) | |
account.email | string (email) | |
account.name | string | null | |
account.locale | "sq" | "en" | |
account.email_verified | boolean | |
account.balance_micro_eur | integer | |
account.created_at | string (date-time) |
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} /auth/verify Confirm an email address
Auth: none
Credits the 900 s signup grant the first time only.
Request body application/json
| Name | Type | Description |
|---|---|---|
tokenrequired | string |
Responses
200Verified.
application/json
| Name | Type | Description |
|---|---|---|
verified | boolean | |
signup_grant | boolean | True when this call credited the free minutes. |
400The link is invalid or expired (invalid_token).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/auth/resend Send the verification email again
Auth: sessionCookie
Responses
200The email was already verified.
202Sent.
/auth/password/forgot Ask for a password reset email
Auth: none
Always answers 202, whether or not the account exists.
Request body application/json
| Name | Type | Description |
|---|---|---|
emailrequired | string (email) |
Responses
202If the account exists
/auth/password/reset Choose a new password
Auth: none
Ends every session of the account.
Request body application/json
| Name | Type | Description |
|---|---|---|
tokenrequired | string | |
passwordrequired | string |
Responses
200Password changed.
400The link is invalid or expired (invalid_token).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/account/keys List API keys (web app)
Auth: sessionCookie
Responses
200Keys without their secret part.
application/json
| Name | Type | Description |
|---|---|---|
data | ApiKey[] | |
data[].idrequired | string (uuid) | |
data[].namerequired | string | |
data[].prefixrequired | string | The first 12 characters of the key. |
data[].created_atrequired | string (date-time) | |
data[].last_used_atrequired | string (date-time) | null | |
data[].revoked_atrequired | string (date-time) | null |
/account/keys Create an API key (web app)
Auth: sessionCookie
The full key is returned once. Store it safely.
Request body application/json
| Name | Type | Description |
|---|---|---|
namerequired | string |
{
"name": "Serveri i prodhimit"
} Responses
201Created.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
namerequired | string | |
prefixrequired | string | The first 12 characters of the key. |
created_atrequired | string (date-time) | |
last_used_atrequired | string (date-time) | null | |
revoked_atrequired | string (date-time) | null | |
keyrequired | string |
/account/keys/{id} Revoke an API key (web app)
Auth: sessionCookie
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
204Revoked.
404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} /account/webhook-secret Webhook signing secret (web app)
Auth: sessionCookie
Responses
200The secret.
application/json
| Name | Type | Description |
|---|---|---|
webhook_secret | string |
/account/webhook-secret/rotate Create a new webhook signing secret (web app)
Auth: sessionCookie
Responses
200The new secret.
application/json
| Name | Type | Description |
|---|---|---|
webhook_secret | string |
/billing/checkout Add credit with Stripe Checkout (web app)
Auth: sessionCookie
Request body application/json
| Name | Type | Description |
|---|---|---|
amount_eurrequired | integer | Whole euros. |
{
"amount_eur": 25
} Responses
200Send the browser to this URL.
application/json
| Name | Type | Description |
|---|---|---|
url | string (uri) |
400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} /billing/ledger Credit history (web app)
Auth: sessionCookie
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer | |
cursor | query | string |
Responses
200Newest first.
application/json
| Name | Type | Description |
|---|---|---|
data | LedgerEntry[] | |
data[].id | integer | |
data[].amount_micro_eur | integer | Positive for credit |
data[].kind | "topup" | "signup_grant" | "usage" | "usage_refund" | "refund" | "adjustment" | |
data[].job_id | string (uuid) | null | |
data[].note | string | null | |
data[].created_at | string (date-time) | |
next_cursor | string | null |
Trial
/trial/uploads Start a free trial upload
Auth: none
No account needed. The trial transcribes the first 60 seconds of one file. Limits: 3 per IP address per rolling 24 hours and 500 per UTC day overall (429 trial_limit or 429 trial_capacity). The Retry-After header says how many seconds until the limit frees up: until the oldest trial of that IP turns 24 hours old, or until the next UTC midnight for the overall cap. Files can be up to 100 MB.
Request body application/json
| Name | Type | Description |
|---|---|---|
filenamerequired | string | |
content_typerequired | string | |
size_bytesrequired | integer | |
turnstile_token | string |
{
"filename": "mesazh-zanor.m4a",
"content_type": "audio/mp4",
"size_bytes": 1048576
} Responses
201PUT the file to upload_url, then start the transcription with trial_token.
application/json
| Name | Type | Description |
|---|---|---|
trial_tokenrequired | string | |
upload_idrequired | string (uuid) | |
upload_urlrequired | string (uri) | |
methodrequired | "PUT" | |
headersrequired | object | |
expires_atrequired | string (date-time) |
400The request is not valid (invalid_request).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "invalid_request",
"message": "size_bytes: The file is larger than the 2147483648 byte limit."
}
} 403The human check failed (turnstile_failed).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
429trial_limit or trial_capacity. See Retry-After.
- Header
Retry-AfterintegerSeconds until the limit frees up.
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/trial/transcriptions Start the free trial transcription
Auth: trialToken
Request body application/json
| Name | Type | Description |
|---|---|---|
upload_idrequired | string (uuid) |
Responses
202Queued.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
statusrequired | "queued" | "processing" | "completed" | "failed" | "canceled" | |
created_atrequired | string (date-time) | |
completed_atrequired | string (date-time) | null | |
duration_secondsrequired | number | null | |
billed_secondsrequired | integer | null | |
cost_micro_eurrequired | integer | null | |
errorrequired | object | null | |
error.code | string | |
error.message | string | |
metadatarequired | object | |
versionrequired | integer | Latest transcript version. 0 until the transcript exists. |
filenamerequired | string | null | |
content_typerequired | string | null |
400upload_incomplete or size_mismatch.
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 409trial_used: the free trial covers one file.
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/trial/transcriptions/{id} Poll the free trial transcription
Auth: trialToken
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200The transcription, plus transcript once it is completed.
application/json
| Name | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
statusrequired | "queued" | "processing" | "completed" | "failed" | "canceled" | |
created_atrequired | string (date-time) | |
completed_atrequired | string (date-time) | null | |
duration_secondsrequired | number | null | |
billed_secondsrequired | integer | null | |
cost_micro_eurrequired | integer | null | |
errorrequired | object | null | |
error.code | string | |
error.message | string | |
metadatarequired | object | |
versionrequired | integer | Latest transcript version. 0 until the transcript exists. |
filenamerequired | string | null | |
content_typerequired | string | null | |
transcript | Transcript | |
transcript.versionrequired | 1 | |
transcript.languagerequired | "sq" | |
transcript.modelrequired | string | |
transcript.durationrequired | number | |
transcript.textrequired | string | |
transcript.segmentsrequired | Segment[] | |
transcript.segments[].idrequired | integer | |
transcript.segments[].startrequired | number | |
transcript.segments[].endrequired | number | |
transcript.segments[].speakerrequired | string | null | |
transcript.segments[].textrequired | string | |
transcript.segments[].wordsrequired | Word[] | |
transcript.captionsrequired | Cue[] | null | Edited caption cues. |
401Missing or invalid credentials (unauthorized).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "unauthorized",
"message": "Authentication is required."
}
} 404Not found (not_found).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
{
"error": {
"code": "not_found",
"message": "Transcription not found."
}
} Operations
/billing/stripe/webhook Stripe events (called by Stripe)
Auth: none
Verifies Stripe-Signature on the raw body. checkout.session.completed (paid) adds credit from amount_total; charge.refunded removes the refunded amount. Repeated events are ignored.
Request body application/json
Responses
200Received (a repeated event answers duplicate true).
400The signature is not valid (invalid_signature).
application/json
| Name | Type | Description |
|---|---|---|
errorrequired | object | |
error.coderequired | string | Stable machine-readable code. |
error.messagerequired | string |
/healthz Process is up
Auth: none
Responses
200OK.
/readyz Database and storage are reachable
Auth: none
Responses
200Ready.
503A dependency is down.
/openapi.json This document as JSON
Auth: none