Overview
The@timeback/clr package provides a client for the CLR v2.0 API, enabling:
- Credentials: Upsert verifiable CLR credentials with platform-signed proofs
- Discovery: Retrieve API capabilities via the OpenAPI discovery endpoint
Installation
npm install @timeback/clr
pnpm add @timeback/clr
yarn add @timeback/clr
bun add @timeback/clr
pip install timeback-clr
uv add timeback-clr
Quick Start
import { ClrClient } from '@timeback/clr'
const client = new ClrClient({
env: 'staging',
auth: {
clientId: process.env.CLR_CLIENT_ID!,
clientSecret: process.env.CLR_CLIENT_SECRET!,
},
})
// Upsert a CLR credential
const credential = await client.credentials.upsert({
'@context': [
'https://www.w3.org/ns/credentials/v2',
'https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1',
],
id: 'urn:uuid:example-clr-id',
type: ['VerifiableCredential', 'ClrCredential'],
issuer: {
id: 'https://example.edu',
type: ['Profile'],
name: 'Example University',
},
name: 'Student Transcript',
validFrom: '2024-01-01T00:00:00Z',
credentialSubject: {
type: ['ClrSubject'],
verifiableCredential: [],
},
})
// Get API discovery information
const discovery = await client.discovery.get()
from timeback_clr import ClrClient
client = ClrClient(
env="staging",
client_id=os.environ["CLR_CLIENT_ID"],
client_secret=os.environ["CLR_CLIENT_SECRET"],
)
# Upsert a CLR credential
credential = await client.credentials.upsert({
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1",
],
"id": "urn:uuid:example-clr-id",
"type": ["VerifiableCredential", "ClrCredential"],
"issuer": {
"id": "https://example.edu",
"type": ["Profile"],
"name": "Example University",
},
"name": "Student Transcript",
"validFrom": "2024-01-01T00:00:00Z",
"credentialSubject": {
"type": ["ClrSubject"],
"verifiableCredential": [],
},
})
# Get API discovery information
discovery = await client.discovery.get()
Credentials
Create or update verifiable CLR credentials. The platform digitally signs each credential, adding a cryptographic proof to ensure authenticity and integrity. Inputs are validated with Zod schemas before requests.const result = await client.credentials.upsert({
'@context': [
'https://www.w3.org/ns/credentials/v2',
'https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1',
],
id: 'urn:uuid:student-transcript-001',
type: ['VerifiableCredential', 'ClrCredential'],
issuer: {
id: 'https://example.edu',
type: ['Profile'],
name: 'Example University',
},
name: 'Student Achievement Record',
validFrom: '2024-01-01T00:00:00Z',
credentialSubject: {
type: ['ClrSubject'],
verifiableCredential: [
{
'@context': ['https://www.w3.org/ns/credentials/v2'],
id: 'urn:uuid:achievement-001',
type: ['VerifiableCredential', 'AchievementCredential'],
issuer: {
id: 'https://example.edu',
type: ['Profile'],
name: 'Example University',
},
name: 'Algebra I Completion',
validFrom: '2024-06-15T00:00:00Z',
credentialSubject: {
type: ['AchievementSubject'],
achievement: {
id: 'https://example.edu/achievements/algebra-1',
type: ['Achievement'],
name: 'Algebra I',
criteria: { narrative: 'Completed all units with 80%+ mastery' },
achievementType: 'Competency',
},
},
},
],
},
})
result = await client.credentials.upsert({
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1",
],
"id": "urn:uuid:student-transcript-001",
"type": ["VerifiableCredential", "ClrCredential"],
"issuer": {
"id": "https://example.edu",
"type": ["Profile"],
"name": "Example University",
},
"name": "Student Achievement Record",
"validFrom": "2024-01-01T00:00:00Z",
"credentialSubject": {
"type": ["ClrSubject"],
"verifiableCredential": [
{
"@context": ["https://www.w3.org/ns/credentials/v2"],
"id": "urn:uuid:achievement-001",
"type": ["VerifiableCredential", "AchievementCredential"],
"issuer": {
"id": "https://example.edu",
"type": ["Profile"],
"name": "Example University",
},
"name": "Algebra I Completion",
"validFrom": "2024-06-15T00:00:00Z",
"credentialSubject": {
"type": ["AchievementSubject"],
"achievement": {
"id": "https://example.edu/achievements/algebra-1",
"type": ["Achievement"],
"name": "Algebra I",
"criteria": {"narrative": "Completed all units with 80%+ mastery"},
"achievementType": "Competency",
},
},
},
],
},
})
| Method | Returns | Description |
|---|---|---|
upsert() | ClrCredential | Create or update a credential; returns signed result |
Credential Structure
A CLR credential follows the W3C Verifiable Credentials v2 model:| Field | Type | Description |
|---|---|---|
@context | string[] | JSON-LD context URIs |
id | string | Unique credential identifier (URN/URI) |
type | string[] | Must include VerifiableCredential |
issuer | ClrProfile | Issuing organization profile |
name | string | Human-readable credential name |
validFrom | string | ISO 8601 date when credential is valid |
credentialSubject | ClrCredentialSubject | Subject with nested achievements |
Discovery
Retrieve API capabilities and the OpenAPI specification.const discovery = await client.discovery.get()
discovery = await client.discovery.get()
| Method | Returns | Description |
|---|---|---|
get() | DiscoveryResponse | OpenAPI 3.0 spec for the CLR v2 API |
The CLR v2.0 spec declares the discovery endpoint as public, but the BeyondAI implementation
requires OAuth authentication. The client handles this transparently.
Standalone vs Composed
The client works standalone or composed into@timeback/core:
// Standalone
import { ClrClient } from '@timeback/clr'
// Composed
import { TimebackClient } from '@timeback/core'
const client = new ClrClient({ env: 'staging', auth })
const timeback = new TimebackClient({ env: 'staging', auth })
timeback.clr.credentials.upsert(clrCredential)
# Standalone
from timeback_clr import ClrClient
client = ClrClient(env="staging", client_id=client_id, client_secret=client_secret)
# Composed
from timeback_core import TimebackClient
timeback = TimebackClient(env="staging", client_id=client_id, client_secret=client_secret)
await timeback.clr.credentials.upsert(clr_credential)
Error Handling
import { ClrError } from '@timeback/clr/errors'
try {
await client.credentials.upsert(invalidCredential)
} catch (error) {
if (error instanceof ClrError) {
console.log(error.statusCode)
console.log(error.message)
}
}
from timeback_clr import ClrError
try:
await client.credentials.upsert(invalid_credential)
except ClrError as error:
print(error)
Configuration
new ClrClient({
// Environment mode (Timeback APIs)
env: 'staging' | 'production',
auth: {
clientId: string,
clientSecret: string,
},
// OR Explicit mode (custom API)
baseUrl: string,
auth: {
clientId: string,
clientSecret: string,
authUrl: string,
},
// OR Provider mode (shared auth across clients)
provider: TimebackProvider,
// Optional
timeout?: number, // Request timeout in ms (default: 30000)
})
# Environment mode (Timeback APIs)
client = ClrClient(
env="staging", # or "production"
client_id="...",
client_secret="...",
)
# Explicit mode (custom API)
client = ClrClient(
base_url="https://custom.example.com",
auth_url="https://auth.example.com/oauth2/token",
client_id="...",
client_secret="...",
)
# Provider mode (shared auth)
from timeback_common import TimebackProvider
provider = TimebackProvider(env="staging", client_id="...", client_secret="...")
client = ClrClient(provider=provider)
# Optional
# timeout: float = 30.0 (request timeout in seconds)
Next Steps
CASE
Competencies and standards
Caliper
Learning event tracking
Types
CLR type definitions