ROAR DocumentationROAR Documentation
  • Tech Specs
  • Databases
  • Workflows
  • Application
  • GitHub Actions
  • Dashboard Components
  • Firebase App Check
  • Cloud Functions
  • Backend Architecture
  • Internationalization
  • Integrating New Apps
  • Optimizing Assets
  • ROAR Redivis Instance
  • Logging and Querying
  • Emulation
  • Data Tools
  • Data Organization
  • Data Requests
GitHub
  • Tech Specs
  • Databases
  • Workflows
  • Application
  • GitHub Actions
  • Dashboard Components
  • Firebase App Check
  • Cloud Functions
  • Backend Architecture
  • Internationalization
  • Integrating New Apps
  • Optimizing Assets
  • ROAR Redivis Instance
  • Logging and Querying
  • Emulation
  • Data Tools
  • Data Organization
  • Data Requests
GitHub
  • Tech Specs
    • ROAR Backend Technical Specification
    • ROAR Administrations & Assignments: Technical Specification
    • ROAR Agreements: Technical Specification
    • ROAR Assessment Execution: Technical Specification
    • ROAR Task Integration with Measurement Services: Technical Specification
    • ROAR Parent Accounts: Technical Specification
    • ROAR Permissions: Technical Specification
    • ROAR Rostering Integration Spec
    • ROAR Task Configuration and Variant System: Technical Specification
    • ROAR Users and Organizations: Technical Specification
  • Databases
    • Database Information
    • gse-roar-admin
    • gse-roar-assessment
  • BigQuery
    • Querying Assessment Data
    • BigQuery schema: administrations
    • BigQuery Runtime Configuration File
    • BigQuery schema: classes
    • BigQuery schema: districts
    • BigQuery schema: families
    • BigQuery schema: groups
    • BigQuery schema: legal_versions
    • BigQuery schema: legal
    • Querying Admin Data
    • BigQuery schema: schools
    • BigQuery schema: user_runs
    • BigQuery schema: user_trials
    • BigQuery schema: users
  • Workflows
    • Workflows
    • Creating an Assignment
    • Authentication
    • Creating new Users
    • User Roster Changes
    • How to Impersonate a Clever User on Localhost
  • Application

    • Auth
  • GitHub Actions
    • ROAR Apps GitHub Actions
      • GitHub Actions in ROAR Apps
      • firebase-deploy-preview.yml
      • firebase-hosting-merge.yml
      • publish-to-npm-create-new-release.yml
      • submit-dashboard-pr.yml
    • ROAR Dashboard GitHub Actions
      • GitHub Actions in the ROAR Dashboard
  • Dashboard Components
    • Dashboard Components
    • Organization Score Reports
  • Firebase App Check
    • Firebase App Check Configuration for roar-firekit and roar-dashboard
  • Backend Architecture
    • Architecture
      • Backend Architecture in ROAR
      • Data Models
      • Database Implementations
      • Error Handling Architecture in ROAR
      • Repository Layer Architecture
      • Service Layer Architecture
    • API
      • Classes
        • Class: AdministrationServiceError
        • Class: FirebaseClientError
        • Class: FirebaseImplementationError
        • Class: FirestoreAdministrationRepository
        • Class: FirestoreAdministrationRepositoryError
        • Class: abstract FirestoreBaseRepository<T>
        • Class: FirestoreFilterAdapter
        • Class: FirestoreIdentityProviderRepository
        • Class: FirestoreIdentityProviderRepositoryError
        • Class: FirestoreOrgRepository
        • Class: FirestoreOrgRepositoryError
        • Class: FirestoreRepositoryError
        • Class: FirestoreUserClaimRepository
        • Class: FirestoreUserClaimRepositoryError
        • Class: FirestoreUserRepository
        • Class: FirestoreUserRepositoryError
        • Class: IdentityProviderServiceError
        • Classes
      • Enumerations
        • Enumeration: CollectionType
        • Enumeration: IdentityProviderType
        • Enumeration: Operator
        • Enumerations
      • Functions
        • Functions
        • Function: chunkOrgs()
        • Function: createAdministrationService()
        • Function: createFirestoreImplementation()
        • Function: createIdentityProviderService()
        • Function: isEmptyOrgs()
      • Interfaces
        • Interface: Administration
        • Interface: AdministrationBaseRepository
        • Interface: AdministrationService
        • Interface: AssentConsent
        • Interface: Assessment
        • Interface: BaseModel
        • Interface: BaseRepository<T>
        • Interface: Claims
        • Interface: CompositeCondition
        • Interface: CompositeFilter
        • Interface: CreateAdministrationServiceParams<AdminRepo, OrgRepo, UserClaimRepo>
        • Interface: CreateParams
        • Interface: DeleteParams
        • Interface: EducationalOrgsList
        • Interface: FieldCondition
        • Interface: FilterAdapter<T>
        • Interface: FirestoreCreateParams
        • Interface: FirestoreDeleteParams
        • Interface: FirestoreFetchDocumentParams
        • Interface: FirestoreGetAllParams
        • Interface: FirestoreGetByIdParams
        • Interface: FirestoreGetByNameParams
        • Interface: FirestoreGetByRoarUidParams
        • Interface: FirestoreGetParams
        • Interface: FirestoreGetWithFiltersParams
        • Interface: FirestoreImplementation
        • Interface: FirestoreRunTransactionParams<T>
        • Interface: FirestoreUpdateParams
        • Interface: FutureParams
        • Interface: GetAdministrationIdsForAdministratorParams
        • Interface: GetAdministrationIdsFromOrgsParams
        • Interface: GetAllParams
        • Interface: GetByNameParams
        • Interface: GetByProviderIdParams
        • Interface: GetByRoarUidParams
        • Interface: GetParams
        • Interface: GetRoarUidParams
        • Interface: IdentityProvider
        • Interface: IdentityProviderBaseRepository
        • Interface: IdentityProviderService
        • Interface: Legal
        • Interface: OrgBase
        • Interface: OrgBaseRepository
        • Interface: OrgsList
        • Interfaces
        • Interface: Result<T>
        • Interface: RunTransactionParams<T>
        • Interface: SingleFilter
        • Interface: UpdateParams
        • Interface: User
        • Interface: UserBaseRepository
        • Interface: UserClaim
        • Interface: UserClaimBaseRepository
        • Interface: createIdentityProviderServiceParams<IDPRepo, UserClaimRepo, UserRepo>
        • Interface: getAdministrationIdsFromOrgsParams
        • Interface: _setAdministrationIdsParams
      • Type Aliases
        • Type Alias: BaseFilter
        • Type Alias: ComparisonOperator
        • Type Alias: Condition
        • Type Alias: DocumentCreatedEvent
        • Type Alias: DocumentDeletedEvent
        • Type Alias: DocumentUpdatedEvent
        • Type Alias: DocumentWrittenEvent
        • Type Alias: ParameterValue
        • Type Aliases
        • Type Alias: SelectAllCondition
      • Variables
        • Variable: FirebaseAppClient
        • Variable: FirebaseAuthClient
        • Variable: FirestoreClient
        • Variable: ORG_NAMES
        • Variables API Documentation
    • Examples
      • Examples
    • Guides
      • Guides
  • Cloud Functions
    • gse-roar-admin
      • Admin Database
      • appendToAdminClaims()
      • associateassessmentuid()
      • createAdministratorAccount()
      • createGuestDocsForGoogleUsers()
      • createLevanteGroup()
      • createLevanteUsers()
      • createnewfamily()
      • createstudentaccount()
      • mirrorClasses()
      • mirrorCustomClaims
      • mirrorDistricts()
      • mirrorFamilies()
      • mirrorGroups()
      • mirrorSchools()
      • removefromadminclaims()
      • saveSurveyResponses()
      • setuidcustomclaims()
      • softDeleteUserAssignment()
      • softDeleteUserExternalData
      • softDeleteUser()
      • syncAssignmentCreated()
      • syncAssignmentDeleted()
      • syncAssignmentUpdated()
      • syncAssignmentsOnAdministrationUpdate()
      • syncAssignmentsOnUserUpdate()
      • syncCleverOrgs()
      • syncCleverUser()
    • gse-roar-assessment
      • Assessment Database
      • organizeBucketLogsByDate()
      • setuidclaims()
      • softDeleteGuestTrial()
      • softDeleteGuest()
      • softDeleteUserRun()
      • softDeleteUserTrial()
      • syncOnRunDocUpdate()
  • Internationalization
    • ROAM Fluency
    • ROAR Letter
    • ROAR Phoneme
    • Internationalization of ROAR Apps
    • ROAR Sentence
    • ROAR Word
  • Integrating New Apps
    • Integrating Roar Apps into the Dashboard
    • Dashboard Integration
    • Monitoring and Testing
    • Preparing the App for Packaging and Deployment
    • Packaging and Publishing to npm
    • Secrets in the GitHub Repository
  • Assets Optimization
    • Optimizing Assets
    • Audio Optimization Guide
    • Image Optimization Guide
  • ROAR Redivis Instance
    • ROAR Redivis Instance
    • ROAR Data Validator Trigger
    • ROAR Data Validator
  • Logging and Querying
    • ROAR Logging
  • Emulation
    • Running the Emulator
      • Commands
    • Emulator Configuration Guide
      • Configuration
      • Cypress Configuration
      • Setup and Dependencies
      • Firebase CLI Configuration
      • Firebase Emulator Configuration
      • GitHub Secrets and Workflows
      • Importing and Exporting Data
      • Local Environment Variables
  • Clowder Implementation
    • Clowder Integration
    • Letter - Clowder
    • Multichoice - Clowder
    • Phoneme - Clowder
    • ARF & CALF - Clowder

ROAR Rostering Integration Spec

Overview

This document defines how ROAR integrates with rostering partners (e.g. school districts) to ingest and synchronize user, organization, course, and enrollment data. It supports both API-based (e.g., OneRoster, Clever) and CSV-based integration.

Scope

  • In scope: Weekly ingestion of users, orgs, courses, enrollments; marking un-updated entities; generating unenrollment lists; deferred SSO binding.
  • Out of scope: Transformation logic between OneRoster/Clever and ROAR models. Transformation logic for CSV upload.

Actors

  • District rostering feed (API or CSV)
  • ROAR ingestion service
  • ROAR auth service
  • Admin dashboard / reviewer

Data Models

  • External Entities: user, org, course, class, enrollment
  • Internal Entities: ROARUser, ROAROrg, ROARCourse, ROAREnrollment
  • Deferred Auth Binding: If user is ingested via API, auth_uid is not assigned until first SSO login

Workflows

Full Rostering Ingestion

ROAR currently only supports full rostering ingestion. Future work may support incremental ingestion using "delta" records. This full rostering sync can be run on command or scheduled weekly. A "rostering run" is a single execution of the ingestion process for one rostering partner.

Deferred Auth UID Binding

Unenrollment Flow (stale user detection)

Roster Sync Validation

Monitoring & Alerts

Rostering events and sync statuses are recorded in the user_rostering_events and rostering_sync_status tables, respectively. See below for table schemas.

Post-sync validation

After the unenrollment flow is complete, ROAR runs a validation script that:

  • Counts the number of active users, orgs, and classes provided by the rostering partner
  • Compares these counts to the number of active users, orgs, and classes in ROAR
  • Logs mismatches and raises alerts if discrepancies exceed defined thresholds

SQL Schema

users

See the users-orgs spec for the full schema. The users table contains a last_rostering_update field that tracks the last time a user was ingested via rostering.

rostering_partners

CREATE TABLE rostering_partners (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  org_id UUID REFERENCES orgs(id), -- The top level ROAR org that this rostering partner corresponds to
  name TEXT UNIQUE NOT NULL,
  display_name TEXT NOT NULL,
  description TEXT,
  created_at TIMESTAMP DEFAULT now(),
  updated_at TIMESTAMP DEFAULT now(),
  deleted_at TIMESTAMP,
);

rostering_runs

CREATE TABLE rostering_runs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  partner_id UUID REFERENCES rostering_partners(id),
  ended_at TIMESTAMP,
  success BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT now(),
  updated_at TIMESTAMP DEFAULT now(),
  deleted_at TIMESTAMP,
);

rostering_run_stats

To fully capture counts for ingested, created, updated, and unenrolled entities per type, we use a normalized schema. This is cleaner and more extensible than expanding the rostering_runs table.

CREATE TABLE rostering_run_stats (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  run_id UUID REFERENCES rostering_runs(id),
  entity_type TEXT NOT NULL CHECK (entity_type IN ('user', 'org', 'class', 'course', 'enrollment')),
  action TEXT NOT NULL CHECK (action IN ('created', 'updated', 'unenrolled', 'skipped', 'failed')),
  count INTEGER NOT NULL,
  created_at TIMESTAMP DEFAULT now(),
  updated_at TIMESTAMP DEFAULT now(),
  deleted_at TIMESTAMP,
);

user_rostering_events

Discuss

Is this necessary? We could just use the rostering_sync_status table to capture this information.

This table captures notable, discrete events during rostering runs, primarily for audit and tracking. Think of it as a log of business-level outcomes like:

  • A user being unenrolled
  • An auth UID being bound
  • A batch being skipped due to validation failure
CREATE TABLE user_rostering_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  rostering_run_id UUID NOT NULL REFERENCES rostering_runs(id),
  event_type TEXT NOT NULL CHECK (event_type IN ('unenroll', 'auth_bind', 'batch_skip')),
  user_id UUID NOT NULL REFERENCES users(id),
  timestamp TIMESTAMP,
  details JSONB,
  created_at TIMESTAMP DEFAULT now(),
  updated_at TIMESTAMP DEFAULT now(),
  deleted_at TIMESTAMP,
);

rostering_sync_status

This table records the result of attempting to sync each entity (user, org, class, etc.) in a given ingestion run. It’s used for:

  • Tracking which entities succeeded/failed
  • Supporting retries on partial failures
  • Diagnosing ingestion problems at the row level
CREATE TABLE rostering_sync_status (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  rostering_run_id UUID NOT NULL REFERENCES rostering_runs(id),
  entity_type TEXT NOT NULL CHECK (entity_type IN ('user', 'org', 'class', 'course', 'enrollment')),
  entity_id UUID NOT NULL,
  status TEXT, -- 'success', 'failed', 'skipped', etc.
  error_message TEXT,
  processed_at TIMESTAMP DEFAULT now()
  created_at TIMESTAMP DEFAULT now(),
  updated_at TIMESTAMP DEFAULT now(),
  deleted_at TIMESTAMP,
);
Edit this page
Last Updated:
Contributors: Adam Richie-Halford
Prev
ROAR Permissions: Technical Specification
Next
ROAR Task Configuration and Variant System: Technical Specification