# Connect in 3 steps

Work in a clone of the repository that holds your OpenAPI document.

1. **Run `vendor init`.** It finds your OpenAPI document and your SDK
   packages, and shows the two files it would write.

   ```sh title="Create the files"
   npx @upseam/cli vendor init
   ```

   **You should see** both files and a `y/N` question before anything is
   written.

2. **Merge the two files.** `vendor init` writes `upseam.vendor.yml` and
   `.github/workflows/upseam-vendor.yml`. Keep the workflow at that path.

3. **Cut a tagged release.** Self-serve publishing runs from tags only.

   ```sh title="Publish a release"
   gh release create v2.0.0 --generate-notes
   ```

   **You should see** the registration in the Actions log after each release.

The first release registers your repository with Upseam. There is no form and
no manual registration.

## Check how it went

```sh title="Check the registration"
npx @upseam/cli vendor status
```

`vendor status [path]` reads the repository from the git remote `origin` and
asks Upseam, with no token, what it knows. It prints whether the repository is
registered, its state, the holds, each package with its proof or what is
missing, and the last release. It exits 0 when the call works, also for a
repository that is not registered, and 1 on a network error or a rejected
name. `--json` prints the answer as it is and `--upseam-url <url>` points to
another Upseam App.

Preview the events of a release before you publish it:

```sh title="Preview the events"
npx @upseam/cli vendor publish --dry-run
npx @upseam/cli vendor publish --dry-run --base v1.0.0 --json
```

## What vendor init does

- It finds `openapi.yaml`, `openapi.yml` or `openapi.json` at the root, or any
  `openapi*` or `swagger*` file up to four directories deep. It also finds the
  package names of your SDKs in `package.json` and `pyproject.toml`, and
  skips a `package.json` with `"private": true`.
- It shows both files and asks `y/N` before writing. Without a terminal it
  needs `--yes` or `--dry-run`.
- `--yes` writes without asking, `--dry-run` writes nothing, `--force`
  replaces an existing file, `--spec <path>` picks the document and
  `--changelog <url>` sets the changelog link.
- It then offers the README badge, prints the link to your page in the App and
  prints the docs snippet. See [Badge and docs snippet](vendor-badge.md).

## The workflow

The workflow runs on every published release, and by hand with
`workflow_dispatch` on a tag. It diffs the document between the previous
release tag and the release, then sends the events. A release with no earlier
tag, or with no API changes, sends no events. It still reaches Upseam, so the
first one registers you and every one refreshes the state.

```yaml title=".github/workflows/upseam-vendor.yml"
name: upseam-vendor
on:
  release:
    types: [published]
  workflow_dispatch:
permissions:
  contents: read
  id-token: write
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
        with:
          fetch-depth: 0
          persist-credentials: false
      - uses: upseam/action@v0
        with:
          mode: vendor
```

## The config

`upseam.vendor.yml` sits at the repository root.

```yaml title="upseam.vendor.yml"
provider: acme-mail
name: Acme Mail
spec: openapi.yaml
versioning: path
changelog: https://docs.acme.example/changelog
packages:
  npm: [acme-mail]
  pypi: [acme-mail]
```

| Key          | Meaning                                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| `provider`   | Upseam provider id: 2 to 40 characters of `a-z`, `0-9` and `-`.                                          |
| `name`       | Display name.                                                                                            |
| `spec`       | Path to the OpenAPI 3.0 or 3.1 document, YAML or JSON, relative to the repository root.                  |
| `versioning` | How the API version is named: `path` (`/v2` in `servers`), `date`, `semver` or `none` (the release tag). |
| `changelog`  | Optional https link to your changelog. Every event links to it.                                          |
| `packages`   | SDK package names that users install, under `npm` and `pypi`.                                            |

## Connect from the App

The App lists the repositories where you have admin rights under **For API
vendors**. Pick one to see the OpenAPI document and the packages Upseam
detected, a preview of the events your next release would send and the registration
status. You can correct the provider id and the packages.

Setup writes the same files as `vendor init`. When the installation grants the
Workflows permission, Upseam makes one commit on a branch `upseam/vendor-setup`
and opens a pull request. Without it, the page shows the files with Copy
buttons and the `vendor init` command, and writes nothing. Upseam does not
request the permission.

## How the Action stays safe

- **No secret.** The job asks GitHub for a short-lived token that names your
  repository, its owner and the tag of the run. The token is valid only at the
  Upseam deployment it is sent to, the Action never prints it, and Upseam
  accepts each token once.
- **Ownership.** Upseam checks the token signature against the GitHub Actions
  keys. A fork or any other repository has its own registration and proves its
  own packages.
- **Data, not code.** The request carries the manifest, the release tag and
  link, and the change events. No source code is sent, only names from your
  OpenAPI document. Upseam validates the events and never runs anything from
  them.
- **Local references only.** A `$ref` to other files or URLs is refused, so
  the diff reads nothing outside the document.
