> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ai-coustics.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authenticate apps

> Keep SDK keys on trusted servers and manage short-lived credentials for supported client integrations.

Use an SDK key in a trusted server environment. For an application distributed to users, keep the SDK key on your backend and issue short-lived JSON Web Tokens (JWTs) to authenticated clients.

This page describes core SDK 0.25.0 and Python `aic-sdk` 3.3.0. Check your binding in the [compatibility matrix](/reference/sdk/compatibility-matrix). The released browser package `@ai-coustics/aic-sdk-wasm` 0.25.0 uses core SDK 0.25.0 and exposes JWT refresh through `processor.getContext().updateBearerToken(token)`. Follow the [WebAssembly guide](/reference/sdk/language-bindings/wasm) for its browser setup; the Python methods below belong to the native binding.

## Server-side authentication

### Get an SDK license

Create an SDK key on the [developer platform](https://developers.ai-coustics.com/). Store it in your deployment's secret manager and inject it as `AIC_SDK_LICENSE` at runtime. Pass the complete value to the processor as shown in the [SDK quickstart](/models/get-started/sdk-quickstart).

Do not commit the key, print it in logs or put it in a browser bundle, URL or client-accessible configuration. If a key is exposed, replace it through the developer platform and redeploy affected services.

Self-service keys use online authorization and usage reporting. Local inference does not imply disconnected operation. See [network and telemetry behavior](/reference/concepts/sdk-telemetry) before restricting network access.

## Client-side authentication

Your backend authenticates users and issues short-lived tokens while keeping the SDK key private. Protect the token endpoint with authentication, rate limits and HTTPS.

<Info>
  JWT credentials and in-place token updates were introduced in core SDK 0.19.0, available in Python from 2.3.0. Refresh requires both the initial credential and its replacement to be JWTs.
</Info>

### Mint a token on your backend

The platform's signed SDK key has a base64-encoded JSON payload containing an `api_key` field. The token endpoint uses that inner key as the HTTP Basic username, with an empty password. This decoding step belongs only on your backend. It does not verify the SDK key's signature.

Save this as `token_service.py` in your backend project. It uses the Python standard library and expects `AIC_SDK_LICENSE` in the environment.

```python theme={null}
import base64
import json
import os
import time
import urllib.request

TOKEN_ENDPOINT = "https://api.ai-coustics.io/v1/sdk/tokens"


def token_expiry(token: str) -> int:
    # Decode exp for scheduling only. The SDK/backend authenticates the token.
    payload = token.split(".")[1]
    payload += "=" * (-len(payload) % 4)
    return int(json.loads(base64.urlsafe_b64decode(payload))["exp"])


def mint_token() -> dict:
    raw = os.environ["AIC_SDK_LICENSE"]
    payload = raw.split(".")[0]
    payload += "=" * (-len(payload) % 4)
    api_key = json.loads(base64.b64decode(payload))["api_key"]
    basic = base64.b64encode(f"{api_key}:".encode()).decode()
    request = urllib.request.Request(
        TOKEN_ENDPOINT,
        data=b"",
        method="POST",
        headers={"Authorization": f"Basic {basic}"},
    )
    with urllib.request.urlopen(request, timeout=10) as response:
        token = json.load(response)["token"]
    expires_at = token_expiry(token)
    if expires_at <= time.time():
        raise RuntimeError("Token endpoint returned an expired token")
    return {"token": token, "expires_at": expires_at}


if __name__ == "__main__":
    result = mint_token()
    print(f"Token obtained; expires at Unix time {result['expires_at']}")
```

Run `python token_service.py`. A successful request prints an expiry timestamp without logging the token. Your authenticated application endpoint can return the function's result to the authorized client. Set `Cache-Control: no-store` on that response and exclude response bodies from request logs.

A failed request raises an HTTP or connection error. Check the key and backend connectivity before retrying. Do not retry authentication failures indefinitely or forward raw upstream error bodies to clients.

### Initialize the SDK with the token

The following native Python example demonstrates the credential argument. Install `aic-sdk==3.3.0` in an isolated environment and supply a fresh token as `AIC_SESSION_TOKEN`. Your application normally obtains that token through its authenticated backend API.

```python theme={null}
import os
import aic_sdk as aic

model_path = aic.Model.download("quail-vf-2.2-l-16khz", "./models")
model = aic.Model.from_file(model_path)
config = aic.ProcessorConfig.optimal(model)
processor = aic.Processor(model, os.environ["AIC_SESSION_TOKEN"], config)
processor_context = processor.get_context()
```

Verify activation by processing audio with the [quickstart](/models/get-started/sdk-quickstart) and checking session errors. Keep model downloads and network requests outside the audio callback.

### Refresh before the token expires

Use the returned expiry or the JWT's `exp` claim to schedule refresh. Do not assume a fixed lifetime. Request a replacement early enough to allow for clock skew and transient network failures, for example 5 minutes before expiry for a token whose lifetime exceeds that margin.

After your authenticated backend returns a fresh token, update the existing processor context:

```python theme={null}
# fresh_token is the token returned by your authenticated backend API.
processor_context.update_bearer_token(fresh_token)
```

The update preserves processor state and does not reload the model. Run retrieval and refresh from your control thread, with one refresh in flight per session. Use bounded retries with backoff and jitter for transient failures, stop retries at shutdown and surface a failed refresh to your application.

`TokenUnsupportedError` means the original credential or its replacement is not a JWT. The previous credential remains in use. A processor created with an SDK key must be recreated to switch to JWT authentication.

### Handle expired or rejected credentials

Do not depend on one exception to identify every authentication failure:

| Symptom | Action |
| :- | :- |
| `LicenseExpiredError` while accepting a credential | Obtain a valid replacement and check the system clock. |
| `TokenUnsupportedError` during refresh | Check that both credentials are JWTs. Recreate a key-initialized processor if changing credential type. |
| `ProcessingNotAllowedError` during processing | Check authorization, activation connectivity and session lifecycle. Refresh a JWT if appropriate; a terminated session needs a new processor. |
| Token retrieval timeout or server error | Retry outside the audio thread within a bounded deadline; record the failure without the credential. |

JWT activation, authorization or required usage-reporting failures can stop processing. Tolerated network failures do not establish offline access.

<Warning>
  In Python, `Processor.process()` raises on disallowed processing and returns no output array. Choose a failure policy: stop the session, report degraded operation or use an approved fallback. Do not label bypassed audio as enhanced.
</Warning>

See [troubleshooting](/production/troubleshooting) for recovery steps and [deployment](/production/deployment) for startup, readiness and shutdown checks.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.