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

# Pay to Access

> Sell usage-metered access to your own app or website — issue a code that locks itself out after N uses, verified by your own backend via a public API

# Pay to Access

**Pay to Access** sells metered access to something you host yourself — a web app, a members' page, anything outside PayRequest. After payment, PayRequest issues a random access code with a usage limit (e.g. "5 uses"). Your own backend calls PayRequest's public API each time someone tries to use the code — PayRequest tracks how many uses are left and locks the code out once the limit is reached.

<Info>
  Pay to Access is available on all plans. Product type: `pay_to_access`.
</Info>

<CardGroup cols={2}>
  <Card title="No File Involved" icon="key">
    Unlike Digital Products or Pay to Unlock, nothing is uploaded or stored — PayRequest only issues and tracks the access code
  </Card>

  <Card title="You Own the Gate" icon="lock-open">
    Your app decides what "access" means (a login, an API call, a page view) — PayRequest just answers "is this code still valid?"
  </Card>

  <Card title="Usage-Limited, Not Time-Limited" icon="gauge">
    Caps the *number of uses*, not just a subscription window — "paid for 5 logins" locks after the 5th, however many people share the code
  </Card>

  <Card title="Revocable" icon="ban">
    If a code leaks or an order is refunded, revoke it from the order detail page and it stops working immediately
  </Card>
</CardGroup>

## Setting Up a Pay to Access Product

<Steps>
  <Step title="Start a New Product">
    Navigate to **Products** and click **New Product**. Select **Pay to Access** as the product type.
  </Step>

  <Step title="Set Max Uses and Expiry">
    Under **Access Settings**, set **Max Uses** (how many times the code can be verified before it locks) and **Code Expiry** (how many days the code stays active).
  </Step>

  <Step title="Set Your Price and Save">
    Enter the price and save. No file upload is required — there's nothing to store.
  </Step>

  <Step title="Integrate the API">
    In your own app's backend, call the verify/consume API below at the moment you want to check or burn a use.
  </Step>
</Steps>

## How Delivery Works

1. **Customer pays** — Payment is processed through Mollie.
2. **Code generated** — A random 64-character access token is created with the configured `max_uses` and `expires_at`.
3. **Shown immediately** — The code appears in a copyable "Your Access Code" card on the payment success page, right away — no need to wait for email.
4. **Email delivery** — The order confirmation email also includes the access code (`{{access_codes}}` template variable), so the customer has a backup copy.
5. **Your app verifies it** — Your backend calls the public API below to check the code and burn a use.

## Integrating with Your App

These two endpoints are **public and require no OAuth token** — the 64-character code itself is the only secret, the same trust model as PayRequest's existing digital download links. Call them from your own server (not client-side JavaScript, so the code isn't exposed in browser devtools).

<Warning>
  Unlike the rest of `/api/v1`, these endpoints do not use OAuth. Do not send your PayRequest API token to them — they only need the access code.
</Warning>

### Verify (read-only)

Check whether a code is currently valid without consuming a use. Use this to show "you have 3 uses left" before deciding whether to actually grant access.

```bash theme={null}
POST /api/v1/access-tokens/{token}/verify
```

<ParamField path="token" type="string" required>
  The 64-character access code the customer entered
</ParamField>

```bash theme={null}
curl -X POST "https://payrequest.app/api/v1/access-tokens/YOUR_CODE_HERE/verify" \
  -H "Accept: application/json"
```

**Valid code:**

```json theme={null}
{
  "valid": true,
  "remaining_uses": 3,
  "max_uses": 5,
  "expires_at": "2026-08-01T00:00:00+02:00"
}
```

**Invalid code:**

```json theme={null}
{ "valid": false, "reason": "limit_reached" }
```

`reason` is one of: `not_found` (404), `expired` (403), `revoked` (403), `limit_reached` (403).

### Consume (burns one use)

Atomically burns one use of the code. Call this the moment you actually grant access (e.g. right when the customer's login succeeds) — every successful call permanently reduces the remaining uses. Don't call it on every page load.

```bash theme={null}
POST /api/v1/access-tokens/{token}/consume
```

<ParamField path="token" type="string" required>
  The 64-character access code the customer entered
</ParamField>

```bash theme={null}
curl -X POST "https://payrequest.app/api/v1/access-tokens/YOUR_CODE_HERE/consume" \
  -H "Accept: application/json"
```

**Successful consume:**

```json theme={null}
{
  "valid": true,
  "remaining_uses": 2,
  "max_uses": 5,
  "expires_at": "2026-08-01T00:00:00+02:00"
}
```

**Rejected (limit already reached):**

```json theme={null}
{ "valid": false, "reason": "limit_reached", "remaining_uses": 0 }
```

<Tip>
  Consuming is atomic under concurrent requests — if two requests for the same code arrive at once with only one use left, exactly one succeeds and the other gets `limit_reached`. You'll never see the use count go negative or over the limit.
</Tip>

### Example: gating a login

```javascript theme={null}
// Your app's backend, at the moment a customer tries to log in
const response = await fetch(
  `https://payrequest.app/api/v1/access-tokens/${accessCode}/consume`,
  { method: 'POST', headers: { Accept: 'application/json' } }
);
const result = await response.json();

if (!result.valid) {
  // show "code invalid / no uses left" and stop
  return;
}

// grant access - result.remaining_uses tells you how many logins are left
```

## Managing Issued Codes

Open any order containing a Pay to Access product to see its issued code, remaining uses, and expiry. Two actions are available:

* **Reset** — sets the use count back to 0 and extends the expiry. Use this if a customer needs more uses.
* **Revoke** — immediately invalidates the code (e.g. it leaked, or the order was refunded). A revoked code always returns `reason: "revoked"`, even before its uses or expiry run out.

You don't need to hunt through orders to find a code, though — the product page (**Products → \[your Pay to Access product]**) lists the 20 most recently issued codes directly, each with its use count, status, a copy button, and a link to the order it came from.

## FAQ

<AccordionGroup>
  <Accordion title="What happens if the customer shares the code with other people?">
    Nothing stops that — the code isn't tied to a single device or browser. It simply stops working once `max_uses` is reached, no matter who used it or how many people shared it. This matches "you paid for 5 uses" rather than "you paid for 1 seat."
  </Accordion>

  <Accordion title="Do I need an API key to call verify/consume?">
    No. The access code itself is the only secret required, the same as PayRequest's `/download/{token}` links. Keep the call server-side so the code isn't exposed in browser devtools.
  </Accordion>

  <Accordion title="What's the difference between verify and consume?">
    `verify` is read-only and never changes the remaining uses — good for showing a "uses left" counter. `consume` is the one that actually burns a use, and should only be called at the moment you grant access.
  </Accordion>

  <Accordion title="Can I change Max Uses after the code has already been issued?">
    Editing the product only changes the default for *future* purchases. To change an already-issued code's limit, use Reset on the order detail page (extends expiry and zeroes the use count) — there's currently no way to change `max_uses` on an existing code without resetting it.
  </Accordion>

  <Accordion title="Does Pay to Access support custom fields or multiple quantities?">
    No. Each Pay to Access product is a single access code sold as one item, so the custom fields step and quantity selection are skipped automatically.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Digital Products" icon="file-arrow-down" href="/products-and-pricing/digital-products">
    For selling files instead of metered access
  </Card>

  <Card title="Pay to Unlock" icon="lock-closed" href="/products-and-pricing/pay-to-unlock">
    For blurred-photo/audio-preview style unlocks
  </Card>

  <Card title="Creating Products" icon="plus" href="/products-and-pricing/creating-products">
    Learn about all product types and settings
  </Card>

  <Card title="API Overview" icon="code" href="/api/overview">
    PayRequest's OAuth-protected billing API (separate from the Pay to Access verify/consume endpoints)
  </Card>
</CardGroup>
