> ## Documentation Index
> Fetch the complete documentation index at: https://docs.entire.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Install flow

> How a customer installs your plugin, and how to verify the install receipt.

The customer starts on your site, approves on Entire, and returns to you.

1. **You send the customer to Entire.** Make an unguessable state value, tie it to the customer's browser session (for example in a signed, HttpOnly cookie), and redirect to:

   ```text theme={null}
   https://entire.com/plugins/{slug}/install?redirect_uri={post-install URL}&state={state}
   ```

2. **The customer approves.** They sign in to Entire, choose a project they manage, and choose all of its repositories or a list. The page shows the permissions you registered. For `repositories:create` it shows "Create repositories in this project". Only people who manage the project can install plugins.

3. **Entire sends the customer back.** On approval, the browser returns to your `redirect_uri` with two query parameters, `receipt` and `state`. If the customer cancels, it returns with `error=access_denied` and the state instead.

4. **You verify the receipt.** Check every item in the list below before you trust it.

5. **You store the installation ID.** It is the only key you need. Show the customer a confirmation page.

## Verifying an install receipt

The receipt is a compact JWT signed with EdDSA. The SDK's `App.AcceptReceipt` does all of this:

* The `state` query parameter equals the state you stored for this browser session. Compare in constant time. A mismatch is a forged or replayed callback.
* The header `typ` is `entire-install-receipt+jwt`, and `alg` is `EdDSA`.
* The `iss` claim is one of Entire's [Cores](https://docs.entire.com/guides/api/regions-and-routing.md#cores). Check this before you fetch keys, because the issuer decides where the keys come from.
* The signature verifies against the key named by `kid` in the issuer's JWKS at `{iss}/.well-known/jwks.json`.
* `aud` includes `urn:entire:plugin:{slug}`, and `plugin_id` is your slug.
* `exp` has not passed. Receipts last five minutes, so verify them as soon as they arrive.
* The `state` claim inside the receipt equals your stored state.
* You have not accepted this `jti` before.

The verified receipt carries `installation_id`, `project` (the project's name) and `project_id`.

## Key installations on their ID alone

Installations are global. Any Core serves any installation, and the receipt's issuer only tells you which Core handled the consent. Do not key your records on the issuer, and do not assume the installation belongs to one region. Call whichever Core is nearest your service.
