> For the complete documentation index, see [llms.txt](https://docs.stellic.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stellic.com/stellic-partner-api/programs/create-an-audit-for-a-program.md).

# Create an audit for a program

Use this endpoint to start a new version of a program's requirements, usually for a new catalog year. Send a `name`, the entry `year` it applies to, and optionally a `base_audit` (an audit `id`) to copy requirements from. You get back `new_audit_id`, a standalone draft. To change the audit that's published now, use [`POST /audits/{audit_id}/draft`](/stellic-partner-api/audits/get-or-create-a-draft-of-a-published-audit.md) instead.

## POST /programs/{program\_short\_name}/audits

>

```json
{"openapi":"3.0.0","info":{"title":"Stellic for Developers","version":"1.0"},"tags":[{"name":"Programs","description":"Programs in your catalog and the audits (requirement versions) attached to them. Programs are addressed by `short_name`; audits by internal numeric ID."}],"servers":[{"url":"https://{institution}.stellic.com/partnerapi/v1","variables":{"institution":{"default":"{institution}","description":"Your institution's Stellic subdomain: the part before `.stellic.com` in the URL you use to sign in. It becomes the host in every request, as in `https://{institution}.stellic.com/partnerapi/v1/...`. Replace the `{institution}` placeholder with your subdomain before sending."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Personal Access Token issued by your Stellic integration partner. Send it as `Authorization: Bearer <token>` on every request. One token per institution; do not share across environments."}},"schemas":{"CreateAuditRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Name of the new audit. Must be unique within the program."},"year":{"type":"integer","description":"Entry year the audit applies to. Once published, each student on the program gets the published audit with the latest `year` that isn't after their own year (their catalog year for the program if they have one, otherwise their entry year). Only one published or draft audit per program may have a given year. Ignored for shared requirement programs, which have no student criteria. On a regular program, an audit without `year` isn't matched to any student."},"base_audit":{"type":"integer","description":"Internal `id` of an existing audit whose requirement tree is copied into the new draft. Omit to start from an empty root. Audits of compliance-requirement programs are rejected."}}},"CreateAuditResponse":{"type":"object","properties":{"success":{"type":"boolean"},"new_audit_id":{"type":"integer","description":"Internal database ID of the new draft audit."}}},"ErrorMessage":{"type":"object","description":"Error envelope used by the programs, audits, and requirements endpoints.","properties":{"error":{"type":"string","description":"Human-readable reason. Match on it if you need to branch; the strings on each endpoint page are verbatim."}}},"AuditNameConflictResponse":{"type":"object","description":"Returned with 400 when another audit on the program already has the requested name.","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"audit":{"type":"object","description":"The existing audit. For shared requirement programs the `name` field carries the audit's status instead of its name.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string"},"concentrations":{"type":"array","description":"Concentrations referenced by the audit's concentration requirements, if any.","items":{"type":"object"}},"requirement":{"type":"integer","description":"Internal ID of the audit's root requirement."}}}}}}},"paths":{"/programs/{program_short_name}/audits":{"post":{"tags":["Programs"],"parameters":[{"name":"program_short_name","in":"path","required":true,"description":"The program's `short_name` (the `external_id` value returned by `GET /programs/`). Matched exactly, case-sensitive.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuditRequest"}}}},"responses":{"201":{"description":"Draft audit created. The new audit is a standalone draft; nothing is published and no student is affected until you call `POST /audits/{audit_id}/publish`. Use `new_audit_id` on the audit endpoints.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuditResponse"}}}},"400":{"description":"Validation failure. Two body shapes: most failures return `error`; a name collision returns `success: false` with `message` and the existing `audit`.\n\nCommon causes:\n- Missing `name` - returns `'name' is required.`\n- A criteria field other than `year` (`campus`, `entry_term`, `specific_students`, `departments`, `student_tags`) - returns `Invalid criteria fields provided: ['campus']. The following is supported today: ['year'].`\n- `base_audit` is not an integer - returns `Invalid base_audit ID: {base_audit}`\n- `base_audit` does not exist - returns `No Audit matches the given query.`\n- No program has this `short_name` - returns `No Program matches the given query.`\n- Another audit on this program already has this name - returns `success: false`, `message: Another audit has the same name: '{name}'. Change the name first and try again.`, and the existing audit under `audit`\n- Another audit on this program already targets the same `year` - returns `['Another audit: {name} applies to the same student criteria. Edit that audit or change the criteria of this one.']`\n- The token's account lacks edit rights on the program - returns an empty `error` string","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorMessage"},{"$ref":"#/components/schemas/AuditNameConflictResponse"}]}}}},"401":{"description":"Missing or invalid Bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"detail":{"type":"string"}}}}}},"403":{"description":"Your IP address isn't on the allowlist for this endpoint. Contact Stellic to add it; the response includes the `client_ip` Stellic saw. Also returned, with only `error` set to `Compliance requirements are not available via the API.`, when the program or `base_audit` belongs to a compliance requirement.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"client_ip":{"type":"string"}}}}}}}}}}}
```

#### What happens after the call

* A draft audit named `name` is created. With `base_audit`, it copies that audit's requirements, including linked shared requirements; without it, it's an empty root.
* On a regular program, `year` is saved with the draft, and the general requirements that apply to it are updated in the background. On a shared requirement, `year` is ignored.
* No student is affected until you [publish](/stellic-partner-api/audits/publish-a-draft-audit.md) the draft. Once it's published, each student on the program gets the published audit with the latest `year` that isn't after their year: their catalog year for the program if they have one, otherwise their entry year.

#### Notes

{% hint style="warning" %}
**Send `year` on a regular program.** An audit without it isn't matched to any student once it's published, and `year` is the only criterion the API accepts.
{% endhint %}

* **One audit per `year`.** If a published or draft audit on the program already has that year, the call returns `['Another audit: <name> applies to the same student criteria. Edit that audit or change the criteria of this one.']` Draft that audit instead.
* **Don't put `-draft` in `name`.** That suffix marks a published audit's draft, so an audit named that way without one is left out of [`GET /programs/{program_short_name}/audits`](/stellic-partner-api/programs/list-a-program-s-audits.md) and can't be published.
* **Other criteria are rejected.** `campus`, `entry_term`, `specific_students`, `departments`, or `student_tags` in the body return `Invalid criteria fields provided: ['campus']. The following is supported today: ['year'].` Set them in Stellic.
* **A name already in use comes back in a different body:** `success: false`, a `message`, and the existing `audit`, instead of `error`.
* **`base_audit` is an audit `id` from any program.** A non-integer returns `Invalid base_audit ID: <value>`, and an unknown ID returns `No Audit matches the given query.` An audit of a compliance requirement returns a 403.
* **The token's account needs edit rights on the program's audits.** Without them, the call returns a 400 with an empty `error`.
* **Other errors return 400 with `{"error": "<message>"}`,** including an unknown program (`No Program matches the given query.`). A compliance requirement returns a 403.

#### Related APIs

* [`GET /programs/{program_short_name}/audits`](/stellic-partner-api/programs/list-a-program-s-audits.md) to find a `base_audit`.
* [`GET /audits/{audit_id}`](/stellic-partner-api/audits/read-an-audit.md) to read the draft; its root requirement is the first `parent_req_id`.
* [`POST /audits/{audit_id}/requirements/{parent_req_id}/subrequirements`](/stellic-partner-api/audits/add-a-sub-requirement.md) and [`POST /requirements/{requirement_id}/constraints`](/stellic-partner-api/requirements/add-a-constraint-to-a-requirement.md) to build it.
* [`POST /audits/{audit_id}/publish`](/stellic-partner-api/audits/publish-a-draft-audit.md) to publish it.
