Level 1: Student Onboarding

Programmatically create and manage students, users, and their relationships (parents, guardians) in TimeBack's roster.

What Student Onboarding Provides

Student onboarding enables your learning app to:

  • Create and update student records in TimeBack's roster
  • Manage user accounts with roles and profiles
  • Link students to agents (parents, guardians, relatives)
  • Query agent relationships for a user

This is foundational for apps that need to manage student data programmatically rather than relying solely on LTI authentication or manual registration.

Prerequisites

Before implementing student onboarding:

  1. Register your app (see Level 0: Registration)
  2. Request OAuth credentials with the roster.createput scope (see Authentication Guide)

Implementation Guide

Step 1: Obtain Access Token

Request an access token with roster write permissions:

const response = await fetch('https://platform.timeback.com/auth/1.0/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,
  },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    scope: 'https://purl.imsglobal.org/spec/or/v1p2/scope/roster.createput',
  }),
});

const { access_token } = await response.json();

Step 2: Create or Update a Student

Use the upsertStudent endpoint to create a new student or update an existing one:

PUT https://platform.timeback.com/rostering/1.0/students

Example request:

const studentData = {
  student: {
    sourcedId: 'student-uuid-here', // Your unique identifier for this student
    status: 'active',
    username: 'john.doe',
    enabledUser: 'true',
    givenName: 'John',
    familyName: 'Doe',
    middleName: null,
    email: 'john.doe@example.com',
    phone: null,
    grades: ['5'],
    primaryOrg: {
      sourcedId: 'organization-uuid', // The school/org this student belongs to
      type: 'org',
    },
  },
};

const response = await fetch('https://platform.timeback.com/rostering/1.0/students', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${accessToken}`,
  },
  body: JSON.stringify(studentData),
});

// Returns 200 OK on success

Required fields:

Field Type Description
sourcedId string Unique identifier for the student
status string active, inactive, or tobedeleted
username string Login username
enabledUser string "true" or "false" (as strings)
givenName string Student's first name
familyName string Student's last name
primaryOrg object Reference to the student's primary organization
grades string[] Array of grade levels (e.g., ["5", "6"])

Step 3: Create or Update a User

For non-student users (teachers, parents, administrators), use the upsertUser endpoint:

PUT https://platform.timeback.com/rostering/1.0/users/{sourcedId}

Example request:

const userData = {
  user: {
    sourcedId: 'user-uuid-here',
    status: 'active',
    enabledUser: 'true',
    givenName: 'Jane',
    familyName: 'Smith',
    email: 'jane.smith@example.com',
    grades: [],
    primaryOrg: {
      sourcedId: 'organization-uuid',
      type: 'org',
    },
    roles: [
      {
        roleType: 'primary',
        role: 'parent',
        org: {
          sourcedId: 'organization-uuid',
          type: 'org',
        },
      },
    ],
  },
};

const response = await fetch(`https://platform.timeback.com/rostering/1.0/users/${userData.user.sourcedId}`, {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${accessToken}`,
  },
  body: JSON.stringify(userData),
});

// Returns 201 Created on success

Available roles:

  • student, teacher, parent, guardian, relative
  • aide, counselor, principal, proctor
  • districtAdministrator, siteAdministrator, systemAdministrator

Create relationships between students and their agents (parents, guardians):

PUT https://platform.timeback.com/rostering/1.0/students/{sourcedId}/agents/{agentId}

Example request:

const agentData = {
  user: {
    sourcedId: 'parent-user-uuid', // The agent's user ID
    type: 'user',
  },
  relationshipType: 'parent', // free-form string describing the relationship
};

const response = await fetch(
  `https://platform.timeback.com/rostering/1.0/students/${studentId}/agents/${agentRelationshipId}`,
  {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify(agentData),
  },
);

// Returns 201 Created on success

Relationship type:

The relationshipType is a free-form string. Use any value that describes the relationship in your system.

Step 5: Query User Agents

Retrieve all agents associated with a user:

GET https://platform.timeback.com/rostering/1.0/users/{sourcedId}/agents

Example request:

const response = await fetch(`https://platform.timeback.com/rostering/1.0/users/${userId}/agents`, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${accessToken}`,
  },
});

const data = await response.json();
// Returns: { agents: [{ user: { sourcedId, type }, relationshipType }] }

Step 6: Remove an Agent Relationship

Delete an agent relationship when it's no longer valid:

DELETE https://platform.timeback.com/rostering/1.0/students/{sourcedId}/agents/{agentId}

Example request:

const response = await fetch(
  `https://platform.timeback.com/rostering/1.0/students/${studentId}/agents/${agentRelationshipId}`,
  {
    method: 'DELETE',
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  },
);

// Returns 204 No Content on success

Required Scopes

Operation Required Scope
upsertStudent https://purl.imsglobal.org/spec/or/v1p2/scope/roster.createput
upsertUser https://purl.imsglobal.org/spec/or/v1p2/scope/roster.createput
upsertStudentAgent https://purl.imsglobal.org/spec/or/v1p2/scope/roster.createput
deleteStudentAgent https://purl.imsglobal.org/spec/or/v1p2/scope/roster.createput
getUserAgents https://purl.imsglobal.org/spec/or/v1p2/scope/roster.readonly

Common Pitfalls

Not matching sourcedId in path and body

// ❌ Bad: Path and body sourcedId don't match
await fetch('/rostering/1.0/users/user-123', {
  body: JSON.stringify({ user: { sourcedId: 'user-456', ... } }),
});

// ✅ Good: Path and body sourcedId match
await fetch('/rostering/1.0/users/user-123', {
  body: JSON.stringify({ user: { sourcedId: 'user-123', ... } }),
});

Using boolean instead of string for enabledUser

// ❌ Bad: Boolean value
{
  enabledUser: true;
}

// ✅ Good: String value
{
  enabledUser: 'true';
}

Missing required organization reference

// ❌ Bad: Missing primaryOrg
{ student: { sourcedId: '...', givenName: 'John', ... } }

// ✅ Good: Include primaryOrg
{
  student: {
    sourcedId: '...',
    givenName: 'John',
    primaryOrg: { sourcedId: 'org-uuid', type: 'org' },
    ...
  }
}

Register your app first to get OAuth credentials for API access.

Implement seamless authentication so students can access your app without credentials.

Send learning activity events to unlock coaching insights and unified analytics.

Detailed guide on obtaining and managing OAuth access tokens.