ConcordiaTracker DOCS
Open app →

Contact support

Tell us what is going wrong. We typically reply within the hour, by email. No account needed.

Already have a case number? Check its status.

Developers

API reference

ConcordiaTracker exposes a small HTTP API. Most of it backs the web app and needs a signed-in user, but one endpoint is open to anyone and is the useful one for an automated client: live section, meeting-time, and seat data for any Concordia course.

The machine-readable description lives at /openapi.json. It is OpenAPI 3.1, and every operation has a unique operationId, typed parameters, and a response schema, so it converts straight into tool definitions for an LLM function-calling runtime.

Base URL

Every endpoint is under https://concordiatracker.com/api/. Every response, including every error, is JSON.

Course sections (no authentication)

GET /api/sections?subject=COMP&catalog=248 returns every published section of that course for the terms Concordia currently lists, newest first.

Each section carries classNumber (the value Concordia’s Student Centre asks for when you enrol), termCode, section, component, meetingTimes, building, room, instructionMode, and live enrolled, capacity, waitlisted, and waitlistCap counts. hasReserved is true when some seats are held for particular programmes, which is why an apparently open section can still refuse you.

Authentication

The web app authenticates with a Supabase access token, which expires in an hour and is minted by a browser sign-in. That is no use to a script, a cron job, or an agent, so anything unattended uses an API token instead: long-lived, named, revocable one at a time, and scoped narrower than a session.

Send it the same way: Authorization: Bearer <token>. There are four scopes and a token can never widen its own.

An admin token can publish content on behalf of any organisation, and every write it makes is recorded in the audit log, marked as made on the club’s behalf when the account is not on that club’s team. What it cannot do is enforced by the database rather than by the endpoint: it cannot add itself to an existing club’s team, and deleting an organisation or removing a teammate is not reachable through the API at all.

A token is shown once, when it is created, and cannot be retrieved afterwards. Only its SHA-256 hash is stored, so a database dump is not a set of live credentials. If you lose one, the answer is to revoke it and make another. Revoking takes effect immediately.

Each token is limited to 120 requests a minute. Going over returns rate_limited with a Retry-After header, which is a different answer from unauthorized: a client that cannot tell them apart will either retry forever or give up on a token that is perfectly good.

Owner API: how the business is doing

Four read-only endpoints, for a dashboard or an agent. Every response carries generated_at, timezone: "UTC", and a notes array naming anything that makes the figures less than complete: Stripe paging, test mode, a missing key, or how far back visitor tracking actually goes.

The definitions are fixed and worth knowing, because the same words mean different things in different dashboards. A paying customer has been charged more than $0 and the charge settled. A trial is not a paying customer. A comped account is counted as a user and never as paying. Internal and test accounts are excluded from every figure except the one that counts them. MRR comes only from subscriptions that have actually been charged, and ARR is labelled an estimate because it is one month multiplied by twelve, not a year of observed revenue.

/owner/users returns counts and never identities: no names, no emails, no user ids. A long-lived token sitting in a cron job is a looser credential than a session, so the worst a leaked one can do is reveal how the business is doing, not export the user table.

Amounts are in cents, so mrr_cents: 375 is $3.75. Timestamps are ISO-8601 in UTC, always. A dashboard in another timezone silently shifting a day is the classic wrong answer nobody notices.

Personal API: your own courses and grades

A PATCH body takes any of status, notes, and grade. A grade is either {"percent": 87} or {"earned": 17, "total": 20}, and null clears it. The response returns the assessment as it now stands, so nothing has to guess whether the write landed.

Those three fields are the whole write surface, deliberately. A token cannot change a weight, move a date, or set provenance: a weight edited by a script is a grade computed from a number nobody checked, and provenance is a claim about where a date came from that a script cannot honestly make.

Two things the responses are careful about. An assessment with no date returns due: null rather than today. An outline that says the Examinations Office will set the date has not given us one, and inventing it would put a made-up deadline on the highest-stakes item you have. And /me/gpa averages over the weight graded so far, returning that denominator as graded_weight, so a term two assessments in is not reported as though the ungraded 80% were zeros.

Errors

Every failure returns the same shape, so one parser handles all of them: error (the human message, kept for older clients), code, message, hint, status, and docs.

code is stable and safe to branch on. The values are bad_request, unauthorized, forbidden, not_found, method_not_allowed, conflict, rate_limited, not_configured, upstream_error, and internal_error.

An unknown path under /api/ returns a JSON not_found, never an HTML error page. An unknown path anywhere else on the site returns a real HTTP 404 with a short body pointing at the sitemap, llms.txt, and this reference.

Markdown content negotiation

The homepage, every documentation page, and the About, Contact, and Developers pages are available as markdown. Send Accept: text/markdown and you get markdown back, with Vary: Accept set so a shared cache cannot hand you the wrong variant.

Rate limits and etiquette

There is no published quota on the sections endpoint, but it proxies Concordia’s own directory: cache what you fetch, do not poll in a tight loop, and identify your client with a User-Agent. Ticket creation is rate limited per IP address. If you are building something that needs more than casual use, get in touch first.

For AI agents

Start from llms.txt. It carries a "when to use this" section naming the questions this site can answer well (course content, prerequisites, section times and seats, Concordia’s GPA scale, tuition rates, registrar deadlines) and states plainly what it cannot answer, namely anything about an individual student’s private record.