Skip to main content

ERP Partner Integration

This section is for ERP and SIS providers embedding TimetableMaster inside their own product.

Your users never see a TimetableMaster login, bill, or brand. They stay in your product, on your domain, and open a Timetable area that happens to be powered by us.

The three things you build

#WhatDirectionWhy
1LaunchYou → usSign a user in from your ERP, with no login screen
2Entity syncYou → usKeep teachers, subjects, classes and rooms current
3Reading schedulesUs → youPull the generated timetable back into your product

Build them in that order. Launch is testable on its own with curl, and sync is the largest piece.

Base URL and authentication

All partner endpoints live under /api/partner/v1 and take your organisation's API key:

Authorization: Bearer ttm_<your api key>

These calls are server-to-server only. The API key must never reach a browser. It authorises everything in the organisation, including minting a session for any user. See Authentication.

We create one organisation per customer institution and issue you an API key for each. There is no self-service organisation creation.

Identifiers: the one thing to get right

Every entity you send us carries your identifier in externalId. We store it, treat it as the entity's identity for all future syncs, and return it on every entity in every read response.

That means you never build a mapping table. You send EMP-1001, and when you ask what teacher EMP-1001 is teaching, you get an answer in those terms.

Internally we also use a localId, an identifier scoped to a single timetable. Lesson units reference entities by localId, and the entity lists let you resolve localId → externalId. You only need localId to join a unit to an entity within one timetable response.

IdentifierWhoseScopeYou use it for
externalIdYoursYour systemAddressing entities, joining our data to your records
localIdOursOne timetableResolving references inside a timetable response

Response envelope

Every endpoint returns:

{
"success": true,
"data": { },
"timestamp": "2026-09-14T10:00:00.000Z"
}

Errors:

{
"success": false,
"error": { "code": "PARTNER_ACCESS_DISABLED", "message": "Partner access is not enabled for this organisation" },
"timestamp": "2026-09-14T10:00:00.000Z"
}

Always branch on error.code, never on the message text.

Common error codes

CodeStatusMeaning
PARTNER_ACCESS_DISABLED403The organisation is not enabled for partner access
SYNC_IN_PROGRESS409Another sync is running for this organisation and entity type
MASS_RETIRE_BLOCKED409A snapshot would retire too much. See Entity sync
INVALID_LAUNCH_TOKEN401The launch token was already used, expired, or never existed
UNKNOWN_ENTITY_TYPE400Not one of teachers, subjects, rooms, classes, students