CareerOS Partners API

Changelog

v1 only ever adds — columns, resources, operators. We never remove or repurpose a shipped field within v1; breaking changes would ship as a parallel v2 with ≥6 months of overlap and advance email notice to every token holder.

2026-09-12

A student is identity plus a cohort

  • cohort_name moves onto the student, and the programs array is gone. Placement used to be buried inside programs[].cohort_name; it is now a plain field on the student, exactly like the column in the roster file your advisors upload. graduation_date moves with it. Everything else the array carried — per-programme name, status, course year — is no longer stored, so send a student as identity, cohort and flags and nothing more.
  • POST and PUT now require a complete object. A cohort needs name, display_name, program, degree, start_date, end_date, intake_year and intake_term; a student needs email, first_name, last_name, cohort_name and is_alumni. These are precisely the columns the advisor roster template makes mandatory — only campus and study mode are optional there, and the same two are optional here. A half-populated cohort is invisible to the filters advisors work from, which is why it is no longer accepted.
  • A student write no longer creates cohorts. Naming a cohort that does not exist is refused with COHORT_NOT_FOUND instead of quietly creating an empty one, because a student write carries no degree, dates or intake to build it from. Push /v1/cohorts first — which the guide has always recommended and the API now requires.
  • PATCH is unchanged in spirit and more useful in practice. It never creates, so everything except the identity may be omitted, and an omitted field is left exactly as it is rather than cleared. Send email plus only what you want to change: correct a name, add a phone number, set is_alumni, or move someone to a different cohort with cohort_name. To move students into a cohort that does not exist yet, create it with POST /v1/cohorts and then patch them. A cohort's name remains frozen — every other cohort field is editable.
  • The API reference now has separate create and update schemas — CohortWrite / CohortPatch and StudentWrite / StudentPatch — so it states exactly which fields a create requires rather than showing one schema for every verb.

2026-09-11

Every student must be placed in a cohort

  • A student write whose programmes carry no cohort_name is now refused with MISSING_COHORT, and nothing is written for that row. Until now such a student was created with cohort_id: null and reported as a plain success — an account on no advisor's roster, inheriting no résumé templates and receiving nothing targeted at their year. Only the programme the student should be placed in needs a name; the others are recorded as history either way. The most common cause is a source row with no graduation year, since a cohort name is usually built from it.
  • When several programmes are sent, placement now ranks only the programmes that carry a cohort_name. Previously an in-progress programme without one would win the ranking and leave the student unplaced even though another programme named a cohort; now the student is placed in that cohort instead. The programme placement picks is also the one whose graduation_date is stored, so the date and the cohort always come from the same programme.

2026-09-07

Write students into CareerOS

  • /v1/students now accepts POST (create), PUT (idempotent upsert) and PATCH (update). Each takes a batch of up to 500 students and returns per-row results, so one refused student never blocks the rest of a sync. Writes require a token CareerOS has enabled for writing, scoped to a single university.
  • Address students by email. It is unique across CareerOS, so a nightly sync is idempotent without you storing an identifier of ours. Send external_id as well if you have a durable student number: it then takes precedence when matching, so a student whose address changes stays one account instead of becoming two.
  • Cohorts are writable too: POST, PUT and PATCH /v1/cohorts manage display name, programme, degree, campus, intake year and term, dates, study mode and résumé templates — the same fields your advisors edit in the app, so you never need to send a roster spreadsheet. A cohort is addressed by name within your university and name is frozen once created. Omitting a field leaves it untouched; sending null clears it. Assigning a student to a cohort is part of the student write: send cohort_name on the programme.
  • Academic context lives on the cohort, not on every student: set degree once on the cohort and students placed there inherit it, and expected completion is the cohort's end_date plus graduation_year on the programme. A student's own graduation_date means they have actually graduated.
  • A created student is an invited account: no login identity until the person first signs in, and no email is sent. invitations_sent is always 0 — your advisors decide when a student is contacted. Requests are student-grain, so an extract with one row per course per term must be grouped first; send the programmes in a programs array.
  • New writable fields beyond name and graduation date: contact phone (E.164 with country code — a local number is refused rather than guessed), and per-programme program, course_year, status and cohort placement. Send graduation_date only once a person has genuinely graduated: a past date is what makes an account an alumni account.
  • is_alumni is writable. CareerOS normally derives it from graduation_date, but only when the student next signs in or their cohort or graduation date changes — there is no nightly job, so a historical roster you push would read is_alumni: false until each person logged in. Send is_alumni on the student to set it straight away. Omitting the field is not the same as sending false: omitting leaves the derivation alone. The derivation still runs later and remains the authority, so a value that disagrees with graduation_date is overwritten the next time that student's cohort or graduation date changes — when it disagrees, we say so in that row's notes rather than let you assume it stuck.
  • Cohort placement picks the programme in progress first, then one not yet started, then a finished one — a future exchange never displaces the programme a student is in today. Naming a cohort_name that does not exist creates it with the programme metadata you sent.
  • PATCH /v1/students?id=eq. is unchanged. The original single-row form still takes a flat body of first_name / last_name / graduation_date and still answers 204. If you already integrate against it, nothing about your calls needs to change; the batch form is what you get when you omit the id filter.
  • Not stored on a student: dates of birth and gender (we hold no demographics), and degree, specialization and anticipated_completion_datedegree belongs on the cohort, and expected completion is the cohort's end date. Courses and grades are not stored yet; there is no transcript model today. Send any of them anyway and they are dropped, with the field names echoed back in dropped_fields so you can see what was ignored rather than assume it was saved.
  • DELETE returns 405. Erasure runs through a staged deletion flow with a grace window and its own audit trail, so it is not something an API token can start — email support@thecareeros.com.
  • An email that already belongs to an account at another university, to a non-student account, or to an existing CareerOS user with no university (self-serve) is refused rather than adopted — and so is a write addressed by external_id whose student has since moved to another university or become staff. updated_at on /v1/students now moves whenever a student changes, so incremental syncs that page on it no longer miss edits.
  • Contact email stays create-only. Pushing a student by external_id with a new address records it as a secondary address and tells you so in that row's notes; it does not become a login or the contact address, because that needs the student to verify it. Email support@thecareeros.com to complete a domain migration.
  • Two things that used to pass silently now come back as rejections: a cohort_name that matches more than one of your cohorts (COHORT_NAME_AMBIGUOUS, so we never guess which), and a résumé-template name that matches none of yours (UNKNOWN_TEMPLATE).

2026-08-12

Event participants and advisor appointments

  • New resource: /v1/appointments — every booking of your advisors' CalendarOS appointment types, across all advisors: advisor, student, appointment type, timing, status (requested / confirmed / rejected / no_show / cancelled), and location. Full history — past and upcoming, all statuses, including bookings of appointment types that were since deleted. Free-text booking notes and booking-form answers are not exposed.
  • New resource: /v1/event_participants — one row per student per event: who registered for each event and who actually attended. Filter by event with ?event_id=eq.; each row carries the student's identity, cohort, raw status / attendance_status, and derived is_registered / has_attended / is_waitlisted booleans that sum exactly to the counts on /v1/events.
  • Cancelled registrations are included with is_registered=false, so sign-up drop-off is measurable. attendance_status=pending means attendance was never marked — check the event's attendance_tracked before reading absence into it.

2026-08-11

Test accounts removed; deactivated staff and students retained

  • /v1/events and /v1/alumni no longer return records that originate from CareerOS internal test accounts. Expect a small drop in row counts — these were never real events or real alumni, so any figure they affected was overstated.
  • /v1/advisor_students: the assigned_advisor_* columns now include advisors who have since been suspended or moved to another role. Previously an advisor disappeared from these columns the moment their account was deactivated, which made some students appear to have no advisor at all and undercounted assigned_advisor_count for others. Historic advising relationships are facts and now stay reported.
  • /v1/events: registrations, attended and waitlisted now include students who have since been deactivated or have left their cohort. A student who attended an event keeps that attendance in your numbers. Counts can only go up.
  • No columns were added, removed or renamed, and no filters changed. If you snapshot these resources, expect a one-off step in the affected counts rather than a gradual shift.

2026-08-10

Analytics resources: engagement, companies, events

  • New resource: /v1/advisor_students — the student roster with advising context: application status, profile_completeness, onboarding_updated_at, cohort label, and assigned advisors. Same students as /v1/students, more columns.
  • New resource: /v1/student_engagement — per-student totals for companies saved, contacts saved, outreach sent, replies received, applications submitted, interviews obtained, referrals and coffee chats. Students with no activity are included with zeros so denominators stay correct.
  • New resource: /v1/student_engagement_monthly — the same metrics at one row per student per active month, for time-series reporting.
  • New resource: /v1/companies — companies your students saved or applied to, with save counts and an application-funnel breakdown, one row per company per university.
  • New resource: /v1/events — events your university organises, with registration and attendance counts. Use attendance_tracked to exclude events where attendance was never recorded before computing rates.
  • cohort_name now means the same thing everywhere. /v1/students and /v1/resumes returned a cohort's internal name; they now return its display label — the display_name your advisors set, falling back to name when there isn't one. That is what the app shows and what the new resources above already returned. cohort_id is unchanged, so anything keyed on the id is unaffected.
  • All five use the metric definitions of the equivalent CareerOS advisor screens. Two known differences from the in-app figures are documented per resource: ghost and deleted accounts are excluded here, and student_engagement.company_saved counts the auto-assigned CareerOS company while /v1/companies excludes it.

2026-07-23

Resume source files move to private storage

  • /v1/resumes: source_url now always returns null. Student-uploaded source PDFs moved to private storage as part of a platform-wide PII review; the raw storage URL is no longer usable. The column stays in the response shape. It was populated on almost no rows — reach out if your integration relied on it.

2026-07-15

Profiles, onboarding, alumni — and a cleaner resumes shape

  • New resource: /v1/student_profiles — structured career profiles (summary + sections), respecting student visibility settings.
  • New resource: /v1/onboarding — each student's latest onboarding questionnaire; sensitive demographic answers are removed server-side.
  • New resource: /v1/alumni — the alumni directory with current position where known.
  • /v1/students gained cohort_id and cohort_name columns.
  • /v1/resumes: removed the legacy body and preview_url columns — content is the single canonical resume payload.

2026-07-14

Jobs

  • New resource: /v1/jobs — employer-posted, advisor-curated, and aggregated postings from the last 365 days, with university_ids / cohort_ids / all_cohorts visibility columns.

2026-05-19

Launch

  • Partners API v1 goes live: /v1/universities, /v1/students (GET + PATCH), /v1/cohorts, /v1/campuses, /v1/resumes.
  • Consortium tokens: one token can span every member university, resolved at request time.