Authentication

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 keys

Choosing 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.

PublicKeyAuth.swift
AIAutocomplete(configuration: .init(
apiConfig: .apiKey(.init(
// Public key (pk_v1_...) — safe to ship in the app binary
apiKey: "pk_v1_your_public_key",
appIdentifier: "com.example.myapp",
extraHeaders: ["X-Tenant": "acme"]
)),
onSubmit: { result in print(result.query) }
))
PropTypeDefaultDescription
apiKeyRequiredStringYour public key (pk_v1_...). Sent as Authorization: Bearer.
endpointURL?"https://api.ai-autocomplete.com/api/suggest"Suggest endpoint override; nil uses the default.
authScheme.bearer | .basic.bearerSend the key as a Bearer token (default) or Basic credentials.
appIdentifierString?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.

POSThttps://api.ai-autocomplete.com/api/auth/token
Headers
Authorization: Bearer <sk_v1_…> — your secret key.
Body
product_idthe UUID of the product the token is for. Visible in the URL of the product editor at /edit.

Request 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.

AccessTokenAuth.swift
// 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: String
let 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 = .convertFromSnakeCase
let 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) }
))
PropTypeDefaultDescription
getAccessTokenRequired() async throws -> AccessTokenAsync token producer. Concurrent callers are coalesced onto one in-flight fetch.
accessTokenString?Pre-seeded token — skips the cold-start fetch when you already hold a fresh one.
endpointURL?"https://api.ai-autocomplete.com/api/suggest"Suggest endpoint override; nil uses the default.
appIdentifierString?Echoed in X-App-Identifier.
extraHeaders[String: String][:]Merged into every request. Authorization is always overwritten by the SDK.