Skip to content
Woolf Help Center home
Woolf Help Center home

Woolf API v0 is retiring on October 15, 2026 — how to move to v1

Woolf API v0 is retiring on October 15, 2026

We are switching off version 0 of the Woolf partner API on October 15, 2026. After that date, any request your systems send to the old v0 endpoints will fail, and anything that depends on those requests — enrolling students, sending grades, syncing submissions — will stop working.

This guide walks you through the migration one step at a time. Most integrations can complete it in a few days of engineering work. If anything here is unclear, contact your Woolf partner manager or support@woolf.university — we're happy to review your migration plan or get on a call with your team.

How do I know if this affects me?

Look at the URL your server sends Woolf API requests to:

You are calling...VersionAffected?
https://api.woolf.university/ (nothing after the domain)v0Yes — you must migrate
https://api.woolf.university/publicv0Yes — you must migrate
https://api.woolf.university/v1v1No — you're already done

The in-browser Woolf SDK is not affected. If your LMS pages load the Woolf SDK (@woolfuniversity/sdk) or call the tracking endpoints (/track/consumption, /track/submission, etc.), nothing changes there. This deprecation only covers the GraphQL API your server calls.

Why v1 is worth the move

  1. v0 stops working on October 15, 2026. After the deadline, v0 requests are rejected. Migrating early means you find and fix issues on your schedule, not during an outage.
  2. v1 can create submissions directly from your server. In v0, submitAssignment could only update a submission that already existed. In v1, addSubmission creates the record too — one call, no pre-existing activity needed.
  3. Everything works with one token. v0 forced you to mint and refresh per-student OAuth tokens (authorizeOAuth, refresh) for some operations. In v1, your server does everything with your college API token.
  4. Better reporting, fewer calls. v1's studentDegreeProgress returns a student's complete degree progress (workload, grades, per-course breakdown) in a single query — data that took multiple v0 queries to assemble.
  5. Bulk operations with job tracking. v1 accepts bulk uploads and returns a jobId you can check with jobDetails, instead of you looping over hundreds of single calls.

Part 1 — Must-do changes

Work through these in order. Each section says exactly what to find in your code and what to replace it with.

Step 1: Change the URL

Find where your code sends requests to the Woolf API and add /v1 to the path.

Before: POST https://api.woolf.university/ (or /public) After: POST https://api.woolf.university/v1

Everything else about how you call the API stays the same: it is still GraphQL, still POST, still Content-Type: application/json, and still the same Authorization: Bearer <your college token> header. Your API token does not change.

Step 2: Replace each mutation you use

Find every GraphQL mutation name your code sends, look it up in this table, and replace it with the v1 version. Every v0 mutation is listed — if you use it, it's here.

If your code calls (v0)...Replace it with (v1)...What else changes
addDegreeStudentaddStudentToDegreenameFirst, nameLast, externalId move inside a student: {...} object; price is now tuitionCost (see example A)
addStudentsToCollegeaddStudentToCollegev1 takes one student per call — loop over your list
createResource, apiCreateResourceaddResourceResponse is now { resource { ... } } instead of the resource directly
modifyResource, apiModifyResourcemodifyResourceSame name, response wrapped the same way as addResource
deleteResource— (remove the call)This v0 mutation performs no action and always returns false. You can safely delete the call from your code
archiveResources, apiArchiveResources, unarchiveResourcesNot yet available in v1If your integration archives or unarchives resources, contact support@woolf.university before you migrate and we will work out a path with you
addGradeaddGradeArguments move inside a grade: {...} object; score is renamed value (see example B)
teacherAddGradeaddGradeSame as above; pass the teacher via teacherId
addFeedbackaddFeedbackArguments move inside a feedback: {...} object; descr is renamed content
teacherAddFeedbackaddFeedbackSame as above; pass the teacher via teacherId
submitAssignment, studentSubmitAssignment, apiStudentSubmitAssignmentaddSubmissionArguments move inside a submission: {...} object; descr is renamed content; college token instead of student token (see example C)
addEvidenceAttach evidence via the evidenceAssets field on addSubmission / addGrade / addFeedback instead
archiveGradearchiveActivityPass the activity's ID instead of (resource, student, weight)
importAsset, removeAssetAssets are now passed via the assets field on addResource / modifyResource
submitStudentsForEvaluationsubmitStudentsForEvaluationSame name; arguments move inside an evaluation: {...} object
authorizeOAuth, refreshgenerateUserTokenOne call with your college token; no refresh flow needed
addWebhook, changeWebhook, removeWebhookSame names, same argumentsNo change needed

Example A — enrolling a student

# v0 — old mutation { addDegreeStudent( email: "student@example.com" degreeId: "degree-uuid" externalId: "your-internal-id-123" nameFirst: "Priya" nameLast: "Sharma" price: 400000 ) { id } } # v1 — new mutation { addStudentToDegree( email: "student@example.com" degreeId: "degree-uuid" tuitionCost: 400000 student: { externalId: "your-internal-id-123" nameFirst: "Priya" nameLast: "Sharma" } ) { id } }

Example B — sending a grade

# v0 — old # note: "score" mutation { addGrade( resourceId: "resource-uuid" studentId: "student-uuid" score: 85.0 weightId: "weight-uuid" ) { id } } # v1 — new # note: "value", wrapped in "grade" mutation { addGrade(grade: { resourceId: "resource-uuid" studentId: "student-uuid" value: 85.0 weightId: "weight-uuid" }) { activityId } }

Example C — submitting an assignment

# v0 — old: required an existing activityId and used "descr". # Failed if the activity didn't exist yet. mutation { submitAssignment( activityId: "activity-uuid" resourceId: "resource-uuid" descr: "<p>The student's submission text</p>" ) { id } } # v1 — new: "content" instead of "descr", wrapped in "submission". mutation { addSubmission(submission: { activityId: "activity-uuid" # include to UPDATE an existing submission resourceId: "resource-uuid" studentId: "student-uuid" content: "<p>The student's submission text</p>" }) { activityId } }

One behavior change to be aware of: in v1, if you leave out activityId, addSubmission creates a new submission rather than failing. If your code relied on the v0 behavior of "error when the activity doesn't exist," always pass activityId when you intend an update.

Step 3: Replace each query you use

If your code calls (v0)...Replace it with (v1)...Notes
listCourses(degreeId)courses(degreeId, pagination)Results are now paginated: read them from the list field, total from count
showResource(id)resource(id)
showStudentDegreeProgress(degreeId, studentIds)studentDegreeProgress(id, degreeId)One call per student (id = student email, your external ID, or Woolf UUID); returns far more detail than v0 did
showStudentCourseProgress(courseId, studentIds)activities(courseId, studentId, ...)Also see the courseStudents roster query below — usually a better fit
showStudentResourceProgress(resourceIds, studentId)activities(resourceId, studentId, ...)

Step 4: Update code that mints student tokens

If you generate per-student tokens (for example, to initialize the Woolf SDK on your pages), replace the v0 OAuth flow:

# v0 — old: two mutations plus refresh handling mutation { authorizeOAuth(userId: "user-uuid") { accessToken } } mutation { refresh(token: "old-token") { accessToken } } # v1 — new: one call with your college token mutation { generateUserToken(id: "student-uuid") }

Tokens still expire — call generateUserToken again when you need a fresh one, instead of running a refresh flow.

Step 5: Check your webhook handler

The webhook mutations (addWebhook, changeWebhook, removeWebhook) are unchanged, and your existing webhook subscriptions keep working. However, v1 webhook payloads are simplified: they contain deliveryId, signature, event, and data. If your handler reads any other fields from the payload (e.g. delivery metadata), update it to use only those four.

Step 6: Test before you switch production

  1. Ask your Woolf contact for sandbox credentials (https://api.x.university/v1) if you don't have them.
  2. Note that schema introspection is disabled on production endpoints — autocomplete against the live API won't work. Ask us for the current v1 schema file (SDL), or explore via GraphiQL at {base}/v1/graphiql in the sandbox.
  3. Run your integration's main flows end-to-end in the sandbox: enroll a test student, send a grade, send a submission, receive a webhook.
  4. Switch production traffic, then watch your logs for GraphQL errors (errors[].extensions.code) for the first few days. Remember responses can be HTTP 200 and still contain errors — always check the errors array.

Part 2 — Nice-to-haves (optional, but they'll simplify your code)

None of these are required to meet the deadline. They're new v1 capabilities that replace things partners used to build themselves.

  • studentDegreeProgress — one query returns a student's full degree snapshot: total workload done vs. required, GPA fields, and per-tier/per-course breakdowns. If you currently stitch this together from several queries, this replaces all of them.
  • courseStudents roster query — returns every (student, course) enrollment for your college, with filters. The killer combination is readyForSubmission: true + isActiveDegreeStudent: true, which returns exactly the students who have cleared every gate (workload, assignments, meetings, grade threshold) — the same logic Woolf uses internally, so your numbers always match ours.
  • Bulk uploads with jobDetailsbulkUploadActivity returns a jobId; poll jobDetails(jobId) to see when it finishes. Much faster than looping single calls, and rate-limit friendly.
  • Look students up by your own IDs — anywhere v1 takes a student id string, you can pass the externalId you set at enrollment (or the student's email) instead of storing Woolf's UUIDs.
  • A second API key for background jobs — if you run nightly syncs and real-time calls on the same key you can hit rate limits (HTTP 429). Ask us for a separate key for your batch traffic.

Timeline

DateWhat happens
Nowv1 is live; sandbox available for migration testing
October 15, 2026v0 endpoints (/ and /public) are switched off; unmigrated integrations stop working

Questions? Contact your Woolf partner manager or support@woolf.university.