---
title: "Floating licenses and shared seats"
description: "Let many registered installations share a smaller number of running-app seats."
slug: "/docs/v1/licensing/floating"
section: "Licensing models"
order: 902
audience: "all"
level: "intermediate"
last_updated: "2026-10-25"
version: "v1"
related: ["/docs/v1/licensing/platform-plans", "/docs/v1/sdks/compatibility"]
draft: false
---
Let many registered installations share a smaller number of running-app seats.

## Plan requirement and limits
**Pro:** up to **10 concurrent sessions per software license**. **Business:** no commercial platform cap on floating seats; the developer still sets a positive per-license `max_concurrent_sessions` and a separate device limit. The technical whole-number limit is 2,147,483,647. Higher numbers do not reserve server capacity or promise that many simultaneous connections.

Lower plans receive `PLAN_UPGRADE_REQUIRED` (403). Setting more than 10 seats on Pro returns `PLAN_LIMIT_REACHED` (403). An existing larger configuration is preserved on a downgrade to Pro, with an effective limit of 10; existing records are not deleted. Checkin/deactivation remains available after losing eligibility so seats can be released.

## Configure a software plan
Under Plans & features, set Apps running at once. Blank means an ordinary license. Heartbeat interval defaults to **180 seconds** (30–600 allowed); timeout defaults to **600 seconds**, at least twice the interval and no more than 24 hours. A timeout is an outer limit for an unreachable app, not guaranteed availability or offline grace.

## API lifecycle
1. Activate the installation with the required fingerprint. Activation alone does not grant a seat: the signed `floating.session` is null.
2. `POST /api/v1/licenses/checkout` with the activation bearer token, `X-Installation-ID` and JSON `fingerprint`. A successful response includes a private `session_token` and a signed lease in `license`.
3. `POST /api/v1/licenses/heartbeat` with the same headers and JSON `fingerprint` plus `session_token` before its signed expiry.
4. `POST /api/v1/licenses/checkin` with those fields when finished. A successful `checked_in: true` releases the session; it does not grant access.

```json
{
  "fingerprint": "<the current fingerprint object, not this string>",
  "session_token": "<private session token returned by checkout>"
}
```

Store the token privately. Verify the Ed25519 signature, installation/fingerprint binding and the signed session’s token hash and deadlines. Validation for an active floating license also sends `session_token`. The same installation reusing a live checkout does not consume a second seat.

## Full pools and lost connectivity
The owning license is locked while seats are counted and allocated; parallel requests for the final free seat cannot both obtain it. A full pool returns `ALL_SEATS_IN_USE` (409). Explain that another running app must close, or let the customer end a stuck session in the portal.

Expired sessions stop counting when the next checkout cleans them; scheduled cleanup also removes expired rows. A client must stop licensed work when its signed lease expires, even if the longer regular offline grace has not ended. Checkout and renewal require the server. No on-premise license server is provided.

## SDK behavior
The matching source SDKs reserve a seat automatically after activation/check as needed. Python and Node.js expose `checkout()`, `heartbeat()`, `checkin()` and `close()`. Rust exposes the same seat methods; C# uses `CheckoutAsync`, `HeartbeatAsync`, `CheckinAsync` and `CloseAsync`.

**PHP has no background heartbeat thread.** Call `tick()` frequently in long jobs and inspect a returned failure before continuing work. The other SDK workers also cannot renew while a process is suspended; always enforce the signed lease deadline at the action boundary. Close/checkin during graceful shutdown where possible.

Use the [SDK compatibility guide](/docs/v1/sdks/compatibility) to distinguish current source behavior from earlier packaged releases.
