Two key types. Public keys plug straight into the SDK from the app. Secret keys live on your server and mint short-lived access tokens that the SDK uses — recommended for production.
Manage your keysChoosing a mode
Public key mode is the fastest path: drop a pk_v1_ key into apiConfig and you're done. Secret key + access token mode is what we recommend for production — your server keeps the secret key, mints short-lived access tokens, and the SDK handles refresh transparently.
Public key mode
Use a public key when calling the API straight from the app. Construct .apiKey with the key; everything else is optional.
AIAutocomplete(configuration: .init(apiConfig: .apiKey(.init(// Public key (pk_v1_...) — safe to ship in the app binaryapiKey: "pk_v1_your_public_key",appIdentifier: "com.example.myapp",extraHeaders: ["X-Tenant": "acme"])),onSubmit: { result in print(result.query) }))
| Prop | Type | Default | Description |
|---|---|---|---|
apiKeyRequired | String | — | Your public key (pk_v1_...). Sent as Authorization: Bearer. |
endpoint | URL? | "https://api.ai-autocomplete.com/api/suggest" | Suggest endpoint override; nil uses the default. |
authScheme | .bearer | .basic | .bearer | Send the key as a Bearer token (default) or Basic credentials. |
appIdentifier | String? | — | Echoed in X-App-Identifier for tenant routing and telemetry segmentation. |
extraHeaders | [String: String] | [:] | Merged into every request. Authorization is always overwritten by the SDK. |
Secret key + access token mode
Your backend exchanges the secret key for a short-lived access token (at_v1_...); the app fetches that token from your backend and hands it to the SDK. The secret key never leaves your server.
On your server
On your backend, expose a route that exchanges your secret key for a short-lived access token by calling the endpoint below. Return the response to the app; the SDK reads it through getAccessToken. The secret key must never reach the app — keep it in a server-side environment variable, never embedded in the app binary.
https://api.ai-autocomplete.com/api/auth/tokenRequest body
{"product_id": "<uuid>"}
200 OK
{"access_token": "at_v1_AbCdEfGh...","expires_at": 1775464738,"token_type": "Bearer"}
expires_at is unix seconds. The Swift SDK's AccessToken.expiresAt expects unix milliseconds — multiply by 1000 (see below).
401 Unauthorized
{"error": {"code": "INVALID_SECRET_KEY","message": "The provided secret key is invalid"}}
In the app
getAccessToken calls a route on your own backend, decodes the token + expiry, and returns an AccessToken. The SDK caches it and refreshes 30 seconds before expiry. No secret-key code lives in the app.
// IN-APP ONLY. No secret key here — only the URL of your own// backend route, which holds the secret key.struct TokenResponse: Decodable {let accessToken: Stringlet expiresAt: Double // unix seconds from your backend}AIAutocomplete(configuration: .init(apiConfig: .accessToken(.init(getAccessToken: {let url = URL(string: "https://your-backend.example.com/api/ac-token")!let (data, _) = try await URLSession.shared.data(from: url)let decoder = JSONDecoder()decoder.keyDecodingStrategy = .convertFromSnakeCaselet payload = try decoder.decode(TokenResponse.self, from: data)// The Swift SDK expects expiresAt in unix MILLISECONDS.return AccessToken(accessToken: payload.accessToken,expiresAt: payload.expiresAt * 1000)})),onSubmit: { result in print(result.query) }))
| Prop | Type | Default | Description |
|---|---|---|---|
getAccessTokenRequired | () async throws -> AccessToken | — | Async token producer. Concurrent callers are coalesced onto one in-flight fetch. |
accessToken | String? | — | Pre-seeded token — skips the cold-start fetch when you already hold a fresh one. |
endpoint | URL? | "https://api.ai-autocomplete.com/api/suggest" | Suggest endpoint override; nil uses the default. |
appIdentifier | String? | — | Echoed in X-App-Identifier. |
extraHeaders | [String: String] | [:] | Merged into every request. Authorization is always overwritten by the SDK. |