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

# Gate checkout

> Sell access, verify visitors, collect payment, and deliver content.

A published listing is a reusable checkout: visitors complete the verifiers you configured, optionally pay, and receive the reward you attached. This page documents the full server contract behind hosted checkout so you can mirror it from your own app or backend.

The simplest integrations do not need any of this directly. Use the [VerifyGate widget](../widgets/verifygate) or the hosted checkout link. Read on when you want to drive the flow yourself.

## Which API host?

| Caller                       | Gate snapshot                        | Fulfill                                       |
| ---------------------------- | ------------------------------------ | --------------------------------------------- |
| **SDK** (`ProofableClient`)  | `client.getGate(gateId)`             | `client.fulfillGate(...)`                     |
| **`api.proofable.me`**       | `GET /api/v1/profile/gates/{gateId}` | `POST /api/v1/profile/gates/{gateId}/fulfill` |
| **`proofable.me` (browser)** | `GET /api/v1/gates/{gateId}`         | `POST /api/v1/gates/{gateId}/fulfill`         |

Prefer the SDK on your server. The `proofable.me` paths are the same contract, proxied for same-origin browser calls.

## Lifecycle

1. **Snapshot**: load the public gate: requirements, price, schedule, and reward presence (not the secret reward value). Use `client.getGate(gateId)` or `GET /api/v1/profile/gates/{gateId}` on `api.proofable.me`.
2. **Eligibility**: `GET /api/v1/proofs/check?gateId=...&address=...&includePrivate=true&includeQHashes=true` evaluates the visitor's existing proofs against every requirement.
3. **Verify**: if proofs are missing, the visitor completes them (hosted verify link, VerifyGate, or `POST /api/v1/verification` for signature-based verifiers). Pass `gateId` and reuse satisfied proofs via `options.reusedVerifierProofs`.
4. **Pay**: for paid gates, payment happens after verification by default (`executionOrder: "verifyThenCharge"`). Gates with a connected payout account settle through one Stripe checkout session. The visitor picks card or crypto there. Gates with a custom wallet settle by direct USDC on Base.
5. **Fulfill**: deliver the reward with the verified `qHash` (plus payment evidence for paid gates) via `client.fulfillGate(...)` or `POST /api/v1/gates/{gateId}/fulfill`. If the snapshot includes `requestedAccess`, also send the buyer-approved `accessGrant` (connected account + selected resource). Hosted checkout stamps that grant on the receipt. A configured return URL receives `grant` and `receipt` set to the same proof ID. The listing owner then calls `POST /api/v1/proofs/composio/execute` with `grant` set to that proof ID. Proofable runs the tool as the buyer and never returns the buyer’s credential. Missing, revoked, or out-of-scope grants return `403`.

```js theme={"dark"}
import { ProofableClient } from '@proofable/sdk';

const client = new ProofableClient();

// 1. Snapshot
const gate = await client.getGate('gate_your-listing');

// 2. Eligibility
const check = await client.gateCheck({
  gateId: 'gate_your-listing',
  address: visitorWallet,
  includePrivate: true,
  includeQHashes: true,
});

// 5. Fulfill (after verify + pay)
const reward = await client.fulfillGate({
  gateId: 'gate_your-listing',
  qHash: verifiedQHash,
  walletAddress: visitorWallet,
});
```

## Reading the gate check

When `gateId` is passed, the response carries a per-requirement `data.gate` block. **`gate.allRequiredSatisfied === true` is the only signal that checkout is ready.** Top-level `eligible` and `matchedCount` exist for criteria-only checks and must not be used as gate readiness on their own.

```json theme={"dark"}
{
  "success": true,
  "data": {
    "eligible": true,
    "gate": {
      "gateId": "gate_your-listing",
      "allRequiredSatisfied": true,
      "satisfiedVerifierIds": ["proof-of-human", "wallet-risk"],
      "missingVerifierIds": [],
      "reusedVerifierProofs": {
        "proof-of-human": "0xabc…",
        "wallet-risk": "0xdef…"
      }
    }
  }
}
```

* `satisfiedVerifierIds` / `missingVerifierIds`: which requirements existing proofs cover.
* `reusedVerifierProofs`: verifierId → qHash map (requires `includeQHashes=true`). Pass it as `options.reusedVerifierProofs` on `POST /api/v1/verification` so satisfied checks are not re-run.
* Re-run the gate check after every interactive step (OAuth grant, personhood session) before treating checkout as complete. Interactive completions only count once the protocol confirms them here.

## Request-time vs proof-based rules

Each requirement carries `match` rows (`{ path, op, value }`). They are enforced at two different moments:

| Rule type                                                      | Examples                                                                 | Enforced                                                                        |
| -------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Request-time (`eq` on input fields)                            | `domain`, `contractAddress`, `chainId`, `minBalance`, `provider`         | When the verification request is submitted. The locked value must match exactly |
| Proof-based (`traits.*`, `claims.*`, risk fields, `gte`/`lte`) | `claims.age_min`, `claims.liveness_verified`, `riskLevel`, `overallRisk` | After verification, against the check result                                    |

Proof-based rows mean an existing proof may not satisfy a stricter gate. For example, a personhood proof created for a basic gate will not satisfy another gate that also requires `claims.age_min ≥ 21`. The visitor must complete the additional check.

For `wallet-risk` gates, any proof-based row also requires `policyVerified` to be true. A failed risk check never satisfies a gate.

### Verification links

In the gate builder, choose **Verification link**. The visitor pastes their own HTTPS URL; Proofable reads JSON from that URL or its conventional `.json` form and checks whether `verified` is `true`.

Builders do not configure fetch targets, callbacks, or provider-specific connectors. The saved gate contains the portable match rows `reference.type = url` and `resolved.verified = true`. API clients can use other `resolved.*` output matches when a source exposes a different public JSON contract. Resolved-link proofs default to five-minute freshness so checkout does not silently reuse old source status.

See [Content ownership](../verification/ownership-basic#verification-links-in-hosted-gates) for the wire shape and resolver safeguards.

## Paid gates

The snapshot's `monetization.charge` describes pricing:

* `amountUsd`, `label`, `methods` (`usdc`, `stripe`), `cardPayoutReady`
* `executionOrder`: `verifyThenCharge` (default): verification completes first, then payment, then fulfill.

Method semantics:

* `stripe`: hosted Stripe Checkout (card). Available when the gate has a connected payout account.
* `usdc`: direct on-chain USDC transfer on Base. The only method for custom-wallet payout gates.

Fulfill payment evidence:

* **Stripe**: `paymentCheckoutSessionId` from the checkout return.
* **USDC**: `paymentTxHash` of the on-chain transfer.

Payments are bound to one `gateId` + `qHash` pair and cannot be reused for another checkout (`409 PAYMENT_ALREADY_USED`).

## Fulfillment result

```json theme={"dark"}
{
  "success": true,
  "data": {
    "gateId": "gate_your-listing",
    "qHash": "0xabc…",
    "fulfillment": {
      "delivery": "redirect",
      "type": "redirect_url",
      "value": "https://yourapp.com/members"
    }
  }
}
```

`fulfillment.delivery` is one of `access_granted`, `redirect`, `download`, or `reveal`. The secret value appears only here, after verification (and payment) succeeded for the caller's wallet.

## Campaign windows

Gates may carry a `schedule` (`startsAt` / `endsAt`). Outside the window, gate checks report the closed state and verification/fulfillment are refused server-side (`GATE_NOT_STARTED`, `GATE_ENDED`). Treat the window as enforced. It is not a UI-only hint.

## Next

<CardGroup cols={2}>
  <Card title="VerifyGate widget" icon="object-group" href="../widgets/verifygate">
    Drop-in checkout for published gates
  </Card>

  <Card title="Pricing" icon="tags" href="./pricing">
    Plans and credits
  </Card>

  <Card title="Billing" icon="credit-card" href="./billing">
    How verification and checkout charges work
  </Card>

  <Card title="API overview" icon="code" href="../api/overview">
    Full HTTP API surface
  </Card>

  <Card title="Verifier catalog" icon="list-check" href="../verification/verifiers">
    Every check a gate can require
  </Card>
</CardGroup>
