# Primula API - [Introduction](https://developers.primulamanagement.com/index.md): Welcome to the Primula Tracker API - [Quickstart](https://developers.primulamanagement.com/quickstart.md): Authenticate and make your first request - [Introduction](https://developers.primulamanagement.com/api-reference/introduction.md): Conventions shared by every Primula Tracker API V3 endpoint - [Create a session](https://developers.primulamanagement.com/api-reference/sessions/create-a-session.md): Authenticate with username and password. A single JSON object is required and unknown properties are rejected. Returns access_token and refresh_token with HTTP 201; V3 has no refresh or logout operation. - [List schools](https://developers.primulamanagement.com/api-reference/schools/list-schools.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns an unpaginated array; school_ids and only_active are optional filters. - [Get a school](https://developers.primulamanagement.com/api-reference/schools/get-a-school.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns 404 when the school does not exist for the requested company. - [Update school capacity](https://developers.primulamanagement.com/api-reference/schools/update-school-capacity.md): Requires company_id, an authenticated session with schools assigned, and the target school in those assignments. Updates capacity, then reads the school in the requested company. A failed subsequent read can return 404 or 500 after the write has executed. - [Get company settings](https://developers.primulamanagement.com/api-reference/companies/get-company-settings.md): The path ID is the EMS company UUID. It must match the session company when the session has one. Returns 404 when the company or its settings are absent. - [Get a company](https://developers.primulamanagement.com/api-reference/companies/get-a-company.md): The path ID is the EMS company UUID. It must match the session company when the session has one. Returns 404 when the company or its settings are absent. - [List rooms](https://developers.primulamanagement.com/api-reference/rooms/list-rooms.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. school_id is required. Returns an unpaginated list. - [Get a room](https://developers.primulamanagement.com/api-reference/rooms/get-a-room.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. The room ID identifies the room; school_id is not required. - [List users](https://developers.primulamanagement.com/api-reference/users/list-users.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Without email, returns users sorted by updated_at and optionally filters to updated_at strictly after updated_after. When email is supplied, returns zero or one matching user in an array; updated_after i… - [Get a user](https://developers.primulamanagement.com/api-reference/users/get-a-user.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns 404 when the user does not exist for the company. - [List leads](https://developers.primulamanagement.com/api-reference/leads/list-leads.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns paginated lead rows, or paginated LeadStages objects when view=stages. - [Update stages for multiple leads](https://developers.primulamanagement.com/api-reference/leads/update-stages-for-multiple-leads.md): Requires school_id assigned to the session. The body is an array of 1 to 200 items, each with lead_id and at least one non-null stage. All items are validated before writes. Missing leads are omitted from the response. Repeated lead IDs are allowed; each appears once in the response in first-occurre… - [Get a lead](https://developers.primulamanagement.com/api-reference/leads/get-a-lead.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Requires school_id. fields selects properties from the lead projection; unknown fields return 400. Returns 404 when the scoped lead does not exist. - [List stages of a lead](https://developers.primulamanagement.com/api-reference/leads/list-stages-of-a-lead.md): Requires school_id assigned to the session. Each stage includes its own state and timestamp. The company lead-stages setting selects the underlying stage source for both reading and writing; it is not a read-only permission. - [Update lead stages](https://developers.primulamanagement.com/api-reference/leads/update-lead-stages.md): Requires school_id assigned to the session and at least one non-null boolean stage. Unknown properties return 400. Stages are written in the documented schema order. Each stage is a separate write: a later failure returns 404/500 and earlier stages may remain applied; an error response does not incl… - [Update intended start dates](https://developers.primulamanagement.com/api-reference/leads/update-intended-start-dates.md): Requires school_id assigned to the session. At most 200 lead IDs are accepted across all update groups, and duplicates anywhere in the request return 400. Dates must be future YYYY-MM-DD values or TBD. Leads with a CRM/Procare expected start date are not overwritten and are reported in lead_ids_not_… - [List students](https://developers.primulamanagement.com/api-reference/students/list-students.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Returns paginated students. The normal collection includes only Procare Desktop enrollment statuses configured as visible for the school. sta… - [Get a student](https://developers.primulamanagement.com/api-reference/students/get-a-student.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Requires school_id. Returns the full student or the requested field projection. This item read does not apply the collection's session-school assignment check. - [Update student dates and transition rooms](https://developers.primulamanagement.com/api-reference/students/update-student-dates-and-transition-rooms.md): Requires school_id assigned to the session and an authenticated user belonging to company_id. Attribution comes from that user; updated_by_email is accepted but ignored. At least one update property is required. Unknown properties return 400. Dates and room UUIDs may be cleared with null or an empty… - [List payments](https://developers.primulamanagement.com/api-reference/payments/list-payments.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Without group_by, returns sparse payment rows. With group_by and metrics, returns nested PaymentAggregate rows. school_id and school_ids are both applied when provided (their intersection); omitted schoo… - [Get a payment](https://developers.primulamanagement.com/api-reference/payments/get-a-payment.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Only school_id/school_ids and fields affect the item lookup. The shared filter parser still validates supplied family IDs, last_n_days, is_posted and amount bounds, but collection filters do not narrow t… - [List enrollment series](https://developers.primulamanagement.com/api-reference/enrollments/list-enrollment-series.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. The response representation is selected in this order: - [List family balances and ledgers](https://developers.primulamanagement.com/api-reference/family_balances/list-family-balances-and-ledgers.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Default: sparse balance rows, one per family, with optional fields. group_by plus metrics selects nested FamilyBalanceAggregate rows; family_ids does not filter aggregates. school_id and school_ids are b… - [List transactions for a family](https://developers.primulamanagement.com/api-reference/family_balances/list-transactions-for-a-family.md): Requires school_id assigned to the session. Returns an unpaginated array of per-family ledger objects, each containing student_ids and transactions; it is not a flat transaction array. The path ID is the family UUID. No matches produce an empty array rather than 404. last_n_days defaults to 30 and i… - [Get a family balance](https://developers.primulamanagement.com/api-reference/family_balances/get-a-family-balance.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. The path ID is the family UUID. school_id/school_ids narrow the lookup and fields selects the response properties. Other collection filters do not apply, although the shared parser still validates suppli… - [Get pre-registration fallout](https://developers.primulamanagement.com/api-reference/pre_registration_fallout/get-pre-registration-fallout.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Omitting view returns a PreRegistrationFalloutReport object containing years, school_summaries and total_summary. view=students returns a pag… - [List student absences](https://developers.primulamanagement.com/api-reference/absences/list-student-absences.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Provide school_id or student_id; student_id takes precedence when both are supplied (both IDs are still validated). Provide date_from, optionally date_to (defaults to today), OR a positive last_n_days. l… - [List waitlist entries](https://developers.primulamanagement.com/api-reference/waitlist/list-waitlist-entries.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Waitlist entries with room eligibility derived from the preferred start date. - [Get a waitlist entry](https://developers.primulamanagement.com/api-reference/waitlist/get-a-waitlist-entry.md): The student UUID identifies the entry. Requires schools assigned to the session. Collection filters do not apply. A student outside the session schools returns 403; no applicable entry returns 404. - [Update preferred start date](https://developers.primulamanagement.com/api-reference/waitlist/update-preferred-start-date.md): The student must exist in Waitlist onboarding status and belong to a school assigned to the session. preferred_start_date must be later than today. Recomputes and returns the waitlist entry and room eligibility. Clearing this date is not supported. The update may have executed if the subsequent read… - [List billing plans](https://developers.primulamanagement.com/api-reference/billing_plans/list-billing-plans.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns consolidated billing-plan rows, or nested BillingPlanAggregate rows when group_by and metrics are supplied. school_id is always required. A consolidated row keeps the ID of the first plan in its… - [Get a consolidated billing plan](https://developers.primulamanagement.com/api-reference/billing_plans/get-a-consolidated-billing-plan.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Requires school_id. The ID is the consolidated row ID returned by the collection (the first plan ID in a group). Collection filters do not affect the lookup, though the shared parser still validates any… - [Get the spaces snapshot](https://developers.primulamanagement.com/api-reference/spaces/get-the-spaces-snapshot.md): Requires schools assigned to the session. Only company_id and optional school_id are accepted; all other parameters return 400. With school_id, the school must be assigned to the session and its name selects the snapshot entry. Without school_id, the current implementation matches session school ent… - [Get detailed spaces](https://developers.primulamanagement.com/api-reference/spaces/get-detailed-spaces.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. The selected representation determines accepted parameters; every unsupported parameter returns 400. - [Update closed spaces](https://developers.primulamanagement.com/api-reference/spaces/update-closed-spaces.md): Requires an authenticated user in the Operations or Admin group, school_id assigned to the session, and room IDs belonging to that school. Each room_id/period pair must be unique in the body. closed_spaces must be nonnegative and no greater than room capacity. Positive values create/update overrides… - [Get enrollment tracker](https://developers.primulamanagement.com/api-reference/enrollment_tracker/get-enrollment-tracker.md): Requires school_id assigned to the session. Returns the school tracker grid and computed enrollment information, optionally narrowed by room_id and monthly period. Returns 404 if the tracker is absent. - [List school efficiency ratios](https://developers.primulamanagement.com/api-reference/efficiency_ratios/list-school-efficiency-ratios.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Returns ratios sorted by school_name ascending. No school_id filter or pagination is implemented. - [List incomplete Procare message tasks](https://developers.primulamanagement.com/api-reference/procare_messages/list-incomplete-procare-message-tasks.md): Lists incomplete tasks in the global message queue. Tasks are not scoped to a company or school. status=incomplete is required; completed-task history is not available from this operation. last_n_hours defaults to 6 and accepts positive fractional hours. Returns an unpaginated array. - [Queue a Procare message](https://developers.primulamanagement.com/api-reference/procare_messages/queue-a-procare-message.md): Queues office_chat or classroom_chat delivery through the configured message service. Requires authentication but does not take company_id or enforce a company/school scope here. HTTP 202 means the task was accepted, not that delivery has finished. Poll the relative URL in Location for status. Non-2… - [Get Procare message task status](https://developers.primulamanagement.com/api-reference/procare_messages/get-procare-message-task-status.md): Reads a task in the global queue. No company/school filter is applied. A service-side 404 becomes a JSON 404; other unsuccessful or malformed upstream responses become 502. The bundled service returns HTTP 200 with JSON null for an unknown task; V3 preserves that result. - [List attendance records](https://developers.primulamanagement.com/api-reference/attendances/list-attendance-records.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Provide date_from AND date_to, or a positive last_n_days; mixing them returns 400. Defaults min_hours to 2; records below that threshold are excluded. room_id/room_ids and student_id/student_ids are merg… - [List attendance billing audits](https://developers.primulamanagement.com/api-reference/billing_attendance_audits/list-attendance-billing-audits.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns attendance checked against billing, including unpaid_day. The backend supports a rolling window only: last_n_days is required. Each result is an attendance row flattened from its student record. - [Get classroom attendance](https://developers.primulamanagement.com/api-reference/classroom_attendance/get-classroom-attendance.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Daily classroom counters and student states, with their synchronization timestamp. date defaults to the current server date. Returns 404 if no snapshot exists. - [List activity summaries](https://developers.primulamanagement.com/api-reference/activities_summary/list-activity-summaries.md): Authenticated proxy to the activity service. company_id and school_id are required nonempty strings. This handler does not validate them as UUIDs or apply session company/school equality checks. It forwards the upstream status, Content-Type and body. Local validation and gateway errors are plain tex… - [List detailed activities](https://developers.primulamanagement.com/api-reference/activity_details/list-detailed-activities.md): Authenticated proxy to the activity service. company_id and school_id are required nonempty strings. This handler does not validate them as UUIDs or apply session company/school equality checks. It forwards the upstream status, Content-Type and body. Local validation and gateway errors are plain tex… - [Get company data refresh status](https://developers.primulamanagement.com/api-reference/data_refresh/get-company-data-refresh-status.md): Returns the latest recorded company process. Returns 404 when no process is available. last_data_refresh_at is a display timestamp rather than an RFC3339 value. - [List school data refresh details](https://developers.primulamanagement.com/api-reference/data_refresh/list-school-data-refresh-details.md): Returns the latest school process statuses grouped by school. An explicit school_id must be assigned to the session. Without school_id, the current process lookup requests company-wide statuses. The school-name lookup uses the session school assignments. Returns an array without pagination headers. - [List sibling discount entries](https://developers.primulamanagement.com/api-reference/sibling_discounts/list-sibling-discount-entries.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Students from families with siblings and resolved billing-plan discounts. - [Get a sibling discount entry](https://developers.primulamanagement.com/api-reference/sibling_discounts/get-a-sibling-discount-entry.md): The student UUID identifies the entry. Requires schools assigned to the session. Collection filters do not apply. A student outside the session schools returns 403; no applicable entry returns 404. - [List new start tracker entries](https://developers.primulamanagement.com/api-reference/new_start_tracker/list-new-start-tracker-entries.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Start-date invoicing, service windows, and pro-rate information per student. - [Get a new start tracker entry](https://developers.primulamanagement.com/api-reference/new_start_tracker/get-a-new-start-tracker-entry.md): The student UUID identifies the entry. Requires schools assigned to the session. Collection filters do not apply. A student outside the session schools returns 403; no applicable entry returns 404. - [List withdrawal tracker entries](https://developers.primulamanagement.com/api-reference/withdrawal_tracker/list-withdrawal-tracker-entries.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Withdrawal invoicing, deposit and service-window information per student. - [Get a withdrawal tracker entry](https://developers.primulamanagement.com/api-reference/withdrawal_tracker/get-a-withdrawal-tracker-entry.md): The student UUID identifies the entry. Requires schools assigned to the session. Collection filters do not apply. A student outside the session schools returns 403; no applicable entry returns 404. - [List transition tracker entries](https://developers.primulamanagement.com/api-reference/transition_tracker/list-transition-tracker-entries.md): The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. Student transition date, destination room, days until transition, and alert information. - [Get a transition tracker entry](https://developers.primulamanagement.com/api-reference/transition_tracker/get-a-transition-tracker-entry.md): The student UUID identifies the entry. Requires schools assigned to the session. Collection filters do not apply. A student outside the session schools returns 403; no applicable entry returns 404. - [List gusto company records](https://developers.primulamanagement.com/api-reference/gusto_companies/list-gusto-company-records.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns the company catalog as an unpaginated array. - [List employee time off](https://developers.primulamanagement.com/api-reference/gusto_companies/list-employee-time-off.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. First verifies that the Gusto company belongs to company_id, returning 404 if absent. Defaults date_to to today and date_from to three calendar months before date_to. date_to cannot precede date_from. So… - [Get a gusto company](https://developers.primulamanagement.com/api-reference/gusto_companies/get-a-gusto-company.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. The ID is the internal record UUID. Records outside the requested company return 404. - [List gusto employee records](https://developers.primulamanagement.com/api-reference/gusto_employees/list-gusto-employee-records.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns the company catalog as an unpaginated array. - [Get a gusto employee](https://developers.primulamanagement.com/api-reference/gusto_employees/get-a-gusto-employee.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. The ID is the internal record UUID. Records outside the requested company return 404. - [List gusto contractor records](https://developers.primulamanagement.com/api-reference/gusto_contractors/list-gusto-contractor-records.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns the company catalog as an unpaginated array. - [Get a gusto contractor](https://developers.primulamanagement.com/api-reference/gusto_contractors/get-a-gusto-contractor.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. The ID is the internal record UUID. Records outside the requested company return 404. - [List payrolls](https://developers.primulamanagement.com/api-reference/gusto_payrolls/list-payrolls.md): Requires a school assigned to the session. period selects one pay-period end date; period_from and period_to select a range. period cannot be combined with either range bound, and period_to cannot precede period_from. Returns an unpaginated array without collection metadata headers. Omitting selecto… - [List payroll totals](https://developers.primulamanagement.com/api-reference/gusto_payrolls/list-payroll-totals.md): Requires a school assigned to the session. period selects one pay-period end date; period_from and period_to select a range. period cannot be combined with either range bound, and period_to cannot precede period_from. Returns an unpaginated array without collection metadata headers. When selectors a… - [List Procare staff](https://developers.primulamanagement.com/api-reference/procare_staff/list-procare-staff.md): Requires school_id assigned to the session. Returns the school staff array directly, without pagination or X-Total-Count. - [Get a Procare staff member](https://developers.primulamanagement.com/api-reference/procare_staff/get-a-procare-staff-member.md): Scoped to company_id; the handler does not restrict this read to the session school assignments. Returns the staff record directly. A missing record raises a data-layer error which this handler currently reports as HTTP 500. - [Connect your MCP](https://developers.primulamanagement.com/connect-mcp.md): Give your AI tools access to the Primula API docs ## OpenAPI Specs - [openapi](/openapi.yaml) This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.