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:
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.
Three things are checked before the assignment is written:
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_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:
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:
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_idis 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_valueson 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.
DEPRECATEDproduct items are allowed so that an existing seat type being phased out keeps working for the customers still on it.