Seat assignments

How to record which child customer occupies which seat under a parent, so seat usage is an explicit record rather than an inference from the customer hierarchy.


Why this matters

seats_values on a pricing plan schedule records how many seats a parent has paid for. Nothing in that number says which child customer occupies one.

Without that record, a processing-only child is linked to its parent only through parent_resource_ids, which leaves three questions unanswerable: you cannot exclude a child from counting as a seat, you cannot tell one seat type from another when a parent buys several, and you cannot compare occupied seats against paid seats. Any answer has to be reconstructed outside Solvimon and re-reconstructed whenever the hierarchy changes.

A seat assignment makes occupancy a resource in its own right: who occupies which seat type, under which parent, for which period.

How it works

An assignment links three things and a period:

Occupancy is keyed on the product item, not on the subscription or the schedule. Capacity is already scoped that way: seats_values records a seat count per pricing item, so a parent that buys two kinds of seat has two counts. Keying occupancy the same way is what lets one child hold an editor seat while another holds a viewer seat under the same parent, and it means the seat type is read from your catalogue rather than from how the seat happens to be priced.

An assignment changes no billing behaviour on its own. It is the record that seat-scaled credit grants and occupied-versus-paid reporting are built on.

Status is derived, not set

status is read-only and computed from the dates every time the assignment is read:

StatusWhen
SCHEDULEDNow is before start_at
ACTIVENow is at or after start_at, and end_at is unset or in the future
ENDEDNow is at or after end_at

There is no status to patch and no activation call. An assignment that starts next month is created today and becomes ACTIVE on its own.

Assign a seat

start_at is required; leaving end_at unset creates an open assignment, which is the normal case for a seat someone still holds.

Assign a child customer to a seat
$curl -X POST https://test.api.solvimon.com/v1/seat-assignments \
> -H "X-API-KEY: <apiKey>" \
> -H "Content-Type: application/json" \
> -d '{
> "parent_customer_id": "cust_qwDRHT0vsJoJuFCkPm1A",
> "customer_id": "cust_MwDR9l0vsgwcV6CRqw14",
> "product_item_id": "proi_hwDeeN0vcYJaBLAc0C1W",
> "start_at": "2026-09-01T00:00:00Z"
> }'
Response
1{
2 "object_type": "SEAT_ASSIGNMENT",
3 "id": "sass_TwDeeN0vcYJaBLAc0D2k",
4 "status": "ACTIVE",
5 "parent_customer_id": "cust_qwDRHT0vsJoJuFCkPm1A",
6 "customer_id": "cust_MwDR9l0vsgwcV6CRqw14",
7 "product_item_id": "proi_hwDeeN0vcYJaBLAc0C1W",
8 "start_at": "2026-09-01T00:00:00Z",
9 "end_at": null,
10 "created_at": "2026-09-01T09:14:22Z",
11 "updated_at": null
12}

Three things are checked before the assignment is written:

FieldRequirement
parent_customer_idAn ACTIVE customer on your platform
customer_idAn ACTIVE customer, a child of that parent, and carrying the PROCESSING_ONLY role
product_item_idA product item with model type PER_SEAT, in status ACTIVE or DEPRECATED

The PROCESSING_ONLY requirement is what makes exclusion possible: a child that bills in its own right is not a seat, and pointing an assignment at one is rejected on customer_id. A DRAFT product item is rejected because its model type can still change, so PER_SEAT is only settled once the item is published.

End a seat

Ending an assignment is a PATCH that sets end_at. It must be strictly after start_at: a seat occupied for zero time is not an occupancy.

End an assignment
$curl -X PATCH https://test.api.solvimon.com/v1/seat-assignments/sass_TwDeeN0vcYJaBLAc0D2k \
> -H "X-API-KEY: <apiKey>" \
> -H "Content-Type: application/json" \
> -d '{"end_at": "2026-12-31T23:59:59Z"}'

end_at is the only patchable field. Sending customer_id, parent_customer_id, product_item_id or start_at is rejected with field can not be updated, because moving a seat to another person, parent, seat type or start date is a different occupancy and belongs in a new assignment. Setting end_at back to null re-opens the seat, which is how you undo an end date entered by mistake.

DELETE /v1/seat-assignments/{id} is reserved for undoing a mis-created assignment, and only works while the assignment is still SCHEDULED. Once the seat has actually been occupied, the occupancy is history that later invoices were produced against, so deleting it is rejected with seat assignment ... is ACTIVE and can no longer be deleted; set end_at to free the seat instead.

List who holds a seat

The collection endpoint answers the occupancy questions directly, and combines filters:

Query parameterAnswers
parent_customer_idWho occupies seats under this parent
customer_idWhich seats this child holds
product_item_idWho holds this seat type
statusRestrict to SCHEDULED, ACTIVE or ENDED
Active seats under a parent
$curl "https://test.api.solvimon.com/v1/seat-assignments?parent_customer_id=cust_qwDRHT0vsJoJuFCkPm1A&status=ACTIVE" \
> -H "X-API-KEY: <apiKey>"

The response is paginated with the usual limit, page, order_by and order_direction parameters.

Permissions

Four permissions govern the resource, and they are separate from the customer permissions so seat administration can be delegated without granting the ability to change customers:

PermissionAllows
SEAT_ASSIGNMENT.VIEWRead assignments, individually and as a list
SEAT_ASSIGNMENT.CREATEAssign a seat
SEAT_ASSIGNMENT.UPDATESet or clear end_at
SEAT_ASSIGNMENT.DELETERemove an assignment

The Seat Assignment Viewer role carries the view permission and is part of Read Only, so a read-only user sees occupancy without being able to change it. See roles and permissions.

Edge cases

  • A customer cannot hold the same seat twice at once. Overlapping assignments for the same parent, child and product item are rejected naming the assignment that already covers the period. Sequential assignments for the same three are fine, which is what a child who leaves and returns produces.
  • Over-assignment is allowed. Nothing compares the number of assignments against seats_values, so a parent that paid for five seats can have six children assigned. This is deliberate: the record of who occupies a seat is kept accurate first, and reconciling it against what was paid for is a reporting question, not a reason to reject the write.
  • A child under several parents needs an explicit parent. parent_customer_id is required rather than inferred, because a processing-only child can sit under more than one parent and only you know which one the seat is held against.
  • Ending an assignment does not change an invoice. Seat counts are billed from seats_values on the schedule. Ending an assignment records that the seat was vacated; releasing what the parent pays for is a separate change to the subscription.
  • A deprecated seat type still accepts assignments. DEPRECATED product items are allowed so that an existing seat type being phased out keeps working for the customers still on it.