> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-unsw-v6.advance-uac.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-unsw-v6.advance-uac.com/_mcp/server.

# Overview

The UNSW Advance Institution API is how you feed Advance the two fundamental pieces it needs to
assess credit: **the curriculum data of programs and subjects/courses**, and **who is applying**.

Your access token identifies you on every request, so there is no institution id to pass. Reads
and writes are authorised from the token.

## Trying it out

These pages are the reference. To poke at a running instance, each environment serves a live
Swagger UI, where you can paste a token and fire real requests:

* [Test](https://unsw-v6-test.advance-uac.com/institution-api/q/swagger-ui/), which is what
  an integrator can call today
* [Dev](https://unsw-v6-dev.advance-uac.com/institution-api/q/swagger-ui/), which runs ahead
  of test and is less stable

Both serve their raw spec at `/q/openapi.json` on the same host. See
[Authentication](/authentication) for getting the token you'll need to paste in.

## The two halves

#### [Catalogue data](/unsw-advance-institution-api/catalogue/list-programs)

Your reference data: programs, subjects, specialisations, and the curriculum
structures that tie them together. Maintained once, then kept up to date.

#### [Application data](/unsw-advance-institution-api/applications/submit-a-new-application)

Applicants and their applications, including the qualifications they hold
and any supporting documents.

The catalogue comes first. An application names a program code and year, so that program
has to exist before anyone can apply to it.

## Everything is identified by code and year

A program, subject or specialisation is identified by its **code plus the year it applies
to**. `3584` in `2026` is a different record from `3584` in `2027`. Curricula change
between years, and an application is always assessed against the rules of its own intake
year, so both parts matter.

Neither can be changed after creation. To correct a code, delete the record and create it
again.

## Your IDs, not ours

Two separate mechanisms let you work with your own identifiers instead of storing ours.

**`externalIds`** on catalogue records maps a record to its ID in each of your systems:

```json
{ "externalIds": { "sis": "PRG-3584", "crm": "C-000123" } }
```

Send it on create or update and it merges per system, so omitting a system leaves that
system's ID alone. This helps when your own systems disagree about what a program is called.

**`sources`** on applicants and applications records where a record came from and what
you call it, such as your student number or your application number. An applicant or an
application can carry one entry per system of record:

```json
{
  "sources": [
    { "sourceName": "SITS", "sourceRef": "z1234567" },
    { "sourceName": "TRIM", "sourceRef": "T-99814" }
  ]
}
```

You can then look records up by any of your own IDs, without having stored ours:

```
GET /institution/applicants?sourceRef=z1234567
GET /institution/applications?sourceRef=APP-2026-00123
```

A `sourceRef` must be unique within a `sourceName`, and `sourceName` is required whenever
you send a `sourceRef`, since an ID with no system to interpret it isn't findable.

The first entry is the **primary**: it is what list views, search results and the change
feed show, and what the `sourceName` / `sourceRef` pair on every response reports. Order
is yours to set — send the entries in the order you want them.

> **Note**
>
> The single `sourceName` / `sourceRef` pair is still accepted everywhere it was, and is
> still returned on every response. Sending the pair counts as one entry, listed first;
> sending it on a `PATCH` edits the first entry and leaves the rest alone. Nothing built
> against the pair needs to change.

It's worth supplying these on applicants. When you submit an application, an applicant
carrying any of the references you send is matched on that first, ahead of name and date
of birth. That way a returning applicant is still recognised after a name change or a
corrected typo. If the applicant is matched on name and date of birth instead, the
references you sent are added to the record rather than discarded.

## Replace or amend: PUT and PATCH

|         | Behaviour                                                                                                        |
| ------- | ---------------------------------------------------------------------------------------------------------------- |
| `PUT`   | **Replaces.** Every editable field is written as sent, so a field you omit is *cleared*. Send the whole record.  |
| `PATCH` | **Amends.** Only the fields present are touched. Send `null` to clear one explicitly; omit it to leave it alone. |

**Catalogue records offer both.** Reach for `PATCH` when you're changing one thing,
particularly on subjects, where a `PUT` that forgets `description` or `learningOutcomes` will
wipe them.

**Applicants and applications offer `PATCH` only**, deliberately — see
[Correcting records](/corrections) for why, and for the rules on what can still be changed
once an application has been assessed.

An unrecognised field name in a `PATCH` body is rejected rather than ignored, so a typo can't
silently do nothing.

`externalIds` is the one exception in both: it always merges per system, and sending it as
`null` is refused rather than wiping every id you've registered.

## Curriculum structures

A program's curriculum is a tree of containers, such as "Core Courses" or "Prescribed
Electives", each holding subject entries and, optionally, child containers.

Containers are built as `DRAFT`, which lets you assemble and review a tree before anything
uses it. Attaching it to a program or specialisation, or nesting it under another
container, is only allowed while it is a draft. Publishing sets it `LIVE`.

## Assessment is asynchronous

Submitting an application stores it and returns immediately with status `RECEIVED`. Assessment
happens afterwards.

`GET /institution/applications/{id}` reports where intake got to:

| Status       | Meaning                                                |
| ------------ | ------------------------------------------------------ |
| `RECEIVED`   | Stored and awaiting assessment                         |
| `READY`      | You have told us the supporting documents are adequate |
| `PROCESSING` | Taken up for assessment                                |
| `WITHDRAWN`  | Withdrawn and closed. Terminal                         |
| `FAILED`     | Intake errored; see `statusDetail`                     |

The credit itself is a separate endpoint:

```
GET /institution/applications/{id}/outcome
```

`PROCESSING` means an assessment has *started*, not that it has finished. A credit officer may
still be reviewing it, so poll the outcome until its `finalOutcome` is `true`. See
[Credit outcomes](/credit-outcomes) for how to read it, and for the difference between "not
assessed yet" and "assessed, and awarded nothing".

`READY` and `WITHDRAWN` are yours to set, by posting to
`/institution/applications/{id}/ready` and `/institution/applications/{id}/withdraw`. Both
arrived on 18 September, along with the rename of `PROCESSED` to `PROCESSING`. See
[that release](/releases/2026-09-18) if you are migrating.

## Getting a token

Authentication is OAuth 2.0 **client credentials** against Keycloak. Request a token with
your client id and secret, then send it as a bearer token on every call. See
[Get access token](/unsw-advance-institution-api/get-access-token) for the exact request.

Tokens are short-lived. Request a new one when it expires rather than caching it
indefinitely.

## A typical integration

1. Load your catalogue: programs, subjects and specialisations for the intake year
2. Build curriculum structures for each program, and publish them
3. Submit applications as they arrive, with the applicant's qualifications attached
4. Attach supporting documents (transcripts, syllabi) to the application
5. Poll [the outcome](/credit-outcomes) until `finalOutcome` is `true`
6. [Correct](/corrections) anything your own systems change afterwards

Steps 1 and 2 are usually a one-off load followed by incremental amendments; steps 3 to 6
are your day-to-day traffic.