The Bodhi APIs use API keys for authentication. Every request must include your key, which authenticates the call and meters usage against your account’s credit balance.
Your key is a secret. Do not share it, and do not expose it in client-side code such as browsers or mobile apps.
Obtaining your API subscription key
Keys are issued from the platform at platform.navana.ai. Signup is self-serve.
Register
Go to platform.navana.ai and register with your organisation email address. You can also sign in with Google instead.
Verify your email
A one-time code is sent to that address. Enter it to verify the account.
Complete your profile
Fill in your name, team size, and role. This is the last step before the dashboard.
Open API Keys
Select API Keys in the sidebar.
Create the key
Click Create API Key, give it a short name so you can recognise it later, and confirm.
Copy it immediately
The key is displayed once, at creation, and cannot be retrieved afterwards. Copy it and store it somewhere safe before closing the dialog.
Using the API subscription key
Send the key in an X-Api-Key header. Here it is against the non-streaming
transcription endpoint:
curl -X POST https://stt.navana.ai/api/transcribe \
-H "X-Api-Key: $BODHI_API_KEY" \
-F "model=hi-banking-v2-8khz" \
-F "transaction_id=$(uuidgen)" \
-F "audio_file=@recording.wav"import os, uuid, requests
response = requests.post(
"https://stt.navana.ai/api/transcribe",
headers={"X-Api-Key": os.environ["BODHI_API_KEY"]},
data={
"model": "hi-banking-v2-8khz",
"transaction_id": str(uuid.uuid4()),
},
files={"audio_file": open("recording.wav", "rb")},
)import { openAsBlob } from 'node:fs'
const form = new FormData()
form.set('model', 'hi-banking-v2-8khz')
form.set('transaction_id', crypto.randomUUID())
form.set('audio_file', await openAsBlob('recording.wav'), 'recording.wav')
const res = await fetch('https://stt.navana.ai/api/transcribe', {
method: 'POST',
headers: { 'X-Api-Key': process.env.BODHI_API_KEY! },
body: form,
})file, _ := os.Open("recording.wav")
defer file.Close()
var body bytes.Buffer
form := multipart.NewWriter(&body)
form.WriteField("model", "hi-banking-v2-8khz")
form.WriteField("transaction_id", uuid.NewString())
part, _ := form.CreateFormFile("audio_file", "recording.wav")
io.Copy(part, file)
form.Close()
req, _ := http.NewRequest(http.MethodPost,
"https://stt.navana.ai/api/transcribe", &body)
req.Header.Set("X-Api-Key", os.Getenv("BODHI_API_KEY"))
req.Header.Set("Content-Type", form.FormDataContentType())
resp, err := http.DefaultClient.Do(req)The same header goes on the WebSocket upgrade for streaming. See Authentication for every endpoint, and Transcribe a file for the full parameter list.
Best practices for API key management
Keep it server-side. Anything shipped to a browser or mobile app can be read by whoever opens devtools, so the key should stay on your server. If a browser needs transcription, have it talk to your backend, and let the backend hold the key and relay audio to Bodhi.
Read it from the environment or a secret manager, never from source control.
# .env (git-ignored)
BODHI_API_KEY=bd_xxxxxxxxxxxx.xxxxxxxx…import os
api_key = os.environ["BODHI_API_KEY"] # raises early if unsetconst apiKey = process.env.BODHI_API_KEY
if (!apiKey) throw new Error('BODHI_API_KEY is not set')apiKey := os.Getenv("BODHI_API_KEY")
if apiKey == "" {
log.Fatal("BODHI_API_KEY is not set")
}Have every service read one shared secret. Because creating a key revokes the previous one, rotation is a cutover rather than a handover. If each service holds its own copy, a rotation means updating them all before the old key dies.
Rotate as a planned cutover. Create the new key, push the new value, and restart or reload your callers. In-flight requests already authorized will finish, but new ones on the old key fail immediately.
Alert on 401. After a rotation it means a caller missed the update. At
any other time it means something is wrong.
Revoke deliberately. Revoke from the API Keys page in the platform. Revocation takes effect on the next request, since key status is checked on every call, so there is no cache to wait out. Open streams authorized before revocation keep running until they end on their own or run out of credits. Past keys stay listed under Archived keys, with who created each one and when.
Status codes for authentication
| Status | Meaning | What to do |
|---|---|---|
401 |
The key is missing, malformed, unrecognised, or revoked. | Check the header name and that you sent the whole prefix.secret value. If you recently created a key, an old one is still in use somewhere. |
402 |
The account’s credit balance is exhausted. | Top up. Retrying without topping up fails identically. |
403 |
The account is suspended or closed, or the key does not carry the scope the endpoint needs. | An account problem needs support. A scope problem needs a different key, though every key created today is granted all scopes. |