# How it works

## Mental model

A Payment Link is a shareable URL that points to a Shift4-hosted checkout page. When a customer opens the link and completes payment, Shift4 creates a **Checkout Session** — the object that records everything that happened during that checkout attempt.

- **Payment Link** — configuration object: amount, product name, lifecycle controls, collection fields. Managed via the API at `api.shift4.com`.
- **Hosted checkout** — the payment page at `pay.shift4.com/{id}` that the customer sees. Rendered by Shift4; no code to embed or maintain.
- **Checkout Session** — created when a customer opens the hosted checkout page. Carries the `staticFields` and `customFields` (with the customer's answers, once entered) from the link, plus `lastTransaction` — the underlying sale response containing card brand, last four digits, captured amount, billing/shipping address (if collected), and success/failure status. One link can produce many sessions; each session corresponds to one checkout attempt (successful or not).
- **Transaction** — the financial record of the payment attempt. Exposed on the session as `lastTransaction` once the customer submits payment.


The link itself does not contain payment data — it is the configuration template. All per-attempt data lives on the Checkout Session.

## Lifecycle and statuses

A payment link moves through the following statuses:

| Status  | Meaning | Accepts payments? | Can be reactivated? |
|  --- | --- | --- | --- |
| `active` | Live and open for payment. | Yes | N/A |
| `scheduled` (API only) | Created with a future `activatesAt` date; not yet accepting payments. | No — shows "The link will activate soon" to customers | N/A — activates automatically at `activatesAt` |
| `deactivated` | Manually disabled via Customer Hub or API. | No | Yes |
| `expired` | The current time has passed `expiresAt`. | No | No |
| `completed` | The number of successful payments has reached `restrictions.payments.limit`. | No | No |


**Transitions:**

- `active` → `deactivated` — manual action (`POST /paymentlinks/update` with `paymentLink.status: "deactivated"`, or via the Customer Hub).
- `active` → `expired` — automatic when `restrictions.dates.expiresAt` is reached.
- `active` → `completed` — automatic when `restrictions.payments.count` reaches `restrictions.payments.limit`.
- `deactivated` → `active` — manual action (`POST /paymentlinks/update` with `paymentLink.status: "active"`, or via the Customer Hub).
- `scheduled` → `active` — automatic at `restrictions.dates.activatesAt`.


`expired` and `completed` are terminal. Once a link reaches either state it cannot be reopened — create a new link.

## Link types

A payment link's `type` selects what the link does:

- **`line_items`** (default) — a payment. The customer pays for the products defined in `lineItems`. This is the type behind every fixed-amount and customer-chosen amount link.
- **`card_verification`** — validates a card and saves it to the customer **without charging it**. See [Card verification](#card-verification).


For `line_items` links, the **amount shape** of each line item then determines how much the customer pays — a fixed amount, or an amount the customer chooses. Fixed-amount links are available from both the Customer Hub and the API; customer-chosen amount and card-verification links are API only. In all cases, the `amount` field in each line item is expressed in major currency units — `10` equals $10.00.

### Fixed-amount link

A fixed-amount link charges every customer the same preset amount. The merchant defines the amount and currency at creation, and the customer has no input over what they pay. This is the most common link type and covers the vast majority of use cases: invoices, service deposits, event tickets, and product sales.

#### Request snippet

```
{
  "currencyCode": "USD",
  "amount": {
    "total": 10
  }
}
```

#### Example preview

Checkout with fixed amount
### Preset amounts

A preset-amounts link presents the customer with a fixed set of amounts to choose from. You define the options at creation; the customer selects one before proceeding to payment. This format works well when you want to guide the customer towards specific amounts — for example pay-what-you-want tiers, suggested tips, or a fundraiser offering $5, $10, and $25 options.

#### Request snippet

```
{
  "currencyCode": "USD",
  "amount": {
    "options": [5, 10, 25]
  }
}
```

#### Example preview

Checkout with multiple preset amount options
### Custom amount

A custom-amount link lets the customer enter any amount between a configured minimum and maximum. This format suits cases where amounts of any size are welcome and constraining the customer to specific options would feel too prescriptive — pay-what-you-want checkouts, tips, or open fundraisers. A customer-chosen amount link must contain exactly one line item.

#### Request snippet

```
{
  "currencyCode": "USD",
  "amount": {
    "custom": {
      "min": 1,
      "max": 1000
    }
  }
}
```

#### Example preview

Checkout with a custom amount
### Combining preset amounts and a custom amount

The two customer-chosen modes aren't mutually exclusive. Provide `options` and `custom` together in the same `amount` object to show the preset amounts alongside an "other amount" field — the customer can pick a suggested amount or enter their own within the configured range. Only `total` (the fixed-amount shape) is mutually exclusive with the customer-chosen shapes.

### Card verification

A card-verification link runs a $0 authorization to confirm a card is valid — and that there's a real person behind it — **without charging anything**. It's a security / legitimacy check: it succeeds only if the card is live and passes the issuer's checks. Set `type` to `card_verification`; the link takes **no** `lineItems` but **requires** a top-level `currency` and a `customer` (a new inline customer or an existing one by `id`).

The validated card is also saved to that customer, so the next time **the same customer** opens **another payment link from the same merchant**, the card appears as a one-tap option on the hosted checkout — a returning customer can pay without re-entering it. (Saving a card on its own doesn't require verification — a normal payment link can do that too; reach for `card_verification` when validating the card is the point and you don't want to charge.)

#### Request snippet

```
{
  "type": "card_verification",
  "currency": "USD"
}
```

#### Example preview

Card verification checkout
## Lifecycle controls (restrictions)

| Control | API field | Available via | Effect |
|  --- | --- | --- | --- |
| Schedule activation | `restrictions.dates.activatesAt` | API only | Link stays `scheduled` until that ISO 8601 timestamp. Customers see "The link will activate soon." |
| Set expiry | `restrictions.dates.expiresAt` | Customer Hub and API | Link automatically moves to `expired` at that ISO 8601 timestamp. |
| Limit successful payments | `restrictions.payments.limit` | Customer Hub and API | Link moves to `completed` once this many successful payments have been made. `restrictions.payments.count` tracks the running total (read-only). |
| Deactivate | `POST /paymentlinks/update` with `paymentLink.status: "deactivated"` / Customer Hub | Customer Hub and API | Immediately stops accepting payments. Reversible. |


`activatesAt` and `expiresAt` can be combined to define a precise active window — useful for time-limited promotions or scheduled fundraisers.

## Data collection and where it appears

By default, the hosted checkout collects only the card details and email required for payment. Enable additional fields to collect more from the customer:

| Field | API field | Available via |
|  --- | --- | --- |
| Billing address | `collectBillingAddress: true` | Customer Hub and API |
| Company name | (Customer Hub toggle only) | Customer Hub only |
| Shipping address | `collectShippingAddress: true` | API only |
| Custom fields (arbitrary questions) | `customFields` array | API only |
| Static fields (read-only context for customer) | `staticFields` object (key/value map) | API only |


All collected data is stored on the **Checkout Session** object — not directly on the payment link. Every session captures customer email (and phone if provided) and payment details (card brand, last four digits, status); the optional fields from the table above are included when enabled.

Access the session through any of these:

- **API** — `GET /checkoutsessions/retrieve` with the session ID in the `CheckoutSessionId` header (taken from the `returnUrl` redirect's `checkout_session_id` query parameter or the `checkoutSession.id` field on the webhook payload).
- **Webhook** — the full session object is included in the `checkout_session_completed` event payload (API only).


### Static fields vs. custom fields vs. metadata

Three mechanisms exist for attaching data to a payment link. They serve different purposes:

| Field  | Shown to customer | Customer can edit | Use for |
|  --- | --- | --- | --- |
| `staticFields` | Yes | No | Context the customer should see: order ID, event name, reference number |
| `customFields` | Yes (as a form field) | Yes | Information you need from the customer: attendee name, T-shirt size |
| `metadata` | No | No | Internal IDs for your own systems: ERP key, CRM record, campaign tag |


## Common patterns

| Pattern | Configuration | Use case |
|  --- | --- | --- |
| Invoice payment | Fixed amount, billing address enabled, invoice number as a custom field (API only), email delivery | Track payment status in the Customer Hub for A/R collections |
| Deposits and reservations | Fixed amount, expiry date, customer contact info, confirmation email | Secure reservations with deadline enforcement |
| Tickets and admissions | Fixed amount per ticket, `restrictions.payments.limit` set to venue capacity, attendee info via custom fields (API only) | Event ticketing with automatic capacity limits |
| Trip and group payments | Fixed amount per participant, attendee details via custom fields (API only), expiry before the trip date (API only) | School groups, travel bookings, multi-person registrations |
| Customer support / call center | Variable amount, email delivery, real-time status tracking | Create payment links during support calls and send them instantly to the customer |
| Pay-what-you-want (API only) | Customer-chosen amount with preset amounts or a custom amount, optional custom fields for extra info | Fundraising, tips, and name-your-price checkouts |


## Notifications and post-payment signals

When a customer completes checkout, Shift4 sends a `checkout_session_completed` event to your registered webhook endpoint — this is the authoritative signal for a captured payment. For the full list of events, retry behaviour, and payload format, see [Monitor and reconcile](/guides/core-concepts/payment-links/guides/monitor-and-reconcile/api#notifications).

## Next steps

Get started
Create your first payment link and confirm a payment — Customer Hub and API quickstart.

Create link
Set expiry dates, add line items, collect billing address and custom fields.

Monitor and reconcile
Track status, list charges, and receive webhooks for server-side confirmation.

Troubleshooting & FAQ
Answers to common questions about status, notifications, and reconciliation.