Skip to main content

Developer guide

This is the evergreen, generic getting-started for the TimeBack Partner API: authentication, base URLs, and the end-to-end integration flow.

Your credentials live in your bundle, not here

This public guide contains no secrets. Your real clientId / clientSecret, edApp ID, scopes, and SSO config are in the provisioning bundle you download from the Partner Portal. The bundle also ships a partner-specific, agent-ready gettingStartedMarkdown that references those values by their exact JSON field names. Hand the bundle to your engineers (or your coding agent); treat it like an API key.

Base URLs

EnvironmentAPI base URLDocs
Productionhttps://api.alpha-1edtech.aihttps://docs.alpha-1edtech.ai

The Partner API is production-only for partners. Every example below uses the public CloudFront origin https://api.alpha-1edtech.ai — there is no /prod path prefix. CloudFront routes /auth/1.0/*, /rostering/1.0/*, /events/1.0/*, etc. to the ext-dev-api gateway, so you always hit the public domain, never a raw gateway URL.

Authenticate

Exchange your client credentials for a Bearer access token at the public proxy token endpoint:

POST https://api.alpha-1edtech.ai/auth/1.0/token

Two authentication methods are supported — pick one:

  • HTTP Basic auth: send Authorization: Basic <base64(client_id:client_secret)> plus grant_type=client_credentials in the form body.
  • Request body: send client_id and client_secret in the body (application/json or application/x-www-form-urlencoded).

Basic-auth example

curl -X POST https://api.alpha-1edtech.ai/auth/1.0/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic <BASE64_CLIENT_ID_COLON_SECRET>" \
-d "grant_type=client_credentials"

Request-body example

curl -X POST https://api.alpha-1edtech.ai/auth/1.0/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"

The response includes an access_token (a JWT, token_type: "Bearer") valid for 1 hour (expires_in: 3600). Cache and reuse it; re-request when it expires. Send it as Authorization: Bearer <access_token> on every other endpoint.

Keep your client secret safe

The client secret is provided once, in your bundle. Store it securely. If lost, re-provision to generate a new secret (the old one is revoked).

Your edApp identity

Your bundle contains an edApp.id URL of the form https://api.alpha-1edtech.ai/applications/1.0/{YOUR_APP_SOURCED_ID}. Use it as actor.id and edApp.id in all Caliper events you send.

The integration flow

A minimal happy path, top to bottom. During integration testing, use partner-sandbox as your org.sourcedId and always set metadata.isTestUser: true on test users so they're excluded from production reporting.

  1. Get a tokenPOST /auth/1.0/token (above).
  2. Create a test studentPUT /rostering/1.0/users/{sourcedId}.
  3. Create a coursePUT /rostering/1.0/courses/{sourcedId}.
  4. Create a classPUT /rostering/1.0/classes/{sourcedId}.
  5. Enroll the studentPUT /rostering/1.0/enrollments/{sourcedId}.
  6. Send your first eventPOST /events/1.0 (a GradeEvent reporting XP).
  7. VerifyGET /xp/1.0/users/{sourcedId}/entries after the materialization window.

Full request/response shapes and curl examples for every endpoint are in the API reference. The rostering writes (users, classes, enrollments) are testing-only scaffolding — in production, Alpha owns them.

Step 2: Create a test student

Use the OneRoster v1.2 User schema. Generate a UUID for sourcedId — the same ID is embedded in Caliper event URLs.

STUDENT_ID=$(uuidgen) # or crypto.randomUUID() in JS

curl -X PUT https://api.alpha-1edtech.ai/rostering/1.0/users/$STUDENT_ID \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"user": {
"sourcedId": "'$STUDENT_ID'",
"dateLastModified": "2026-01-01T00:00:00.000Z",
"enabledUser": "true",
"givenName": "Test",
"familyName": "Student",
"email": "test-student@yourapp.com",
"roles": [{
"role": "student",
"roleType": "primary",
"org": { "sourcedId": "partner-sandbox" }
}],
"metadata": { "isTestUser": true }
}
}'
Always set isTestUser on test accounts

Set metadata.isTestUser: true on every student you create while testing. This flag excludes the student from all production reporting, analytics, dashboards, XP leaderboards, and mastery rollups. There is no server-side mechanism that sets it for you — omit it and your test activity contaminates real dashboards. For real students on a live deployment, omit the flag (or set it to false).

Step 6: Send your first event

A GradeEvent reports XP earned. extensions.course.id is required and must resolve to a course you own.

curl -X POST https://api.alpha-1edtech.ai/events/1.0 \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "urn:uuid:YOUR_UNIQUE_EVENT_ID",
"type": "GradeEvent",
"profile": "GradingProfile",
"action": "Graded",
"eventTime": "2026-01-01T12:00:00.000Z",
"actor": { "id": "https://api.alpha-1edtech.ai/applications/1.0/YOUR_APP_SOURCED_ID", "type": "SoftwareApplication" },
"edApp": { "id": "https://api.alpha-1edtech.ai/applications/1.0/YOUR_APP_SOURCED_ID", "type": "SoftwareApplication" },
"object": {
"id": "https://yourapp.com/activities/example",
"type": "Attempt",
"count": 1,
"assignee": { "id": "https://api.alpha-1edtech.ai/rostering/1.0/users/YOUR_STUDENT_ID", "type": "Person" },
"assignable": { "id": "https://yourapp.com/resources/lesson-1", "type": "DigitalResource", "mediaType": "application/json" },
"extensions": { "activityName": "Example Lesson" }
},
"generated": {
"id": "https://yourapp.com/scores/example",
"type": "Score",
"scoreGiven": 100,
"maxScore": 100,
"extensions": { "scoreType": "XP" }
},
"extensions": {
"subject": "Math",
"course": { "id": "https://api.alpha-1edtech.ai/rostering/1.0/courses/YOUR_COURSE_ID" }
}
}'

For the full event reference, including AssessmentEvent and heartbeats, see API reference → Events.

Scopes

Every partner receives the same scope set on day one. These authorize the surfaces you'll use:

ScopeAuthorizes
events.writePOST /events/1.0 (send Caliper events)
events.readonlyGET on /analytics/1.0/*, /xp/1.0/*, /insights/1.0/* (read processed event data). There is no raw event read-back endpoint; use XP/analytics to verify events were processed.
roster.readonlyGET on /rostering/1.0/* (read users, courses, classes, enrollments, orgs)
roster.createputPUT on /rostering/1.0/{entity}/{sourcedId} (create/update roster data)
gradebook.readonlyGET on /gradebook/1.0/* (read assessment results and line items)
standards.readGET on /standards/1.0/* (read frameworks and objectives)

There is no dedicated analytics.readonly scope today; events.readonly authorizes everything under /analytics/1.0/*, /xp/1.0/*, and /insights/1.0/*.

Event processing requirements

After you POST an event:

  1. Validated — schema checked; 400 if invalid.
  2. Stored — written to the Caliper activity trail (Parquet/S3).
  3. Processed — resolved to student, app, course → analytics tables (30–120 seconds).
  4. Available — visible in dashboards, XP leaderboards, coaching insights.

On every GradeEvent / AssessmentEvent / AssessmentItemEvent:

  1. The student (actor or object.assignee) must exist — PUT /rostering/1.0/users/{id}.
  2. extensions.subject is required (enum: Math, Reading, Language, etc.).
  3. extensions.course.id is required and must resolve to a course you own. Events pinning a course you don't own are rejected after acceptance — the 202 response stands but no ALI / AR / processed_fact rows are written; logs capture the rejected payload.

Attribution: if the student has an active enrollment in the pinned course, the resulting processed_fact row carries its enrollment_id. If not, the fact lands with enrollment_id = null — activity is still recorded and attributed to the course, but only rolls up into enrollment-scoped analytics once the student is enrolled.

Events are send-only. POST /events/1.0 is the only event-write entry point; there is no read-back endpoint at /events/*. To verify an event was processed, query the matching analytics / XP endpoint after the 30–120 s window. See API reference → Analytics.

Filtering list endpoints

All collection endpoints (GET without a sourcedId) support OneRoster filtering via the filter query parameter. Custom query parameters like ?userId= are not supported.

# Filter assessment results for a specific student
GET /gradebook/1.0/assessment-results?filter=student.sourcedId='STUDENT_ID'

# Filter courses by subject
GET /rostering/1.0/courses?filter=subjects='Math'

# Combine filters with AND
GET /rostering/1.0/enrollments?filter=role='student' AND class.sourcedId='CLASS_ID'

Supported operators: =, !=, >, >=, <, <=, ~ (LIKE), @ (IN). Logical: AND, OR. Other query params: limit, offset, sort, orderBy (asc/desc), fields (comma-separated selection). List endpoints are automatically scoped to resources owned by your client application — you don't need to send any ownership filter.

Next steps

  • Browse the full API reference — one page per functional group.
  • Read the Product overview for the conceptual model (XP, ownership, the integration shape).
  • Download your provisioning bundle and hand its gettingStartedMarkdown to your coding agent.