> ## Documentation Index
> Fetch the complete documentation index at: https://docs.timeback.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Core

> Unified client for all Timeback APIs with shared authentication

## Overview

The Core client (`@timeback/core` for TypeScript, `timeback-core` for Python) provides a unified client that aggregates all Timeback API clients with shared OAuth authentication:

* **OneRoster**: Rostering and gradebook data
* **EduBridge**: Simplified enrollments and analytics
* **Caliper**: Learning analytics events
* **QTI**: Assessment content management
* **PowerPath**: Placement tests and adaptive learning
* **CASE**: Competencies and standards
* **CLR**: Comprehensive Learner Records
* **Webhooks**: Webhook management and filters
* **MasteryTrack**: Test inventory and assignments

All sub-clients share a single OAuth token, reducing authentication overhead.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @timeback/core
  ```

  ```bash pnpm theme={null}
  pnpm add @timeback/core
  ```

  ```bash yarn theme={null}
  yarn add @timeback/core
  ```

  ```bash bun theme={null}
  bun add @timeback/core
  ```

  ```bash pip theme={null}
  pip install timeback-core
  ```

  ```bash uv theme={null}
  uv add timeback-core
  ```
</CodeGroup>

## Quick Start

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { TimebackClient } from '@timeback/core'

  const timeback = new TimebackClient({
  	env: 'staging',
  	auth: {
  		clientId: process.env.TIMEBACK_CLIENT_ID!,
  		clientSecret: process.env.TIMEBACK_CLIENT_SECRET!,
  	},
  })

  // Access any API through the unified client
  const { data: schools } = await timeback.oneroster.schools.list()
  const { data: users } = await timeback.oneroster.users.list({ where: { role: 'student' } })
  await timeback.caliper.events.sendActivity(sensorUrl, activityInput)
  ```

  ```python Python theme={null}
  from timeback_core import TimebackClient

  timeback = TimebackClient(
      env="staging",
      client_id=os.environ["TIMEBACK_CLIENT_ID"],
      client_secret=os.environ["TIMEBACK_CLIENT_SECRET"],
  )

  # Access any API through the unified client
  schools = await timeback.oneroster.schools.list()
  users = await timeback.oneroster.users.list(where={"role": "student"})
  await timeback.caliper.events.send_activity(sensor_url, activity_input)
  ```
</CodeGroup>

## Configuration

### Environment Mode

Connect to Timeback's hosted APIs:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const timeback = new TimebackClient({
  	env: 'staging', // or 'production'
  	auth: {
  		clientId: process.env.TIMEBACK_CLIENT_ID!,
  		clientSecret: process.env.TIMEBACK_CLIENT_SECRET!,
  	},
  })
  ```

  ```python Python theme={null}
  timeback = TimebackClient(
      env="staging",  # or "production"
      client_id=os.environ["TIMEBACK_CLIENT_ID"],
      client_secret=os.environ["TIMEBACK_CLIENT_SECRET"],
  )
  ```
</CodeGroup>

### Base URL Mode

Connect to a self-hosted or custom endpoint:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const timeback = new TimebackClient({
  	baseUrl: 'https://timeback.myschool.edu',
  	auth: {
  		clientId: process.env.CLIENT_ID!,
  		clientSecret: process.env.CLIENT_SECRET!,
  		authUrl: 'https://timeback.myschool.edu/oauth/token',
  	},
  })
  ```

  ```python Python theme={null}
  timeback = TimebackClient(
      services={
          "oneroster": "https://timeback.myschool.edu",
          "edubridge": "https://timeback.myschool.edu",
          "caliper": "https://timeback.myschool.edu",
      },
      auth_url="https://timeback.myschool.edu/oauth/token",
      client_id=os.environ["CLIENT_ID"],
      client_secret=os.environ["CLIENT_SECRET"],
  )
  ```
</CodeGroup>

### Explicit Services Mode

Full control over each service URL. Only configure the services you need:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const timeback = new TimebackClient({
  	services: {
  		oneroster: 'https://roster.example.com',
  		caliper: 'https://analytics.example.com',
  		// edubridge not configured — accessing it will throw
  	},
  	auth: {
  		clientId: '...',
  		clientSecret: '...',
  		authUrl: 'https://auth.example.com/oauth/token',
  	},
  })
  ```

  ```python Python theme={null}
  timeback = TimebackClient(
      services={
          "oneroster": "https://roster.example.com",
          "caliper": "https://analytics.example.com",
          "qti": "https://qti.example.com",
          "powerpath": "https://roster.example.com",
          "case": "https://roster.example.com",
          "clr": "https://roster.example.com",
          "webhooks": "https://analytics.example.com",
      },
      auth_url="https://auth.example.com/oauth/token",
      client_id="...",
      client_secret="...",
  )
  ```
</CodeGroup>

Each service can optionally override the default token URL (TypeScript):

```typescript theme={null}
const timeback = new TimebackClient({
	services: {
		oneroster: 'https://roster.example.com',
		caliper: {
			baseUrl: 'https://analytics.example.com',
			authUrl: 'https://analytics-auth.example.com/token',
		},
	},
	auth: {
		clientId: '...',
		clientSecret: '...',
		authUrl: 'https://auth.example.com/oauth/token',
	},
})
```

## Accessing APIs

Each API is lazily initialized on first access:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // OneRoster - rostering and gradebook
  timeback.oneroster.users.list()
  timeback.oneroster.schools.get(schoolId)
  timeback.oneroster.enrollments.list({ where: { role: 'student' } })

  // EduBridge - simplified enrollments and analytics
  timeback.edubridge.enrollments.list({ userId })
  timeback.edubridge.analytics.getActivity({ studentId, startDate, endDate })

  // Caliper - learning analytics
  timeback.caliper.events.list()
  timeback.caliper.events.sendActivity(sensor, input)

  // QTI - assessment content
  timeback.qti.assessmentItems.list()
  timeback.qti.assessmentTests.get(testId)

  // PowerPath - adaptive learning
  timeback.powerpath.placement.getCurrentLevel({ student, subject })

  // CASE - competencies and standards
  timeback.case.documents.list()

  // CLR - comprehensive learner records
  timeback.clr.credentials.upsert(credential)

  // Webhooks - webhook management
  timeback.webhooks.webhooks.list()
  ```

  ```python Python theme={null}
  # OneRoster - rostering and gradebook
  await timeback.oneroster.users.list()
  await timeback.oneroster.schools.get(school_id)
  await timeback.oneroster.enrollments.list(where={"role": "student"})

  # EduBridge - simplified enrollments and analytics
  await timeback.edubridge.enrollments.list(user_id=user_id)
  await timeback.edubridge.analytics.get_activity(student_id=user_id, start_date="...", end_date="...")

  # Caliper - learning analytics
  await timeback.caliper.events.list()
  await timeback.caliper.events.send_activity(sensor, input)

  # QTI - assessment content
  await timeback.qti.assessment_items.list()
  await timeback.qti.assessment_tests.get(test_id)

  # PowerPath - adaptive learning
  await timeback.powerpath.placement.get_current_level({"student": student_id, "subject": "Math"})

  # CASE - competencies and standards (note: case_ avoids Python keyword conflict)
  await timeback.case_.documents.list()

  # CLR - comprehensive learner records
  await timeback.clr.credentials.upsert(credential_input)

  # Webhooks - webhook management
  await timeback.webhooks.webhooks.list()
  ```
</CodeGroup>

## Managing Multiple Clients

For applications that need to manage multiple `TimebackClient` instances, use `TimebackManager`:

```typescript theme={null}
import { TimebackManager } from '@timeback/core'

const manager = new TimebackManager()
	.register('alpha', {
		env: 'production',
		auth: { clientId: '...', clientSecret: '...' },
	})
	.register('beta', {
		env: 'production',
		auth: { clientId: '...', clientSecret: '...' },
	})

// Target a specific platform
const users = await manager.get('alpha').oneroster.users.list()

// Sync a user across all platforms (uses Promise.allSettled — never throws)
const results = await manager.broadcast(client => client.oneroster.users.create(user))

// Direct property access
if (results.alpha.ok) {
	console.log('Created on alpha:', results.alpha.value.id)
}

// Convenience methods
if (results.allSucceeded) {
	console.log('Synced to all platforms!')
}

results.succeeded.forEach(([name, user]) => {
	console.log(`Created on ${name}:`, user.id)
})

results.failed.forEach(([name, error]) => {
	console.error(`Failed on ${name}:`, error.message)
})
```

### Manager API

| Method                   | Description                                                          |
| ------------------------ | -------------------------------------------------------------------- |
| `register(name, config)` | Add a named client                                                   |
| `get(name)`              | Retrieve a client by name                                            |
| `has(name)`              | Check if a client is registered                                      |
| `names`                  | Get all registered client names                                      |
| `size`                   | Get number of registered clients                                     |
| `broadcast(fn)`          | Execute on all clients (never throws), returns `BroadcastResults<T>` |
| `unregister(name)`       | Remove and close a client                                            |
| `close()`                | Close all clients                                                    |

### BroadcastResults API

| Property/Method | Description                                 |
| --------------- | ------------------------------------------- |
| `succeeded`     | Get successful results as `[name, value][]` |
| `failed`        | Get failed results as `[name, error][]`     |
| `allSucceeded`  | `true` if all operations succeeded          |
| `anyFailed`     | `true` if any operation failed              |
| `values()`      | Get all values (throws if any failed)       |

## Lifecycle

### Check Authentication

Verify OAuth credentials are working:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await timeback.checkAuth()

  if (result.ok) {
  	console.log('Auth OK, latency:', result.latencyMs, 'ms')
  } else {
  	console.error('Auth failed:', result.error)
  }
  ```

  ```python Python theme={null}
  result = await timeback.check_auth()

  if result["ok"]:
      print("Auth OK, latency:", result["latency_ms"], "ms")
  else:
      print("Auth failed:", result.get("error"))
  ```
</CodeGroup>

### Close the Client

Release resources when done:

<CodeGroup>
  ```typescript TypeScript theme={null}
  timeback.close()

  // After close(), further API calls will throw
  console.log(timeback.closed) // true
  ```

  ```python Python theme={null}
  await timeback.close()

  # Or use as an async context manager
  async with TimebackClient(...) as timeback:
      schools = await timeback.oneroster.schools.list()
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="OneRoster" icon="users" href="/beta/build-on-timeback/clients/oneroster">
    Rostering and gradebook API
  </Card>

  <Card title="EduBridge" icon="bridge" href="/beta/build-on-timeback/clients/edubridge">
    Simplified enrollments and analytics
  </Card>

  <Card title="Caliper" icon="chart-line" href="/beta/build-on-timeback/clients/caliper">
    Learning analytics events
  </Card>

  <Card title="SDK" icon="code" href="/beta/build-on-timeback/sdk/overview">
    Full-stack SDK with SSO and activity tracking
  </Card>
</CardGroup>
