# License & Activation

:::note[In one sentence]

A license key unlocks the SDK. It is verified on the first call to `Octet.start(...)` and the SDK works from a local cache afterwards.

:::

## Getting a license

Free license keys are issued via [sdk.octetproof.com/signup](https://sdk.octetproof.com/signup). The free tier covers up to 1,000 signed proofs per month, per key. Paste the key into `OctetConfig.licenseKey` and call `Octet.start(...)`. Nothing else is required.

Use of the SDK is governed by the [Mobile SDK terms](https://octetproof.com/terms/mobile/). The current terms live at `/terms/mobile/`; a dated snapshot lives at `/terms/mobile/vMM.DD.YYYY/`.

The first call activates the license over the network. Subsequent launches read from a local cache. The SDK does not contact the activation backend again until the license needs to renew.

## Validity

License keys renew automatically. The free tier covers up to **1,000 signed proofs per month, per key**, and resets each month; beyond that, proofs are billed per signed proof. You should not need to replace a working key.

Install on as many devices as you like. There is no per-license device cap.

## License states

Octet maintains active licenses, so a key in normal use stays in `ACTIVE`. The `GRACE_PERIOD` and `EXPIRED` states below handle prolonged offline periods and revocation -- a maintained key is not turned off for getting old.

```mermaid
stateDiagram-v2
    [*] --> NOT_ACTIVATED: license verified locally
    NOT_ACTIVATED --> ACTIVE: activation succeeds
    ACTIVE --> RENEWAL_RECOMMENDED: < 30 days to expiry
    ACTIVE --> GRACE_PERIOD: cached past exp, offline
    RENEWAL_RECOMMENDED --> GRACE_PERIOD: same trigger
    GRACE_PERIOD --> EXPIRED: past grace period
    NOT_ACTIVATED --> INVALID: signature / server reject
    ACTIVE --> INVALID: server reject
    EXPIRED --> [*]
    INVALID --> [*]
```

Read the state at any time:

```swift
// iOS
if let status = sdk.licenseStatus {
    print("state: \(status.state), days left: \(status.daysUntilHardStop ?? -1)")
}
```

```kotlin
// Android
sdk.licenseStatus?.let { status ->
    println("state: ${status.state}, days left: ${status.daysUntilHardStop ?: -1}")
}
```

`LicenseStatus.state` drives in-app UI only. It never drives the cryptographic gate. Forcing `state = ACTIVE` in your own code accomplishes nothing.

## Failure modes

`Octet.start(...)` throws a typed `LicenseError`. The complete taxonomy is in [License Types](/docs/api-reference/license-types/). The common cases:

- `MalformedKey`. The key isn't a valid signed token. Fix: re-copy the key.
- `NoActivation`. First launch, and the device is offline. Fix: retry when network returns.
- `Expired`. Cached activation past the offline grace, backend unreachable. Fix: reconnect so the SDK can re-activate.
- `ActivationWindowClosed`. Not returned under the current maintained-license model; retained for compatibility. Fix: contact support.
- `Revoked`. Admin revoke (key leaked, abuse). Fix: contact support.
- `Network(message:)`. Transient. Fix: retry.
- `ServerRejected(httpStatus:, reason:)`. Anything else from the backend. Fix: see `reason`.

## Usage telemetry

The SDK reports **aggregate, privacy-preserving usage counters** to the license
backend, indexed by your license: how many proofs were generated, uploaded, or
could not be produced, by coarse level and region type. The counters carry **no
location data**: no coordinates, no region IDs, no proof contents. They are
buffered in an encrypted file in the app's private storage and uploaded at most
once a day.

It is on by default. Disable it with `telemetryEnabled = false` on
[`OctetConfig`](/docs/api-reference/octet-start/). Disabling deletes any buffered file.

## Where to go next

- [Prerequisites](/docs/getting-started/prerequisites/) for how to request a license key.
- [License Types API Reference](/docs/api-reference/license-types/) for the exact types of `LicenseStatus` and `LicenseError`.
- [Troubleshooting FAQ](/docs/troubleshooting/faq/) for what to do when a key fails.
