9 October 2026
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
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:
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:
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:
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
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
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,applicantTypeapplicantSourcesandapplicationSources— your own references, kept apart, because an application number is not a student numberorganisationNameandorganisationCodeon 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.
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:
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:
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
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:
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
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
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
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.
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.
