Skip to main content
Version: 3.1

Token

A Token is a numbered ticket a patient holds in a walk-in queue — use it when scheduling runs on first-come flow rather than fixed appointment slots. The patient gets a number against a schedulable resource (practitioner, location, or healthcare service), optionally routed to a sub-queue or room.

The Django models store the data; the rules that make a token correct — status enums, the resource binding, queue progression — live in the Pydantic resource specs and the API viewsets. Both layers are covered here.

Source:

Models

Four models make up the subsystem.

ModelPurpose
TokenQueueA queue of tokens for one schedulable resource on one date
TokenSubQueueA sub-queue splitting a resource's tokens (e.g. multiple rooms)
TokenCategoryReusable token categories per facility / resource type
TokenA numbered token issued to a patient within a queue

All four extend EMRBaseModel, the shared Care EMR base that supplies external_id, audit fields created_by/updated_by/created_date/modified_date, and soft-delete via deleted.

TokenQueue and TokenSubQueue both carry a resource FK to emr.SchedulableResource, a polymorphic binding to a practitioner (user), location, or healthcare_service, distinguished by resource_type. See Schedule.

Token fields

FieldTypeRequiredNotes
facilityFK → facility.Facility (CASCADE)yes (server)Copied from the queue's facility on create
patientFK → emr.Patient (CASCADE)optionalNullable. The patient the token belongs to. See Patient
queueFK → TokenQueue (CASCADE)yes (server)Taken from the URL queue, not the body
categoryFK → TokenCategory (CASCADE)yesResolved from the category UUID on create. Must be in the same facility as the queue
sub_queueFK → TokenSubQueue (CASCADE)optionalNullable. Must match the queue's facility and resource
numberIntegerFieldyes (server)Auto-assigned: count(tokens in queue with same category) + 1
statusCharField(255)yes (server)TokenStatusOptions enum (below). Starts at CREATED
is_nextBooleanField (default False)serverQueue-progression flag; clients never write it
noteTextFieldoptionalNullable free-text note
bookingFK → emr.TokenBooking (CASCADE)optionalNullable. Links the token to a booking. related_name="booking_token"

number is unique per category within a queue, not across the whole queue — so one patient can hold several tokens in the same queue, and two categories can both have a token 1.

TokenStatusOptions values

Defined in token/spec.py; bound on Token.status through the read and update specs.

ValueMeaning
UNFULFILLEDToken issued but not served
CREATEDDefault state on creation
IN_PROGRESSCurrently being served (set when made the sub-queue's current token)
FULFILLEDService completed
CANCELLEDCancelled
ENTERED_IN_ERRORSet automatically on delete (soft-delete)

TokenQueue

One resource, one date, one queue of tokens.

facility → FK Facility (CASCADE)
resource → FK SchedulableResource (CASCADE)
name → CharField(255)
is_primary → BooleanField (default True)
date → DateField
system_generated → BooleanField (default False)
  • is_primary is resolved server-side on create: the first queue for a (resource, date) pair becomes primary, later ones do not. The set_primary action re-points it.
  • generate_token creates system_generated queues implicitly (name "System Generated") when no primary queue exists for the date.

TokenSubQueue

A sub-queue routes one queue's tokens to several physical points — multiple vaccination rooms, say, each pulling from its own sub-queue.

facility → FK Facility (CASCADE)
resource → FK SchedulableResource (CASCADE)
name → CharField(255)
status → CharField(255) # TokenSubQueueStatusOptions
current_token → FK Token (CASCADE, nullable)

The spec binds status to TokenSubQueueStatusOptions:

Value
active
inactive

current_token is whichever token is being served at that point. The progression actions (set_next, set_next_token_to_subqueue) keep it current server-side.

TokenCategory

A reusable category scoped to a facility and resource type — "General", "Priority", and so on.

facility → FK Facility (CASCADE)
resource_type → CharField(255) # SchedulableResourceTypeOptions
name → CharField(255)
shorthand → CharField(255) # spec limits to max_length 5
metadata → JSONField (default dict)
default → BooleanField (default False)
  • The spec binds resource_type to SchedulableResourceTypeOptions (practitioner, location, healthcare_service).
  • shorthand is capped at max_length=5 by the spec even though the column allows 255.
  • metadata is an open JSON bag for deployment-specific category config, exposed as dict | None.
  • default is writable only through the set_default action, which clears default on every other category sharing the facility and resource type. It isn't a field on the create spec.

SchedulableResourceTypeOptions values

Defined in scheduling/schedule/spec.py; used by TokenCategory.resource_type and every *WithQueue / create spec that binds to a resource.

Value
practitioner
location
healthcare_service

Resource specs (API schema)

Every spec extends EMRResource (care/emr/resources/base.py): serialize builds the read payload from the model (plus perform_extra_serialization), and de_serialize writes a model from the payload (plus perform_extra_deserialization). id is always the model's external_id (UUID).

Token

SpecRoleExposed fields
TokenBaseSpecsharedid
TokenGenerateSpecwrite · create (nested under a queue)patient? (UUID), category (UUID, required), note?, sub_queue? (UUID)
TokenGenerateWithQueueSpecwrite · create (queue resolved/created)adds resource_type (SchedulableResourceTypeOptions), resource_id (UUID), date
TokenUpdateSpecwrite · updatestatus? (TokenStatusOptions), note?, sub_queue (UUID, nullable)
TokenMinimalSpecread · embeddednote, number, status, category (serialized via TokenCategoryReadSpec)
TokenReadSpecread · listcategory, sub_queue, note, patient, number, status, queue (TokenQueueReadSpec)
TokenRetrieveSpecread · detailextends TokenReadSpec + created_by/updated_by (UserSpec), booking, resource_type, resource, encounter?

What happens on write, from perform_extra_deserialization and the viewset:

  • TokenGenerateSpec.perform_extra_deserialization resolves the patient, category, and sub_queue UUIDs to model instances, returning 404 if any is missing.
  • TokenUpdateSpec.perform_extra_deserialization resolves the sub_queue UUID, or sets sub_queue = None when it's omitted — so an update can clear it.
  • On create (perform_create), queue and facility come from the URL queue, category-vs-queue and sub-queue-vs-queue facility/resource matching is enforced, number is computed under a per-queue lock, and status is forced to CREATED.
  • On update, changing the sub-queue clears the old sub-queue's current_token if it pointed at this token. A sub-queue that already has a current token can't be reassigned.
  • On delete (perform_destroy), the token is soft-deleted: status = ENTERED_IN_ERROR, deleted = True.
  • TokenRetrieveSpec embeds the booking (via TokenBookingMinimumReadSpec), the booking's associated_encounter (Encounter) when present, and the resolved resource (practitioner UserSpec / HealthcareServiceReadSpec / FacilityLocationListSpec) keyed by resource_type.

TokenQueue

SpecRoleExposed fields
TokenQueueBaseSpecsharedid, name
TokenQueueCreateSpecwrite · createadds resource_type (SchedulableResourceTypeOptions), resource_id (UUID), date
TokenQueueUpdateSpecwrite · updateid, name only
TokenQueueReadSpecread · listadds date, is_primary, system_generated
TokenQueueRetrieveSpecread · detailadds created_by, updated_by

TokenQueueCreateSpec.perform_extra_deserialization stashes resource_type and resource_id onto the instance. The viewset's perform_create then resolves or creates the SchedulableResource, sets facility, and computes is_primary.

TokenSubQueue

SpecRoleExposed fields
TokenSubQueueBaseSpecshared / write · updateid, name, status (TokenSubQueueStatusOptions)
TokenSubQueueCreateSpecwrite · createadds resource_type (SchedulableResourceTypeOptions), resource_id (UUID)
TokenSubQueueReadSpecreadadds current_token (TokenMinimalSpec, nullable)

TokenSubQueueCreateSpec.perform_extra_deserialization stashes resource_type and resource_id. On create the viewset resolves or creates the resource and validates it. current_token is serialized only when set.

TokenCategory

SpecRoleExposed fields
TokenCategoryBaseSpecsharedid, name, resource_type (SchedulableResourceTypeOptions), shorthand (max 5), metadata? (dict)
TokenCategoryCreateSpecwrite · createsame as base
TokenCategoryReadSpecread · listadds default
TokenCategoryRetrieveSpecread · detailadds created_by, updated_by

facility is set from the URL on create. default is read-only here and toggled through the set_default action.

Methods & save behaviour

  • Number assignmentToken.number is count(tokens with same queue + category) + 1, computed inside a Lock("booking:token:{queue.id}") and an atomic transaction so two tokens can't claim the same number.
  • Status lifecycle — create forces CREATED; set_next and set_next_token_to_subqueue set IN_PROGRESS; delete sets ENTERED_IN_ERROR. Status is a single field, with no separate history table.
  • Queue progression — custom actions maintain current_token (on TokenSubQueue) and is_next; clients never write them directly:
    • Token action set_next (POST .../{id}/set_next, body { sub_queue }) points the sub-queue's current_token at this token and marks it IN_PROGRESS.
    • TokenQueue action set_next_token_to_subqueue (POST .../{queue}/set_next_token_to_subqueue, body { sub_queue, category? }) picks the oldest CREATED token (optionally filtered by category), assigns it to the sub-queue as current_token, and sets IN_PROGRESS.
    • TokenQueue action set_primary makes one queue primary for its (resource, date), clearing the flag on siblings.
    • TokenCategory action set_default makes one category default per (facility, resource_type).
  • Token generation shortcutTokenQueue action generate_token (POST .../generate_token, body = TokenGenerateWithQueueSpec) finds or creates the primary (system_generated) queue for (resource, date) and issues a token in one call.
  • Queue summaryTokenQueue action summary returns per-category, per-status token counts for a queue.

API integration notes

  • Create tokens under a queue with TokenGenerateSpecqueue, facility, number, and status are all server-set — or call the queue's generate_token action with TokenGenerateWithQueueSpec, which also resolves or creates the queue.
  • Send category, patient, and sub_queue as UUIDs (external_id); the server resolves them to FKs. Cross-facility and cross-resource mismatches return 400.
  • The update spec accepts only TokenStatusOptions values for status, and TokenSubQueue.status only TokenSubQueueStatusOptions. Both columns are plain CharFields, so the spec layer enforces this, not the database.
  • Never set number, is_next, current_token, or is_primary directly — they're queue-progression state owned by the viewset actions above.
  • TokenCategory.metadata is an open JSON bag for deployment-specific config; shorthand is capped at 5 characters by the spec.
  • booking links a token back to a TokenBooking; on retrieve it expands to the booking and its associated encounter.