Skip to navigation

9 October 2026

What changed in this week's release
View as Markdown

Three fields on the credit outcome have been renamed. If you have already built against uoc, assessedUoc or totalCreditUoc, those names are gone. See Credit points replace uoc below — it is a rename only, and nothing about the values has changed.

Everything else is additive: an applicant type you can set and read back, two new application statuses, two filters on the change feed, more detail on the credit outcome, a set of changes to supporting documents — including a fix for entries that went missing after a failed upload — and a list on each application of what it is waiting on.

This page also covers the early-October work, which had no release note of its own.

Institution API

Telling us whether an applicant is domestic or international

{ "applicantType": "INTERNATIONAL" }

Settable on POST /institution/applications and on PATCH /institution/applications/{id}, and returned by every endpoint that reports an application — including the outcome and the change feed.

Optional everywhere. “Not stated” is a real answer, and null clears it back to that, because an intake that recorded a type it should not have has to be able to take it back.

Checked against your own configured list rather than a fixed pair, so a value the management portal offers is a value this API accepts. Case is forgiven. An unknown value is a 400 naming what is on offer:

{
"status": 400,
"error": "'OVERSEAS' is not one of this institution's applicant_type values: DOMESTIC, INTERNATIONAL"
}

It belongs to the APPLICATION, not the applicant. The same person can be domestic for one application and international for the next — a visa granted in between, a citizenship that came through. A single value on the person would make their older application claim something untrue about the day it was lodged. There is no applicant-level equivalent, deliberately.

applicantType is the only field validated this way. stream and sourceName have configured lists too, but those drive the portal’s pickers — this API accepts any value for them, so a typo is stored rather than refused.

Credit points replace uoc

The outcome endpoint spoke in “UOC”, which is one institution’s word for it. The fields now say what they hold:

WasNowWhere
uoccreditPointseach credit
assessedUocassessedCreditPointseach credit
totalCreditUoctotalCreditPointsthe outcome

Nothing else about them changed — same values, same meaning, same rules about what counts. totalCreditPoints is still a plain sum of the credits below it, and assessedCreditPoints still appears only when an officer changed the number.

The rename reaches both GET /institution/applications/{id}/outcome and the outcome carried on /institution/applications/changes.

Two more application statuses

ON_HOLD and FINALISED join the five you already have:

StatusMeaning
ON_HOLDPaused, usually waiting on something outside Advance
FINALISEDYou are done with it

Two things worth knowing before you use them.

ON_HOLD does not pause an assessment. If one is running it carries on — the status is a signal to the people working on the application, exactly as READY is. Nothing in Advance stops on it.

FINALISED is not terminal. An application can still be withdrawn afterwards, which is what happens when an applicant pulls out after a decision. WITHDRAWN remains the only terminal status.

Setting a status, and saying why

POST /institution/applications/{id}/status
{ "status": "ON_HOLD", "reason": "awaiting transcript from Ngee Ann Polytechnic" }

Takes any status except WITHDRAWN, with an optional reason. The reason is kept on the application and on its status history, so “why was this held, and when” has an answer later.

Setting a status the application already has succeeds and changes nothing, so a retry is safe.

WITHDRAWN keeps its own endpoint because withdrawing also closes the assessment — setting the status alone would leave an assessment that still believes it is running. /institution/applications/{id}/ready also stays, and now accepts an optional reason as well. It still works with no body at all, so an existing integration needs no change.

Filtering the change feed by status

GET /institution/applications/changes?updatedSince=…&status=ON_HOLD&status=FAILED

Repeat the parameter, or send one comma-separated value. Omit it for every status. Case is forgiven.

The filter applies before paging, so a page is a full page of matches and hasMore means what it says.

An unrecognised status is a 400 naming the seven that exist, rather than an empty page. On a change feed an empty page means “nothing has changed”, so a typo would otherwise look like a working, quiet feed.

More on the credit outcome

The outcome now reports the application it belongs to, so storing outcomes no longer needs a second call to know what the credit was awarded against:

  • programCode, specialisationCodes, intakeCode, applicantType
  • applicantSources and applicationSources — your own references, kept apart, because an application number is not a student number
  • organisationName and organisationCode on each credit source — the institution the prior study was done at

The organisation is joined from the agreement behind the credit rather than stored with it, so it reflects that organisation’s current name. Both are null where there is no agreement: an exchange mapping, or credit an officer awarded outright.

All of it appears on the change feed too.

What an application is waiting on

ON_HOLD tells you an application is paused. It does not tell you what we are waiting for, and the reason beside it is written by an assessor for their own records — not something you can put in front of an applicant.

Each application now carries pendingActions: the individual things we need, written to be read by whoever has to supply them.

{
"status": "ON_HOLD",
"pendingActions": [
{
"id": "6f1c…",
"actionType": "DOCUMENT_REQUIRED",
"description": "Certified transcript for the Diploma of Business",
"state": "OPEN",
"qualificationId": "829787ff…",
"raisedAt": "2026-10-09T11:40:22+11:00",
"resolvedAt": null
}
]
}
Field
actionTypeDOCUMENT_REQUIRED, CLARIFICATION, VERIFICATION or OTHER
descriptionWhat is needed, in the assessor’s words
stateOPEN, RESOLVED, or CANCELLED if it turned out not to be needed
qualificationIdWhich qualification it is about, where it is about one in particular
resolvedBySourceinstitution_api if your integration closed it, officer if an assessor did

It appears on GET /institution/applications/{id} and on every item in the change feed.

To poll for just the applications that are waiting on something:

GET /institution/applications/changes?updatedSince=…&pendingActions=open

open returns applications with at least one outstanding action, none returns those with none, and omitting it returns both. Case is forgiven, and an unrecognised value is a 400 rather than an unfiltered feed.

Use this rather than ?status=ON_HOLD. An assessor can raise an action without putting the application on hold, so the status filter would miss it — the two are independent, as below.

An empty list is the normal case, not an error. Most applications are waiting on nothing.

pendingActions and status are independent. An application can have open actions without being ON_HOLD, and can be ON_HOLD with none — whether an application is workable is a judgement an assessor makes, not something that follows from a list being empty. Do not infer one from the other, and filter on pendingActions rather than on status when what you want is “waiting on something”.

Supplying what was asked for

Attach documents as you would normally — the one-call upload below is usually the simplest — and send any new qualifications or corrections the usual way.

Then tell us you have responded:

POST /institution/applications/{id}/pending-actions/{actionId}/resolve
{ "note": "Certified transcript uploaded 9 October" }

The note is optional and is kept against the action for the assessor to read. A bodyless POST works too.

Send it once you have actually supplied the thing. Resolving an action does not move any information — it is the signal, not the delivery.

Adding a qualification after an assessment has started

POST /institution/applications/{id}/qualifications

This now returns a 409 once an application has been assessed, where it previously returned 200.

It was never doing anything useful: an assessment works from a snapshot of the qualifications taken when it began, so one added afterwards was stored and never read. The credit could not change, and the 200 said otherwise.

If late evidence changes what an applicant is entitled to, submit a new application — the same answer as for a programme corrected after assessment. See Correcting records.

Documents are unaffected: you can attach them at any time, and an assessor reads them directly.

Safe to retry: resolving an action that is already closed returns it unchanged rather than failing, and leaves the original resolution in place, so a call you are unsure landed can be repeated.

An assessor can also close an action from their side, which is why resolvedBySource exists — institution_api means your integration closed it, officer means an assessor did.

Supplying information does not restart an assessment, and no endpoint does. In practice none is needed: asking for more information does not stop an assessment, so there is nothing waiting to be restarted — the assessor picks the application back up when the evidence arrives. What does need care is an application whose assessment has already FINISHED. New information then cannot change the outcome, and the answer is the same as for a wrong programme: a new application. See Correcting records.

Course topics, in the management portal

Some courses are assessed in parts rather than as a whole — COMM1100 as Economics, Law and Management — and the institution’s assessors can now record those parts, with the guidance for deciding whether a piece of prior study covers each one.

There is nothing to call and nothing you send changes: this is the institution’s own configuration, and the application and outcome shapes are untouched. It is noted here because it explains a vocabulary you will hear in conversation about RPL, and because one external course commonly covers topics belonging to several different courses at once.

See The catalogue for what a topic holds.

Supporting documents

Entries whose upload failed are no longer hidden

Creating a document entry and uploading its bytes are two separate calls, and the second can fail on its own — a timeout, a dropped connection, an expired URL. Until now, an entry whose bytes never arrived was left out of GET .../documents entirely, so there was no way to find it and retry. Worse, it was deleted half an hour later.

Those entries are now listed, and every document carries a status:

statusMeaning
UPLOADEDThe bytes are stored. sizeBytes is set and it can be downloaded
PENDINGThe entry exists, the file does not. sizeBytes is null

Nothing is deleted on a timer any more. A PENDING entry stays until you upload its bytes or delete it, however long that takes — your retry may be on tomorrow’s run, not within the hour.

uploadedAt on a PENDING document is when the ENTRY was created. Nothing has been uploaded, so there is no upload time to report.

Retrying a failed upload

POST /institution/applications/{id}/documents/{documentId}/upload-url

Returns a fresh uploadUrl and requiredHeaders for an entry whose bytes never arrived, against the same document id — a retry does not create a duplicate. Use it whenever the original URL has expired or its PUT failed. The PUT itself is unchanged.

A document that has already been uploaded gets a 409: replacing a stored file means deleting the entry and creating a new one, rather than having a retry quietly overwrite it.

Deleting a document

DELETE /institution/applications/{id}/documents/{documentId}

Removes the entry and its stored file, and works on a PENDING entry as well as an UPLOADED one. This is how an abandoned entry gets cleared now that nothing expires on its own.

Uploading in one call

POST /institution/applications/{id}/documents/content

Send the file as a multipart/form-data part named file and it is stored in a single call — no entry to create first, no presigned URL, no second request. The response is the document itself, already UPLOADED.

curl -X POST "$API/institution/applications/$ID/documents/content" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@transcript.pdf"

The filename and content type are taken from the part.

Limited to 10MB. Above that you get a 413, and the two-step flow is the answer — it sends the bytes straight to storage without passing through this API, so it has no such limit. Both flows produce the same document: nothing downstream can tell which one was used.

All four changes apply to qualification documents too, at /institution/qualifications/{qualificationId}/documents/....

Nothing to do

Beyond the rename, every change is additive. An integration that ignores the new fields and statuses behaves exactly as it does today.

Two behaviours to be aware of rather than act on:

GET .../documents now returns PENDING entries it previously omitted. If you treat everything in that list as downloadable, check status first — a PENDING document has no bytes and its download returns a 404.

pendingActions is present on every application, as an empty list where there is nothing outstanding. An integration that ignores the field is unaffected.

The one change that is not additive is the 409 on adding a qualification to an assessed application. If you do that today you are getting a 200 and no effect, so nothing that works will stop working — but a call that silently did nothing now fails loudly.