# Push a version

### 1. Send the document

`POST /versions` takes the document itself as the request body, YAML or JSON, with a project key:

```sh
curl -sS -X POST "$BASE/versions?label=v1.4.0&git_sha=$GITHUB_SHA&git_ref=$GITHUB_REF" -H "Authorization: Bearer $LATITUDE10_KEY" --data-binary @openapi.yaml
```

### 2. Read the answer

A new version answers `201` with its `seq`, `score`, `breaking` and `warnings` counts and `url`, its page in your workspace. If the bytes are the same as the latest version's, you get that version back with `200`, and nothing new is made.

## Parameters

All three are optional query parameters.

| Parameter | What it's for |
| --- | --- |
| `label` | The version's name. Without one, the document's `info.version` is used. |
| `git_sha` | The commit the document came from. |
| `git_ref` | The branch or tag, such as `refs/heads/main`. |

## What the registry accepts

- One OpenAPI 3.0 or 3.1 document, in YAML or JSON.
- One file: a `$ref` inside the document works, a `$ref` to another file or a URL is refused. The GitHub Action bundles local files into one before it pushes.
- Up to 2 MB, on every plan.

The registry keeps the newest versions up to your plan's number, and removes the oldest after each push: see [Plans and limits](../get-started/plans-and-limits#registry).

## Errors

| Status | When | The `error` |
| --- | --- | --- |
| `401` | No project key, or a revoked one. | `Send a project key: Authorization: Bearer l10p_…` |
| `403` | The workspace has more projects than its plan includes. | `This project is read-only: the workspace has more projects than its plan includes.` |
| `409` | Another push landed at the same moment. | `Another version was pushed at the same moment. Push again.` |
| `413` | The body is larger than the registry keeps. | The size sent and the most that's kept. |
| `422` | It isn't OpenAPI 3.0 or 3.1, it doesn't parse, or it has an external `$ref`. | What's wrong. For a `$ref`: `External $ref isn't supported here: …` and how to bundle it. |
| `429` | The project's pushes for today are used. | `Quota reached for this period. It resets at …`, with `resetsAt`. |

A push counts against the day's quota before its body is read, so a refused push still counts. Daily quotas reset at midnight UTC.

## Where this comes from

- `app/api/registry/v1/versions/route.ts`
- `app/api/registry/v1/_lib/route.ts`
- `app/registry/_lib/versions.ts`
- `app/registry/_lib/bundle.ts`
- `app/registry/_lib/usage.ts`
- `app/workspace/_lib/quotas.ts`
