Skip to main content

Seats

Endpoints for managing AI seat assignments, transfers, and bulk operations.

GET /api/ai-seats

List AI seats with flexible filtering options.

Authentication: permission.tenant.superuser

Query Parameters:

ParameterTypeDescription
planIdstringFilter by plan ID
assignedbooleanFilter by assignment status
unassignedbooleanGet users without any seat assignment
transferEligiblebooleanGet users eligible for seat transfer (no seat or free tier)
includeAllUsersbooleanInclude all users (with and without seats) for the All Users view

Response (Standard):

{
"seats": [
{
"id": "seat-123",
"plan": {
"id": "advanced-456",
"name": "Advanced AI",
"slug": "advanced",
"icon": "sparkles",
"weeklyBudget": 2.00,
"sessionBudget": 0.40,
"purchasable": true
},
"pendingPlan": null,
"username": "alice",
"displayName": "Alice Smith",
"assignedAt": "2024-01-10T09:00:00Z",
"assignedBy": "admin",
"weeklyCostUsed": 1.23,
"sessionCostUsed": 0.15,
"weeklyPeriodStart": "2024-01-10T09:00:00Z",
"sessionPeriodStart": "2024-01-15T12:00:00Z"
}
]
}

Response (Unassigned Users):

{
"users": [
{
"username": "bob",
"displayName": "Bob Jones"
}
]
}

Response (Transfer Eligible):

{
"users": [
{
"username": "charlie",
"displayName": "Charlie Brown",
"plan": {
"id": "free-789",
"name": "Free AI",
"slug": "free"
}
},
{
"username": "diana",
"displayName": "Diana Prince",
"plan": null
}
]
}

Response (All Users View):

Returns both seated and unseated users with hasSeat flag.


POST /api/ai-seats

Create unassigned seats for a plan.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
planIdstringYesPlan ID
quantityintegerYesNumber of seats to create (1-100)

Example Request:

{
"planId": "advanced-456",
"quantity": 5
}

Response:

{
"created": 5,
"planId": "advanced-456",
"planName": "Advanced AI",
"seats": {
"total": 15,
"assigned": 10,
"unassigned": 5
}
}

GET /api/ai-seats/{id}

Get detailed information about a specific seat.

Authentication: permission.tenant.superuser

Response:

{
"id": "seat-123",
"plan": {
"id": "advanced-456",
"name": "Advanced AI",
"slug": "advanced",
"weeklyBudget": 2.00,
"sessionBudget": 0.40,
"tiers": ["everyday", "advanced"]
},
"pendingPlan": null,
"username": "alice",
"user": {
"username": "alice",
"displayName": "Alice Smith",
"email": "alice@example.com"
},
"assignedAt": "2024-01-10T09:00:00Z",
"assignedBy": "admin",
"weeklyCostUsed": 1.23,
"sessionCostUsed": 0.15,
"weeklyPeriodStart": "2024-01-10T09:00:00Z",
"sessionPeriodStart": "2024-01-15T12:00:00Z",
"boost": {
"budget": 5.00,
"used": 0.75,
"remaining": 4.25,
"expiresAt": "2024-02-10T09:00:00Z"
},
"weeklyRemaining": 0.77,
"sessionRemaining": 0.25
}

PUT /api/ai-seats/{id}

Update a seat (assign/unassign user, change plan).

Authentication: permission.tenant.superuser

Request Body:

FieldTypeDescription
usernamestringUsername to assign (null to unassign)
planIdstringMove seat to different plan

Example Request (Assign):

{
"username": "alice"
}

Example Request (Unassign):

{
"username": null
}

Example Request (Change Plan):

{
"planId": "strategic-789"
}

Response:

{
"id": "seat-123",
"plan": {
"id": "strategic-789",
"name": "Strategic AI",
"slug": "strategic",
"weeklyBudget": 5.00,
"sessionBudget": 1.00
},
"pendingPlan": null,
"username": "alice",
"displayName": "Alice Smith",
"assignedAt": "2024-01-10T09:00:00Z",
"assignedBy": "admin",
"weeklyCostUsed": 0.00,
"sessionCostUsed": 0.00,
"changeType": "upgrade",
"changeMessage": "Plan upgraded from Advanced AI to Strategic AI"
}

Side Effects:

  • Assigning a user resets usage counters
  • Changing plans may create pending plan change (prorated)
  • Syncs changes to License Manager

DELETE /api/ai-seats/{id}

Delete an unassigned seat.

Authentication: permission.tenant.superuser

Response:

{
"success": true
}

Restrictions:

  • Cannot delete assigned seats (unassign first)
  • Syncs deletion to License Manager

POST /api/ai-seats/_assign

Assign a user to a plan (claims existing seat or creates new one).

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
usernamestringYesUsername to assign
planIdstringConditionalPlan ID or slug (required for non-enterprise, optional for enterprise)

Example Request:

{
"username": "alice",
"planId": "advanced-456"
}

Response:

{
"id": "seat-123",
"plan": {
"id": "advanced-456",
"name": "Advanced AI",
"slug": "advanced",
"weeklyBudget": 2.00,
"sessionBudget": 0.40
},
"username": "alice",
"assignedAt": "2024-01-15T14:00:00Z",
"assignedBy": "admin"
}

Enterprise Response:

{
"id": "seat-456",
"plan": {
"id": "unlimited-789",
"name": "Unlimited AI",
"slug": "unlimited",
"weeklyBudget": 999.99,
"sessionBudget": 999.99
},
"username": "alice",
"assignedAt": "2024-01-15T14:00:00Z",
"assignedBy": "admin",
"enterprise": true
}

Behavior:

  • Enterprise tenants: Creates enterprise seat, defaults to unlimited plan if no planId provided
  • Non-enterprise: Claims existing unassigned seat or creates new seat
  • Free to paid upgrade: Automatically upgrades free seats to paid plans
  • Reassignment validation: Enforces billing period restrictions for purchasable seats

Errors:

  • 409 Conflict - User already has a seat
  • 409 Conflict - Seat reassignment not allowed yet (billing period restriction)
  • 400 Bad Request - User not found or plan not found
License Manager Integration

For purchasable seats, this endpoint requests reassignment permission from License Manager to enforce billing period rules.


POST /api/ai-seats/_unassign

Unassign a user from their seat.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
usernamestringYesUsername to unassign

Example Request:

{
"username": "alice"
}

Response:

{
"success": true,
"username": "alice",
"seatId": "seat-123",
"previousPlan": {
"id": "advanced-456",
"name": "Advanced AI",
"slug": "advanced"
},
"unassignedAt": "2024-01-15T14:30:00Z"
}

Side Effects:

  • Marks seat as unassigned
  • Preserves seat for potential reassignment
  • Syncs to License Manager

GET /api/ai-seats/user/{username}

Get the current user's seat and budget status.

Authentication: Required (session)

Pre-blocks: ai.verify

Response:

{
"username": "alice",
"seat": {
"id": "seat-123",
"plan": {
"id": "advanced-456",
"name": "Advanced AI",
"slug": "advanced",
"tier": "advanced",
"weeklyBudget": 2.00,
"sessionBudget": 0.40
},
"assignedAt": "2024-01-10T09:00:00Z"
},
"budget": {
"weeklyUsed": 1.23,
"weeklyRemaining": 0.77,
"sessionUsed": 0.15,
"sessionRemaining": 0.25,
"boostRemaining": 4.25,
"totalRemaining": 5.02
},
"periods": {
"weeklyStart": "2024-01-10T09:00:00Z",
"sessionStart": "2024-01-15T12:00:00Z"
}
}

POST /api/ai-seats/_transfer

Transfer a seat from one user to another.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
fromUsernamestringYesCurrent seat holder
toUsernamestringYesNew seat recipient

Example Request:

{
"fromUsername": "alice",
"toUsername": "bob"
}

Response:

{
"success": true,
"fromUsername": "alice",
"toUsername": "bob",
"seatId": "seat-123",
"plan": {
"id": "advanced-456",
"name": "Advanced AI",
"slug": "advanced"
},
"transferredAt": "2024-01-15T15:00:00Z"
}

Validation:

  • Source user must have a seat
  • Source seat must not be a free plan seat (free seats are not transferable)
  • Target user must not have a paid seat (free seats are silently replaced)
  • Enforces reassignment rules for purchasable seats
  • Requests License Manager permission if needed

POST /api/ai-seats/_bulk-assign

Assign multiple users to a plan in a single operation.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
usernamesarrayYesArray of usernames to assign
planIdstringYesPlan ID for all assignments

Example Request:

{
"usernames": ["alice", "bob", "charlie"],
"planId": "advanced-456"
}

Response:

{
"success": true,
"assigned": 3,
"failed": 0,
"results": [
{
"username": "alice",
"success": true,
"seatId": "seat-123"
},
{
"username": "bob",
"success": true,
"seatId": "seat-124"
},
{
"username": "charlie",
"success": true,
"seatId": "seat-125"
}
]
}

Partial Success:

If some assignments fail, the response includes error details:

{
"success": false,
"assigned": 2,
"failed": 1,
"results": [
{
"username": "alice",
"success": true,
"seatId": "seat-123"
},
{
"username": "bob",
"success": false,
"error": "User not found"
},
{
"username": "charlie",
"success": true,
"seatId": "seat-125"
}
]
}

POST /api/ai-seats/_bulk-unassign

Unassign multiple users in a single operation.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
usernamesarrayYesArray of usernames to unassign

Example Request:

{
"usernames": ["alice", "bob", "charlie"]
}

Response:

{
"success": true,
"unassigned": 3,
"failed": 0,
"results": [
{
"username": "alice",
"success": true,
"seatId": "seat-123"
},
{
"username": "bob",
"success": true,
"seatId": "seat-124"
},
{
"username": "charlie",
"success": true,
"seatId": "seat-125"
}
]
}

POST /api/ai-seats/{id}/_boost

Grant boost budget to a seat.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
amountnumberYesBoost amount in USD (minimum $0.01)

Example Request:

{
"amount": 5.00
}

Response:

{
"id": "seat-123",
"username": "alice",
"boost": {
"budget": 5.00,
"used": 0.00,
"remaining": 5.00,
"expiresAt": "2024-02-15T14:00:00Z"
}
}

Boost Behavior:

  • One-time budget added to seat
  • Used before weekly/session budgets
  • Expires after 30 days
  • Cannot be transferred between users
  • Admins only

POST /api/ai-seats/{id}/_change-plan

Change an assigned seat's plan with proration.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
planIdstringYesNew plan ID
strategystringNoswap (default) or direct. swap moves user to a seat from the target plan pool. direct changes the plan on the seat itself via License Manager (handles proration, deferred downgrades).

Example Request:

{
"planId": "strategic-789",
"strategy": "swap"
}

Response:

{
"success": true,
"changeType": "upgrade",
"message": "Plan upgraded from Advanced AI to Strategic AI",
"seat": {
"id": "seat-123",
"plan": {
"id": "strategic-789",
"name": "Strategic AI",
"slug": "strategic",
"weeklyBudget": 5.00,
"sessionBudget": 1.00
},
"pendingPlan": null,
"username": "alice"
},
"proration": {
"creditApplied": 1.50,
"chargeAmount": 3.00,
"netCharge": 1.50,
"effectiveAt": "2024-01-15T14:00:00Z"
}
}

Change Types:

  • Immediate upgrade: Applied immediately, proration calculated
  • Scheduled downgrade: Takes effect at next billing cycle
  • Lateral move: Different plan, same tier

Pending Plan:

Downgrades create a pending plan change:

{
"pendingPlan": {
"id": "everyday-123",
"name": "Everyday AI",
"slug": "everyday",
"effectiveAt": "2024-02-01T00:00:00Z"
}
}

DELETE /api/ai-seats/{id}/_cancel-pending

Cancel a pending plan change.

Authentication: permission.tenant.superuser

Response:

{
"success": true,
"message": "Pending plan change canceled",
"seat": {
"id": "seat-123",
"plan": {
"id": "advanced-456",
"name": "Advanced AI"
},
"pendingPlan": null
}
}

POST /api/ai-seat-purchase

Purchase additional seats with proration and tax estimates.

Authentication: permission.tenant.superuser

Request Body:

FieldTypeRequiredDescription
planIdstringYesPlan ID to purchase seats for
quantityintegerYesNumber of seats to purchase

Example Request:

{
"planId": "advanced-456",
"quantity": 5
}

Response:

{
"success": true,
"planId": "advanced-456",
"planName": "Advanced AI",
"quantity": 5,
"pricePerSeat": 25.00,
"subtotal": 125.00,
"prorationCredit": 12.50,
"taxEstimate": 10.00,
"total": 122.50,
"billingDate": "2024-02-01T00:00:00Z"
}

Purchase Flow:

  1. Calculate proration based on current billing cycle
  2. Estimate taxes based on tenant location
  3. Create seats in Informer
  4. Sync purchase to License Manager
  5. Charge via configured payment method

GET /api/ai-seats/consumption

Get seat consumption summary by plan.

Authentication: permission.tenant.superuser

Response:

{
"plans": [
{
"planId": "advanced-456",
"planName": "Advanced AI",
"planSlug": "advanced",
"totalSeats": 10,
"assignedSeats": 8,
"activeUsers": 7,
"weeklyUsage": 8.45,
"monthlyUsage": 32.18,
"averagePerSeat": 4.03
}
],
"totals": {
"totalSeats": 35,
"assignedSeats": 28,
"activeUsers": 24,
"weeklyUsage": 24.67,
"monthlyUsage": 98.54
}
}

Metrics:

  • totalSeats: All seats for the plan
  • assignedSeats: Seats with users
  • activeUsers: Users who made requests this week
  • weeklyUsage: Cost this week (USD)
  • monthlyUsage: Cost this month (USD)
  • averagePerSeat: Monthly cost / assigned seats
Bulk Operations

Use bulk assign/unassign endpoints when onboarding or offboarding multiple users to reduce API calls and improve performance.