# POST /api/hostedpaymentpages/sessions

Creates a new HPP session with pre-populated field data.

Send an `idempotencyKey` to make the call safe to retry: the same key with the same request
body returns the session already opened rather than opening a second one, and the response's
`idempotencyStatus` says which happened. Reusing a key with a different body is refused.
The key is a request-body field; no `Idempotency-Key` header is read.

**Operation ID:** `hppSessionCreate`

## Authorization

Requires: HostedPaymentPage.HppSessions, HostedPaymentPage.HppSessions.Create, merchant scope.

Requires API key scope: `transactions:write`

Required permissions:
- `HostedPaymentPage.HppSessions`
- `HostedPaymentPage.HppSessions.Create`

## Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| suppressNulls | query | no | boolean | If true, omit properties with null values. |

## Request Body

The session creation parameters.

**Content type:** `application/json`

Schema: [`CreateHppSessionInput`](https://devportal.demo.winkpg.io/docs/api/models/CreateHppSessionInput.md)

Properties:
- `hostedPageId` (string(uuid)) required: The HPP configuration ID this session targets. Required.
- `customerId` (string(uuid)): Optional trusted customer id the merchant binds to this session. When supplied, the  session relates to exactly this one customer: customer resolution at consent-capture  time uses this id exclusively and never falls back to the ambiguous cardholder-email  find-or-create, and the resulting transaction's `InvoiceData.CustomerId` is stamped  from it server-side. The id is validated at session-create time to exist, be active, and  belong to the session's merchant; a missing, inactive/soft-deleted, or cross-merchant id  is rejected. `null` (the default) preserves the historical anonymous behavior where  the customer is found-or-created from the cardholder email collected on the form. (nullable)
- `prefilledFields` (object): Pre-populated field key/value pairs used to pre-fill the payment form. Use one of the well-known  field keys listed below, or an enabled merchant custom field name. Keys reserved for sensitive card  data or internal identifiers are always rejected.     Handling of an unrecognized custom-field key depends on whether the target page scopes its custom  fields (see the hosted page's custom-field allow-list). When the page does NOT scope its custom  fields, an unrecognized key is accepted but ignored when the form is rendered (unchanged behavior).  When the page DOES scope its custom fields, a key that is not a well-known field and not one of the  page's allowed custom fields is rejected at session create with an error naming the offending key.       A custom field the merchant has configured to accept a value from this API without showing it to  the payer is also accepted here. Its value is applied to the resulting transaction server-side,  exactly as supplied, and the field is not rendered on the payment form. Because the payer never  sees it and nobody can correct it later, the value is validated against the field's own rules  (maximum length, regular expression, numeric range) at session create: a value that breaks one of  them is rejected here rather than being carried and dropped at payment time. Conditional: Conditional (see validator source). Conditional: When AmountMode == Suggested. Conditional: When RequiresPrefill(TaxAmountMode) is true. Conditional: When RequiresPrefill(ShippingAmountMode) is true. Conditional: When LockShippingAddress is true. Conditional: When PrefilledFields is not null.  Valid keys (in addition to enabled merchant custom field names):  - `allowed_payment_methods`: Comma-separated list of payment-method keys the session permits (e.g. `card,ach`). A directive, not a form field: it narrows the page's rendered payment methods for this session (intersection with the page's configured + capability-filtered methods). Empty or absent means no session-level restriction. Values must be recognized payment-method keys; session creation rejects unrecognized entries - `base_amount`: Base transaction amount - `billing_address1`: Billing address line 1 - `billing_address2`: Billing address line 2 - `billing_city`: Billing city - `billing_country`: Billing country - `billing_first_name`: Billing first name - `billing_last_name`: Billing last name - `billing_phone`: Billing phone number - `billing_state`: Billing state or province - `billing_zip`: Billing ZIP or postal code - `convenience_fee`: Convenience fee amount. Pins the fee on a session whose amount mode is Locked; on any other amount mode the page ignores it and applies the merchant's configured fee - `customer_email`: Customer email address - `invoice_number`: Invoice number - `order_summary`: JSON-serialized order summary containing line items and totals. Used to render a detailed summary panel on the payment page. Value must be a valid `HppOrderSummaryDto` JSON string - `po_number`: Customer code / purchase order number. Applied to `Level2Data.PoNumber` (max 25 characters, alphanumeric), the same slot the Virtual Terminal writes. Gated by the page's `FieldPurchaseOrder` visibility exactly as `InvoiceNumber` is gated by `FieldInvoice` - `promo_code`: A promotion code the link carries, so a merchant can send a payer a link that arrives with the discount already in the box. It is a <b>suggestion</b>, not a grant: the surfaces still evaluate it server-side against the promotion the merchant owns, and a code that no longer applies is refused exactly as a typed one is. Nothing about a prefilled code is trusted, which is why it can safely ride a URL a payer can edit - `shipping`: Shipping amount - `shipping_address1`: Shipping address line 1 - `shipping_address2`: Shipping address line 2 - `shipping_city`: Shipping city - `shipping_country`: Shipping country - `shipping_first_name`: Shipping first name - `shipping_last_name`: Shipping last name - `shipping_phone`: Shipping phone number - `shipping_state`: Shipping state or province - `shipping_zip`: Shipping ZIP or postal code - `suppress_convenience_fee`: Declares that this session authoritatively carries <b>no</b> convenience fee, so the public surfaces must neither seed one from the merchant's convenience-fee configuration nor recompute a percentage one, and must render no fee line and no disclosure block. A directive, not a form field: it states a producer's decision rather than a value the payer sees. Must be one of the `HppConvenienceFeeSuppressionPolicy` values (`true`, `false`); session creation rejects anything else. Absent means no declaration, which is the pre-directive behaviour: the surfaces resolve the fee themselves - `surcharge`: Surcharge amount - `tax`: Tax amount - `tip`: Tip amount - `vault_restriction`: Vault-restriction directive controlling how the payment instrument may be stored for this session. Must be one of the `VaultRestrictionValues` constants (`any`, `allow_new_and_saved`, `saved_only`). A directive, not a form field. Absent means no restriction (`any`) (nullable)
- `prefilledListFields` (object): Optional list-valued prefills for the merchant's multi-value custom fields, keyed by the  custom-field name. Each key must name a merchant custom field flagged multi-value; a key that  names a single-value field, a well-known field, a blocked key, or a key also present in  `prefilledFields` is rejected. The list is checked against the field's own rules  (value cap, maximum length per item, regular expression) at session create, and every item  must be non-empty. The payer sees the values read-only, one per line, and the resulting  transaction carries them as `customFields[].values`. A field configured to accept a  value from this API without showing it to the payer is accepted here too and stamped  server-side. Conditional: When PrefilledListFields is not null. (nullable)
- `expirySeconds` (integer(int32)): Requested session TTL in seconds. If `null`, the default for the resolved  `linkLifetime` is used. Must be within the bounds that lifetime allows, and must  be omitted entirely for `Permanent`, which has no expiry to set. Conditional: When ExpirySeconds is not null. (nullable)
- `linkLifetime` ([HppLinkLifetime](https://devportal.demo.winkpg.io/docs/api/models/HppLinkLifetime.md)): How long this payment link stays payable.  `Session` (the default) is the historical short single-use  checkout session: 60 to 900 seconds, defaulting to 600, still capped by the tenant's own TTL  settings. `Extended` widens the window to 1 hour through 90 days  for a link that is emailed or texted and paid later. `Permanent`  mints a link with no expiry at all, which stops being payable only when it is paid,  cancelled, or revoked.
  Values:
  - `Session`: The historical short single-use checkout session: 60 to 900 seconds, defaulting to 600, and still subject to the tenant's own min/max/default TTL settings. What every caller that does not name a lifetime gets.
  - `Extended`: A long-lived link, bounded by ExtendedMinTtlSeconds and ExtendedMaxTtlSeconds (1 hour to 90 days), for a link that is emailed or texted and paid later. The upper bound is capped by the tenant's HostedPaymentPageSettings.SessionMaxExtendedTtlSeconds setting.
  - `Permanent`: A link that never expires on its own. The session carries no expiry instant and no document time-to-live; it stops being payable only when it is paid, cancelled, or revoked. Requesting an expiry in seconds alongside this is rejected rather than ignored.
- `correlationId` (string): Optional merchant-supplied correlation ID for tracing/debugging. Conditional: When CorrelationId is not null. Max length: 200. (nullable, max length 200)
- `label` (string): Optional human-readable label for identifying this session (e.g., "Invoice #1234"). Conditional: When Label is not null. Max length: 100. (nullable)
- `cancelUrl` (string): Optional address the payer's Back link returns to from the payment form, before paying, such as  your cart page. Overrides the page's own `PageActions.Cancel` address for this session. Conditional: When CancelUrl is not empty. Max length: 200. (nullable)
- `inactiveMessage` (string): Optional plain-text message a payer sees instead of the generic out-of-service copy once this  link is paused, or once a reusable link has filled its completion cap. (nullable, max length 500)
- `idempotencyKey` (string): Optional caller-supplied key that makes this create safe to retry. Send the same key with the  same request body and the gateway returns the session it already opened instead of opening a  second one, so a timeout or a network retry cannot leave you with two payment links for one  order. Conditional: When IdempotencyKey is not empty. (nullable)
- `requestedCredentialStorage` ([StoredCredentialUsage](https://devportal.demo.winkpg.io/docs/api/models/StoredCredentialUsage.md)): Optional. Declares that the merchant intends to capture stored-credential consent on  this session. When set, the HPP checkout page renders the consent prompt; whether the  cardholder must tick it before submitting is governed by `requireConsent` (or  the tenant default when that is unset). `null` or  `None` leaves the prompt off. The flags describe the  permitted usage scopes the cardholder is being asked to consent to (`Recurring`,  `Installment`, `UnscheduledCOF`, `OneTimeFuture`) and are persisted on the  resulting `StoredCredentialConsent` record. Values combine: send one or more member names separated by a comma and a space, or the integer sum of their values. Responses carry the names. (nullable)
  Values:
  - `None`: No permitted uses. Consent record is effectively inactive.
  - `Recurring`: Scheduled recurring charges (subscriptions, membership dues). Maps to Recurring.
  - `Installment`: Fixed-schedule installment plans dividing a known total. Maps to Installment.
  - `UnscheduledCOF`: Merchant-triggered charges for pre-agreed events (balance top-ups, usage overages, no-show fees, delayed charges). Maps to several MIT reason codes including UnscheduledCOF, NoShow, DelayedCharge, and IncrementalAuth.
  - `OneTimeFuture`: A single known future cardholder-initiated purchase the credential was saved for (e.g. "save card for my next order"). Not used for MITs.
- `requireConsent` (boolean): Optional per-session override for whether ticking the stored-credential consent box is  mandatory before the payment can be submitted. `null` (the default) inherits the  tenant-level `StoredCredentialConsents.RequireConsent` setting, preserving historical  behavior. `true` forces consent to be required for this session (the cardholder must  tick the box to pay); `false` makes it optional (the box is still shown, but the  cardholder may submit a normal sale without ticking it: no token is vaulted and no consent  record is written when they decline). (nullable)
- `declineRetryMode` ([HppDeclineRetryMode](https://devportal.demo.winkpg.io/docs/api/models/HppDeclineRetryMode.md)): Optional per-session override of what this payment link does when the processor declines a  payment. `null` (the default) resolves the policy from the targeted hosted page and  then the tenant default, which is the historical single-use behavior. Conditional: When DeclineRetryMode is not null. (nullable)
  Values:
  - `ConsumeOnDecline`: The historical behavior, and the default at every level of the resolution chain. Any processor answer spends the link permanently: the session stays Consumed and a payer who reloads it is told the link has already been used.
  - `ReleaseOnDecline`: A definite issuer decline hands the link back to Pending so the payer can correct their card and resubmit on the same URL, instead of having to ask the merchant for a new link.
- `tokenizeOnPayment` (boolean): Optional: declares whether the cardholder's card should be vaulted as a reusable  `PaymentToken` when this session's payment succeeds. `null` (the default) defers  to the merchant's `MerchantFeatureSettings.HppTokenizeOnPaymentDefault` flag;  `true` forces tokenization on for this session; `false` forces it off regardless  of the merchant default. (nullable)
- `saveCardOnly` (boolean): Optional: when `true`, this session saves the cardholder's card <b>without</b>  charging it: the gateway runs a zero-dollar account verification (authorize $0 +  AVS/CVV, immediate void) instead of a payment, then vaults the card and captures  consent. Used for "save card now, charge later" onboarding / add-card-to-wallet.  Default `false` (a normal chargeable session). (nullable)
- `parentOrigin` (string): Optional HTTPS origin (scheme + host, e.g. `https://shop.example.com`) of the parent  page that will embed this HPP session in an iframe. When set, the HPP page emits  `postMessage` lifecycle events (`ready`, `session_loaded`,  `payment_succeeded`, etc.) using this value as the `targetOrigin`; never `'*'`.  The host portion must match an entry in the resolved page's `AllowedEmbeddingDomains`  (wildcard subdomains supported). When `null`, postMessage emission is disabled  (fail-closed) and the iframe still works for non-embedded use. Conditional: When ParentOrigin is not empty. Max length: 2048. Conditional: When HostChannel == NativeWebView. (nullable)
- `enableFieldEvents` (boolean): Optional opt-in to per-field `field_focused` / `field_blurred` postMessage  events. When `true`, the iframe attaches a single delegated  focus/blur listener on the form root and emits one envelope per focus boundary with the  element's `data-hpp-field` identifier. <strong>Field values are NEVER included.</strong>  When `false` (the default) no listener is registered, so there is no JS boundary at  which a value could leak. Requires `parentOrigin` to be set: otherwise the  emitter is fail-closed and emits nothing regardless of this flag.
- `presentationMode` ([HppPresentationMode](https://devportal.demo.winkpg.io/docs/api/models/HppPresentationMode.md)): How the public page presents itself for this session.  `Standalone` (the default) renders the full standalone page  exactly as today. `Embedded` renders a chrome-less compact  surface intended for mounting inside a merchant-site iframe: no standalone page background or  full-viewport height, tight paddings, and narrow-width behavior that stays readable at 320 px  without horizontal scrolling.
  Values:
  - `Standalone`: The default, historical behavior: a full standalone page with the surface background, full-viewport minimum height, and the standard outer page padding. null (legacy sessions) resolves here.
  - `Embedded`: A chrome-less compact presentation for mounting inside a merchant-site iframe: no standalone page surface or full-viewport height, tight paddings, and narrow-width behavior that stays readable down to a 320 px viewport without horizontal scrolling.
- `hostChannel` ([HppHostChannel](https://devportal.demo.winkpg.io/docs/api/models/HppHostChannel.md)): Where this session delivers its `postMessage` lifecycle envelopes, and where it accepts  commands from. `ParentWindow` (the default) is the historical  iframe integration: envelopes go to `window.parent` targeted at  `parentOrigin`. `NativeWebView` is for a native iOS,  Android or Flutter app that loads the page in a WebView, where envelopes go to the single  message handler the host app installed instead.
  Values:
  - `ParentWindow`: The default, historical behavior: envelopes are posted to window.parent with the session's parent origin as the target origin, and inbound commands arrive as window messages from that same origin. Emission fails closed when the session carries no parent origin. null (legacy sessions) resolves here.
  - `NativeWebView`: The page is a top-level document inside a native app's WebView, so it has no parent window and no parent origin. Envelopes are delivered to the single message handler the host app installed under the protocol's handler name, and commands arrive through the protocol's entry-point function the host app calls. The host app is the trust boundary: there is no origin to validate, and the embedding-domain allow-list does not apply.
- `purpose` ([HppSessionPurpose](https://devportal.demo.winkpg.io/docs/api/models/HppSessionPurpose.md)): Classifies the calling context that produced this session, which selects the allowed  TTL bounds and any other purpose-specific policy. Defaults to  `CheckoutLink`: the historical merchant-facing pay-link  behavior with a 15-minute TTL ceiling.
  Values:
  - `CheckoutLink`: Standard merchant-facing pay-link session. Short TTL (60s to 900s).
  - `Invoice`: Session created by the invoicing module's send flow to back an invoice's pay-link. Long TTL (1h to 90d) so the link survives net-30 invoice terms with margin for late payment.
  - `PaymentMethodCapture`: Session created by the Merchant Billing module to back a stored-payment-method capture link emailed to a merchant. Long TTL (1h to 90d) so a link sent now can be completed days later, on the merchant's own time, rather than synchronously by an operator.
- `level3Data` ([Level3Data](https://devportal.demo.winkpg.io/docs/api/models/Level3Data.md)): Optional Level 3 commercial-card data: header fields (PO/invoice numbers, ship-from /  destination ZIPs, freight/duty/discount) plus a list of line items. When supplied, the  data is persisted on the session, surfaced read-only on the HPP for the cardholder, and  forwarded onto the resulting `Transaction.Level3Data` so the existing TSYS L3  interchange-qualification mapping consumes it unchanged. `null` (the default)  preserves the historical L2-only flow. Conditional: When Level3Data is not null.
  Fields:
  - `shipFromZip` (string): The ZIP code from where the item is shipped. Conditional: When ShipFromZip is not null. Max length: 10. (nullable, max length 10)
  - `destinationZip` (string): The destination ZIP code for the shipment. Conditional: When DestinationZip is not null. Max length: 10. (nullable, max length 10)
  - `destinationCountryCode` (string): The destination country code for the shipment (ISO 3166-1 alpha-3 format). Conditional: When DestinationCountryCode is not empty. Pattern: ^[A-Z]{3}$. (nullable, max length 3)
  - `invoiceNumber` (string): The invoice number associated with the transaction. Conditional: When InvoiceNumber is not null. Max length: 25. (nullable, max length 25)
  - `orderNumber` (string): The order number for the transaction. Conditional: When OrderNumber is not null. Max length: 25. (nullable, max length 25)
  - `dutyAmount` (number(double)): The total duty amount for the transaction. Must be non-negative. Conditional: When DutyAmount is not null. Must be >= 0. (nullable)
  - `freightAmount` (number(double)): The total freight/shipping amount for the transaction. Must be non-negative. Conditional: When FreightAmount is not null. Must be >= 0. (nullable)
  - `discountAmount` (number(double)): The total discount amount applied at the header level. Must be non-negative. Conditional: When DiscountAmount is not null. Must be >= 0. (nullable)
  - `lineItems` (array<[Level3LineItem](https://devportal.demo.winkpg.io/docs/api/models/Level3LineItem.md)>) required: The list of line items associated with the transaction. (nullable, max items 250)
- `pagePurpose` ([HppPagePurpose](https://devportal.demo.winkpg.io/docs/api/models/HppPagePurpose.md)): Optional per-session override for the targeted page's purpose. `null` (the default) uses  the page's own purpose, so existing integrations are unaffected. A non-null value takes  precedence, letting one page back Payment, SaveCard, and SaveCardWithInitialCharge sessions  without maintaining parallel page instances. Conditional: When PagePurpose is not null. (nullable)
  Values:
  - `Payment`: The default, historical behavior: a chargeable payment page. null resolves here, so every pre-existing HPP instance keeps behaving exactly as before.
  - `SaveCard`: A card-capture page: the cardholder's card is validated via a zero-dollar account verification (authorize $0 + AVS/CVV, immediate void), then vaulted with stored-credential consent. No charge is taken. Amount-bearing configuration (tax, shipping, convenience fee, invoice amounts, donations, tip) is irrelevant and is hidden in the builder / rejected by the validator. Requires the merchant's processor to advertise SupportsZeroDollarVerification.
  - `SaveCardWithInitialCharge`: A card-capture page that also charges the first of a recurring schedule. The cardholder's card is charged a real initial sale (the first payment of a referenced reusable recurring plan, framed as the customer-initiated transaction that establishes the credential-on-file agreement), vaulted with Recurring stored-credential consent, and a customer Contract is created for the remaining payments. Like SaveCard, the amount-bearing builder configuration (tax, shipping, convenience, invoice, donations, tip, customer-set amount) is hidden: the charged amount is driven entirely by the inlined recurring definition, not by per-page amount fields. Requires a complete inlined recurring definition (RecurringPlan).
- `recurringPlan` ([HppRecurringPlan](https://devportal.demo.winkpg.io/docs/api/models/HppRecurringPlan.md)): Optional per-session recurring definition, used only when the resolved `pagePurpose`  is `SaveCardWithInitialCharge`; it then overrides the page's inlined  plan. Required (from this override or the page) for that purpose and validated for complete  economics. Supplying it for any other resolved purpose is rejected. Conditional: Conditional (see validator source).
  Fields:
  - `recurringAmount` (number(double)): The amount charged on each recurring payment after the initial charge. (greater than 0)
  - `frequency` ([Frequency](https://devportal.demo.winkpg.io/docs/api/models/Frequency.md)): The frequency unit of the recurring schedule (e.g. Monthly, Weekly).
    Values:
    - `Secondly`
    - `Minutely`
    - `Hourly`
    - `Daily`
    - `Weekly`
    - `Monthly`
    - `Yearly`
  - `interval` (integer(int32)): The interval multiplier for `frequency` (e.g. `Frequency = Monthly` with  `Interval = 3` is every three months). Defaults to 1. (min 1)
  - `numberOfPayments` (integer(int32)): Total number of payments in the schedule, <b>including</b> the initial charge. A Contract  instantiated from this definition covers the payments after the initial charge  (`GetScheduledPaymentCount`). (min 2)
  - `initialChargeAmount` (number(double)): Optional override for the first (initial) charge amount. When `null`, the initial charge  uses `recurringAmount`. Lets a merchant charge a different amount up front (e.g. a  setup fee bundled into payment one) while the recurring schedule stays at  `recurringAmount`. Conditional: When InitialChargeAmount is not null. Must be > 0. (nullable)
- `captureMode` ([HppCaptureMode](https://devportal.demo.winkpg.io/docs/api/models/HppCaptureMode.md)): Optional per-session capture-mode override for the chargeable Payment flow.  `Sale` authorizes and captures now (the default; `null` uses the  page value); `Authorize` authorizes now and defers capture. Only valid  on the Payment purpose; an explicit Authorize override against a card-capture purpose is rejected. Conditional: When CaptureMode is not null. Conditional: When IsCardCapture(PagePurpose) is true. (nullable)
  Values:
  - `Sale`: Authorize and capture funds in a single step (the historical, default behavior). null resolves here, so every pre-existing chargeable page keeps doing an implicit Sale.
  - `Authorize`: Authorize now, capture later (delayed capture). The resulting transaction is an Authorization in an Authorized state, eligible for capture and void per the action matrix; the capture is completed later against the resulting transaction id via the existing capture action path (including the Apple Pay single-use token case).
- `amountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): How this session treats the payment amount, overriding whether the targeted page would let the  cardholder set it. `CustomerEntered` (the default) leaves the  page's own amount behavior in charge; `Suggested` pre-fills an  editable amount (e.g. a suggested donation); `Locked` pre-fills  a read-only amount the cardholder cannot change (e.g. paying an invoice) that the server verifies  on submit. The amount value is supplied via the `base_amount` prefilled field. Conditional: Conditional (see validator source). Must equal CustomerEntered.
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `taxAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): Optional. How this session treats the tax amount, independently of `amountMode`.  Omit it (`null`) and the tax follows `amountMode`, which is the behavior every  integration had before this property existed: a `tax` prefill on a Locked amount is  locked, and on any other amount mode it is a starting value the payer may change.  `Suggested` pre-fills an editable tax from the `tax`  prefilled field; `Locked` pre-fills a read-only tax that the  server charges whatever the payer submits; `CustomerEntered`  leaves the tax to the payer, even on a Locked amount. Conditional: When TaxAmountMode is not null. Conditional: Conditional (see validator source). (nullable)
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `shippingAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): Optional. How this session treats the shipping amount, independently of  `amountMode`. Omit it (`null`) and the shipping amount follows  `amountMode`, exactly as before this property existed.  `Suggested` pre-fills an editable amount from the  `shipping` prefilled field; `Locked` pre-fills a read-only  amount that the server charges whatever the payer submits;  `CustomerEntered` leaves the shipping amount to the payer,  even on a Locked amount. Conditional: When ShippingAmountMode is not null. Conditional: Conditional (see validator source). (nullable)
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `lockShippingAddress` (boolean): Optional. When `true`, the shipping address supplied through the `shipping_*`  prefilled fields is shown to the payer display-only: they cannot edit it on the hosted page,  and the transaction records that address whatever the submit carried. `false` (the  default) keeps the historical behavior, where a prefilled shipping address is a starting value  the payer may change.
- `customerEmailVisibility` ([HppFieldVisibility](https://devportal.demo.winkpg.io/docs/api/models/HppFieldVisibility.md)): Optional per-session override for whether the payer-facing customer-email field is collected.  `null` (the default) inherits the targeted page's own `FieldCustomerEmail`  configuration, which is what every existing integration gets.  `Hidden` drops the field on a page that shows it,  `Optional` shows it without demanding a value, and  `Required` shows it and blocks submit until it is filled, even  on a page that hides it. One page can therefore serve both a known-customer flow and an  anonymous email-matched flow without a second page configuration. Conditional: When CustomerEmailVisibility is not null. (nullable)
  Values:
  - `Hidden`
  - `Optional`
  - `Required`
- `campaignId` (string(uuid)): Optional campaign this payment link belongs to. Omit it and the session inherits the targeted  page's own campaign, so a merchant can attach a page to a campaign once instead of naming the  campaign on every link it issues. Supply a value and it wins over the page's. (nullable)
- `reusable` (boolean): Whether this link accepts many payers rather than one. Defaults to `false`, which is  the single-use link every caller written before this field existed asks for.
- `maxCompletions` (integer(int32)): How many payments a reusable link accepts before it closes, or `null` for a link that  keeps accepting payers until it is revoked or expires. (nullable)
- `currency` (string): Optional ISO 4217 currency code this link is denominated in, for example `USD`. Omit it  and the session takes the merchant's own configured currency, which is what every link got  before this field existed. Either way the created session reports the currency it settled on,  so an integrator can read it back rather than infer it. Conditional: When Currency is not null. (nullable, max length 3)
- `campaignSource` (string): Optional per-channel source tag for this link: `email`, `sms`, `qr`,  `social`, or whatever short label names the channel you are issuing it through. Mint one  link per channel with a different tag and the campaign report compares them side by side. Conditional: When CampaignSource is not null. (nullable, max length 32)
- `products` (array<[HppSessionProductInput](https://devportal.demo.winkpg.io/docs/api/models/HppSessionProductInput.md)>): Optional cart for a page that sells catalog products: the products to start the order at and  their quantities. Each entry must name a product on the page. A quantity of zero leaves an  optional product out; a required product cannot be left out. Products the cart does not  mention start at the page's default quantity. Rejected on a page that sells no products. Conditional: When Products is not null. (nullable, max items 50)
  Fields of each item:
  - `productId` (string(uuid)) required: The catalog product, which must be one the page sells.
  - `quantity` (number(double)): The quantity to start the order at. Zero leaves an optional product out of the order (the  payer can add it back); a required product needs a quantity inside its limits. (min 0, max 9999)

_Example: Basic checkout session_

The simplest session: a single-use pay link for the targeted hosted page. The page's own configuration decides everything else (amount fields, capture mode, card storage). The label and correlation id are optional merchant-side references for tracing. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Prefilled checkout session_

Pre-fills form fields so the customer only enters card details. Use the well-known field keys (base_amount, invoice_number, customer_email, billing/shipping address fields) or an enabled merchant custom field name. A well-known key is rejected when its field is hidden on the targeted page's configuration (base_amount is always accepted); unrecognized keys are ignored when the form renders. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Lock shipping address_

For a cart that already collected the shipping address and priced shipping from it. lockShippingAddress shows the prefilled shipping address display-only, with a note to return to the cart to change it, and the transaction records that address whatever the submit carried. It requires non-blank shipping_address1 and shipping_zip prefilled fields and a page that shows the shipping address. "Same as billing" is not offered, and a digital wallet is not asked for a delivery address. To change the address, create a new session. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": true
}
```

_Example: Retry-safe session for an order_

Adds an idempotencyKey so the call is safe to resend. The same key with the same body returns the session already opened, with the same session id, link and expiry, instead of opening a second one; the response's idempotencyStatus reports Replayed when that happens, and KeyIgnored when create idempotency is not yet enabled for the merchant. Reusing the key for a different body is refused, so derive it from the thing you are collecting payment for. Up to 128 characters of letters, digits and '.', '_', ':' or '-'. It is a body field: no Idempotency-Key header is read.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Embedded iframe checkout_

A session embedded in an iframe on the merchant's site. parentOrigin must be a strict HTTPS origin (scheme + host, no path) whose host matches the page's allowed embedding domains; it enables postMessage lifecycle events (ready, payment_succeeded, etc.). enableFieldEvents opts into per-field focus/blur events (field identifiers only, never values). presentationMode=Embedded drops the standalone page chrome (surface, full-viewport height, page gutters) so the form sits flush inside the frame and stays readable down to 320 px; it is chrome only, so the flow, the collected fields and every compliance disclosure are unchanged, and it is independent of parentOrigin (an iframe that wants no postMessage events may set it alone). expirySeconds must stay within the purpose's TTL bounds (60 to 900 seconds for the default CheckoutLink purpose). Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": true,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Charge and store the card (card on file)_

Charges the customer and vaults the card as a reusable payment token when the payment succeeds. requestedCredentialStorage renders the consent prompt on the checkout page and records the consented usage scopes; UnscheduledCOF permits later merchant-initiated charges for pre-agreed events (top-ups, no-show fees, delayed charges). If tokenizeOnPayment is true with no declared scope, the scope defaults to UnscheduledCOF server-side. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Charge and store for recurring billing (bound customer)_

Vaults the card with consent scoped to both scheduled recurring charges and unscheduled card-on-file charges. The flags combine, so declare every scope the stored credential will be used under: a later merchant-initiated charge is rejected unless its reason maps to a consented scope. customerId binds the session to a known customer so the stored credential and consent land on exactly that record instead of an email-based find-or-create; the id must be an active customer under the page's merchant. requireConsent true forces the consent box to be mandatory: submit is blocked until the cardholder ticks it, regardless of the tenant StoredCredentialConsents.RequireConsent default. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": true,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Offer optional card saving_

Shows the save-card box but leaves it optional: requireConsent false renders the prompt yet lets the cardholder decline. Declining completes a normal sale with no token vaulted and no consent record written; ticking it vaults the card for later unscheduled card-on-file charges. Omitting requireConsent instead inherits the tenant StoredCredentialConsents.RequireConsent setting. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": false,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Never store the card_

Forces tokenization off for this session regardless of the merchant's tokenize-on-payment default. Use for one-off payments where a stored credential is not wanted. Leaving tokenizeOnPayment null instead defers to the merchant default. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": false,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Save a card without charging (save card only)_

Stores the card with no charge: the gateway runs a zero-dollar account verification (authorize $0 with AVS/CVV, immediate void), vaults the card, and captures consent. Requires the merchant's processor to support zero-dollar verification; the request is rejected at creation otherwise. Must not be combined with a non-zero base_amount prefill. The consent scope defaults to UnscheduledCOF when none is declared. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Save Card purpose override on a Payment page_

Runs a save-card-only flow against a page whose own purpose is Payment, so one page can back both checkout and card-capture sessions without a second page instance. The resolved purpose drives everything downstream (zero-dollar verification, consent, tokenization), and the same fail-closed checks a dedicated Save Card page enforces are re-checked at session create. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
}
```

_Example: Save card with initial charge (subscription enrollment)_

Charges the first payment of a recurring schedule as a real sale, vaults the card with Recurring consent, and creates a contract for the remaining payments. The recurring plan override is required for this purpose and must have complete economics: a positive recurring amount, an interval of at least 1, and at least two total payments (the initial charge plus one recurring payment). initialChargeAmount optionally overrides payment one (for example a bundled setup fee); the target page must accept card only. Recurring consent is always captured because the initial charge depends on it; a requestedCredentialStorage sent alongside is unioned in rather than replacing Recurring, so declaring UnscheduledCOF here lets the vaulted card also authorize later ad-hoc merchant-initiated charges beyond the schedule. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
}
```

_Example: Authorize now, capture later_

Overrides the capture mode for this session: the payment is authorized in full but not captured, leaving an Authorization eligible for a later capture or void through the standard transaction actions. Only valid for the chargeable Payment purpose. A null captureMode inherits the page's configured mode; send Sale explicitly to force authorize-and-capture on a page configured for delayed capture. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
}
```

_Example: Fixed amount the customer cannot change (invoice)_

Locks the amount for this session: base_amount is required and renders read-only on the page, and the server verifies the submitted amount matches before creating the transaction. Use for paying an exact invoice. Amount mode is only valid on the chargeable Payment flow; it is rejected on a save-card / card-capture session. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
}
```

_Example: Suggested amount the customer can change (donation)_

Pre-fills base_amount as a suggested amount but leaves it editable, so the cardholder can accept it or enter their own. Use for a suggested donation. base_amount is required for Suggested mode. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
}
```

_Example: Cart-priced tax and shipping on an editable amount_

Your checkout already priced the tax and the delivery, so both are locked: they render read-only and the server charges the session's tax and shipping whatever the page submits. The amount itself stays a suggestion the payer may change. A locked shipping amount wins over the page's flat rate, and zero is free shipping. Omit taxAmountMode or shippingAmountMode and that fee follows amountMode, as it always has. lockShippingAddress fixes the address the shipping was priced for. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": true
}
```

_Example: Link denominated in an explicit currency_

Names the currency the link is denominated in instead of leaving it implied. Only the merchant's own configured currency is accepted: a different code is rejected at create with HostedPaymentPage:HppSession:CurrencyNotSupported rather than accepted and then ignored at charge time. Omit the field and the session takes the merchant's currency anyway; either way the created session reports the currency it settled on in its effective block, so it can be read back rather than inferred. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
}
```

_Example: Cart on a page that sells catalog products_

Starts the order on a page that sells catalog products at chosen quantities: two of the first product, and the optional add-on left out (quantity 0). Each entry must name a product the page sells. Quantities only: the prices are snapshotted from the catalog when this link is created, the amount mode is read as Locked on the server-computed subtotal, a supplied base_amount is overwritten, and the customer's final quantities are priced again at submit. Omit the field to start at the page's default quantities. Replace hostedPageId with the id of your page and each productId with a product it sells.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
}
```

**Content type:** `text/json`

Schema: [`CreateHppSessionInput`](https://devportal.demo.winkpg.io/docs/api/models/CreateHppSessionInput.md)

Properties:
- `hostedPageId` (string(uuid)) required: The HPP configuration ID this session targets. Required.
- `customerId` (string(uuid)): Optional trusted customer id the merchant binds to this session. When supplied, the  session relates to exactly this one customer: customer resolution at consent-capture  time uses this id exclusively and never falls back to the ambiguous cardholder-email  find-or-create, and the resulting transaction's `InvoiceData.CustomerId` is stamped  from it server-side. The id is validated at session-create time to exist, be active, and  belong to the session's merchant; a missing, inactive/soft-deleted, or cross-merchant id  is rejected. `null` (the default) preserves the historical anonymous behavior where  the customer is found-or-created from the cardholder email collected on the form. (nullable)
- `prefilledFields` (object): Pre-populated field key/value pairs used to pre-fill the payment form. Use one of the well-known  field keys listed below, or an enabled merchant custom field name. Keys reserved for sensitive card  data or internal identifiers are always rejected.     Handling of an unrecognized custom-field key depends on whether the target page scopes its custom  fields (see the hosted page's custom-field allow-list). When the page does NOT scope its custom  fields, an unrecognized key is accepted but ignored when the form is rendered (unchanged behavior).  When the page DOES scope its custom fields, a key that is not a well-known field and not one of the  page's allowed custom fields is rejected at session create with an error naming the offending key.       A custom field the merchant has configured to accept a value from this API without showing it to  the payer is also accepted here. Its value is applied to the resulting transaction server-side,  exactly as supplied, and the field is not rendered on the payment form. Because the payer never  sees it and nobody can correct it later, the value is validated against the field's own rules  (maximum length, regular expression, numeric range) at session create: a value that breaks one of  them is rejected here rather than being carried and dropped at payment time. Conditional: Conditional (see validator source). Conditional: When AmountMode == Suggested. Conditional: When RequiresPrefill(TaxAmountMode) is true. Conditional: When RequiresPrefill(ShippingAmountMode) is true. Conditional: When LockShippingAddress is true. Conditional: When PrefilledFields is not null.  Valid keys (in addition to enabled merchant custom field names):  - `allowed_payment_methods`: Comma-separated list of payment-method keys the session permits (e.g. `card,ach`). A directive, not a form field: it narrows the page's rendered payment methods for this session (intersection with the page's configured + capability-filtered methods). Empty or absent means no session-level restriction. Values must be recognized payment-method keys; session creation rejects unrecognized entries - `base_amount`: Base transaction amount - `billing_address1`: Billing address line 1 - `billing_address2`: Billing address line 2 - `billing_city`: Billing city - `billing_country`: Billing country - `billing_first_name`: Billing first name - `billing_last_name`: Billing last name - `billing_phone`: Billing phone number - `billing_state`: Billing state or province - `billing_zip`: Billing ZIP or postal code - `convenience_fee`: Convenience fee amount. Pins the fee on a session whose amount mode is Locked; on any other amount mode the page ignores it and applies the merchant's configured fee - `customer_email`: Customer email address - `invoice_number`: Invoice number - `order_summary`: JSON-serialized order summary containing line items and totals. Used to render a detailed summary panel on the payment page. Value must be a valid `HppOrderSummaryDto` JSON string - `po_number`: Customer code / purchase order number. Applied to `Level2Data.PoNumber` (max 25 characters, alphanumeric), the same slot the Virtual Terminal writes. Gated by the page's `FieldPurchaseOrder` visibility exactly as `InvoiceNumber` is gated by `FieldInvoice` - `promo_code`: A promotion code the link carries, so a merchant can send a payer a link that arrives with the discount already in the box. It is a <b>suggestion</b>, not a grant: the surfaces still evaluate it server-side against the promotion the merchant owns, and a code that no longer applies is refused exactly as a typed one is. Nothing about a prefilled code is trusted, which is why it can safely ride a URL a payer can edit - `shipping`: Shipping amount - `shipping_address1`: Shipping address line 1 - `shipping_address2`: Shipping address line 2 - `shipping_city`: Shipping city - `shipping_country`: Shipping country - `shipping_first_name`: Shipping first name - `shipping_last_name`: Shipping last name - `shipping_phone`: Shipping phone number - `shipping_state`: Shipping state or province - `shipping_zip`: Shipping ZIP or postal code - `suppress_convenience_fee`: Declares that this session authoritatively carries <b>no</b> convenience fee, so the public surfaces must neither seed one from the merchant's convenience-fee configuration nor recompute a percentage one, and must render no fee line and no disclosure block. A directive, not a form field: it states a producer's decision rather than a value the payer sees. Must be one of the `HppConvenienceFeeSuppressionPolicy` values (`true`, `false`); session creation rejects anything else. Absent means no declaration, which is the pre-directive behaviour: the surfaces resolve the fee themselves - `surcharge`: Surcharge amount - `tax`: Tax amount - `tip`: Tip amount - `vault_restriction`: Vault-restriction directive controlling how the payment instrument may be stored for this session. Must be one of the `VaultRestrictionValues` constants (`any`, `allow_new_and_saved`, `saved_only`). A directive, not a form field. Absent means no restriction (`any`) (nullable)
- `prefilledListFields` (object): Optional list-valued prefills for the merchant's multi-value custom fields, keyed by the  custom-field name. Each key must name a merchant custom field flagged multi-value; a key that  names a single-value field, a well-known field, a blocked key, or a key also present in  `prefilledFields` is rejected. The list is checked against the field's own rules  (value cap, maximum length per item, regular expression) at session create, and every item  must be non-empty. The payer sees the values read-only, one per line, and the resulting  transaction carries them as `customFields[].values`. A field configured to accept a  value from this API without showing it to the payer is accepted here too and stamped  server-side. Conditional: When PrefilledListFields is not null. (nullable)
- `expirySeconds` (integer(int32)): Requested session TTL in seconds. If `null`, the default for the resolved  `linkLifetime` is used. Must be within the bounds that lifetime allows, and must  be omitted entirely for `Permanent`, which has no expiry to set. Conditional: When ExpirySeconds is not null. (nullable)
- `linkLifetime` ([HppLinkLifetime](https://devportal.demo.winkpg.io/docs/api/models/HppLinkLifetime.md)): How long this payment link stays payable.  `Session` (the default) is the historical short single-use  checkout session: 60 to 900 seconds, defaulting to 600, still capped by the tenant's own TTL  settings. `Extended` widens the window to 1 hour through 90 days  for a link that is emailed or texted and paid later. `Permanent`  mints a link with no expiry at all, which stops being payable only when it is paid,  cancelled, or revoked.
  Values:
  - `Session`: The historical short single-use checkout session: 60 to 900 seconds, defaulting to 600, and still subject to the tenant's own min/max/default TTL settings. What every caller that does not name a lifetime gets.
  - `Extended`: A long-lived link, bounded by ExtendedMinTtlSeconds and ExtendedMaxTtlSeconds (1 hour to 90 days), for a link that is emailed or texted and paid later. The upper bound is capped by the tenant's HostedPaymentPageSettings.SessionMaxExtendedTtlSeconds setting.
  - `Permanent`: A link that never expires on its own. The session carries no expiry instant and no document time-to-live; it stops being payable only when it is paid, cancelled, or revoked. Requesting an expiry in seconds alongside this is rejected rather than ignored.
- `correlationId` (string): Optional merchant-supplied correlation ID for tracing/debugging. Conditional: When CorrelationId is not null. Max length: 200. (nullable, max length 200)
- `label` (string): Optional human-readable label for identifying this session (e.g., "Invoice #1234"). Conditional: When Label is not null. Max length: 100. (nullable)
- `cancelUrl` (string): Optional address the payer's Back link returns to from the payment form, before paying, such as  your cart page. Overrides the page's own `PageActions.Cancel` address for this session. Conditional: When CancelUrl is not empty. Max length: 200. (nullable)
- `inactiveMessage` (string): Optional plain-text message a payer sees instead of the generic out-of-service copy once this  link is paused, or once a reusable link has filled its completion cap. (nullable, max length 500)
- `idempotencyKey` (string): Optional caller-supplied key that makes this create safe to retry. Send the same key with the  same request body and the gateway returns the session it already opened instead of opening a  second one, so a timeout or a network retry cannot leave you with two payment links for one  order. Conditional: When IdempotencyKey is not empty. (nullable)
- `requestedCredentialStorage` ([StoredCredentialUsage](https://devportal.demo.winkpg.io/docs/api/models/StoredCredentialUsage.md)): Optional. Declares that the merchant intends to capture stored-credential consent on  this session. When set, the HPP checkout page renders the consent prompt; whether the  cardholder must tick it before submitting is governed by `requireConsent` (or  the tenant default when that is unset). `null` or  `None` leaves the prompt off. The flags describe the  permitted usage scopes the cardholder is being asked to consent to (`Recurring`,  `Installment`, `UnscheduledCOF`, `OneTimeFuture`) and are persisted on the  resulting `StoredCredentialConsent` record. Values combine: send one or more member names separated by a comma and a space, or the integer sum of their values. Responses carry the names. (nullable)
  Values:
  - `None`: No permitted uses. Consent record is effectively inactive.
  - `Recurring`: Scheduled recurring charges (subscriptions, membership dues). Maps to Recurring.
  - `Installment`: Fixed-schedule installment plans dividing a known total. Maps to Installment.
  - `UnscheduledCOF`: Merchant-triggered charges for pre-agreed events (balance top-ups, usage overages, no-show fees, delayed charges). Maps to several MIT reason codes including UnscheduledCOF, NoShow, DelayedCharge, and IncrementalAuth.
  - `OneTimeFuture`: A single known future cardholder-initiated purchase the credential was saved for (e.g. "save card for my next order"). Not used for MITs.
- `requireConsent` (boolean): Optional per-session override for whether ticking the stored-credential consent box is  mandatory before the payment can be submitted. `null` (the default) inherits the  tenant-level `StoredCredentialConsents.RequireConsent` setting, preserving historical  behavior. `true` forces consent to be required for this session (the cardholder must  tick the box to pay); `false` makes it optional (the box is still shown, but the  cardholder may submit a normal sale without ticking it: no token is vaulted and no consent  record is written when they decline). (nullable)
- `declineRetryMode` ([HppDeclineRetryMode](https://devportal.demo.winkpg.io/docs/api/models/HppDeclineRetryMode.md)): Optional per-session override of what this payment link does when the processor declines a  payment. `null` (the default) resolves the policy from the targeted hosted page and  then the tenant default, which is the historical single-use behavior. Conditional: When DeclineRetryMode is not null. (nullable)
  Values:
  - `ConsumeOnDecline`: The historical behavior, and the default at every level of the resolution chain. Any processor answer spends the link permanently: the session stays Consumed and a payer who reloads it is told the link has already been used.
  - `ReleaseOnDecline`: A definite issuer decline hands the link back to Pending so the payer can correct their card and resubmit on the same URL, instead of having to ask the merchant for a new link.
- `tokenizeOnPayment` (boolean): Optional: declares whether the cardholder's card should be vaulted as a reusable  `PaymentToken` when this session's payment succeeds. `null` (the default) defers  to the merchant's `MerchantFeatureSettings.HppTokenizeOnPaymentDefault` flag;  `true` forces tokenization on for this session; `false` forces it off regardless  of the merchant default. (nullable)
- `saveCardOnly` (boolean): Optional: when `true`, this session saves the cardholder's card <b>without</b>  charging it: the gateway runs a zero-dollar account verification (authorize $0 +  AVS/CVV, immediate void) instead of a payment, then vaults the card and captures  consent. Used for "save card now, charge later" onboarding / add-card-to-wallet.  Default `false` (a normal chargeable session). (nullable)
- `parentOrigin` (string): Optional HTTPS origin (scheme + host, e.g. `https://shop.example.com`) of the parent  page that will embed this HPP session in an iframe. When set, the HPP page emits  `postMessage` lifecycle events (`ready`, `session_loaded`,  `payment_succeeded`, etc.) using this value as the `targetOrigin`; never `'*'`.  The host portion must match an entry in the resolved page's `AllowedEmbeddingDomains`  (wildcard subdomains supported). When `null`, postMessage emission is disabled  (fail-closed) and the iframe still works for non-embedded use. Conditional: When ParentOrigin is not empty. Max length: 2048. Conditional: When HostChannel == NativeWebView. (nullable)
- `enableFieldEvents` (boolean): Optional opt-in to per-field `field_focused` / `field_blurred` postMessage  events. When `true`, the iframe attaches a single delegated  focus/blur listener on the form root and emits one envelope per focus boundary with the  element's `data-hpp-field` identifier. <strong>Field values are NEVER included.</strong>  When `false` (the default) no listener is registered, so there is no JS boundary at  which a value could leak. Requires `parentOrigin` to be set: otherwise the  emitter is fail-closed and emits nothing regardless of this flag.
- `presentationMode` ([HppPresentationMode](https://devportal.demo.winkpg.io/docs/api/models/HppPresentationMode.md)): How the public page presents itself for this session.  `Standalone` (the default) renders the full standalone page  exactly as today. `Embedded` renders a chrome-less compact  surface intended for mounting inside a merchant-site iframe: no standalone page background or  full-viewport height, tight paddings, and narrow-width behavior that stays readable at 320 px  without horizontal scrolling.
  Values:
  - `Standalone`: The default, historical behavior: a full standalone page with the surface background, full-viewport minimum height, and the standard outer page padding. null (legacy sessions) resolves here.
  - `Embedded`: A chrome-less compact presentation for mounting inside a merchant-site iframe: no standalone page surface or full-viewport height, tight paddings, and narrow-width behavior that stays readable down to a 320 px viewport without horizontal scrolling.
- `hostChannel` ([HppHostChannel](https://devportal.demo.winkpg.io/docs/api/models/HppHostChannel.md)): Where this session delivers its `postMessage` lifecycle envelopes, and where it accepts  commands from. `ParentWindow` (the default) is the historical  iframe integration: envelopes go to `window.parent` targeted at  `parentOrigin`. `NativeWebView` is for a native iOS,  Android or Flutter app that loads the page in a WebView, where envelopes go to the single  message handler the host app installed instead.
  Values:
  - `ParentWindow`: The default, historical behavior: envelopes are posted to window.parent with the session's parent origin as the target origin, and inbound commands arrive as window messages from that same origin. Emission fails closed when the session carries no parent origin. null (legacy sessions) resolves here.
  - `NativeWebView`: The page is a top-level document inside a native app's WebView, so it has no parent window and no parent origin. Envelopes are delivered to the single message handler the host app installed under the protocol's handler name, and commands arrive through the protocol's entry-point function the host app calls. The host app is the trust boundary: there is no origin to validate, and the embedding-domain allow-list does not apply.
- `purpose` ([HppSessionPurpose](https://devportal.demo.winkpg.io/docs/api/models/HppSessionPurpose.md)): Classifies the calling context that produced this session, which selects the allowed  TTL bounds and any other purpose-specific policy. Defaults to  `CheckoutLink`: the historical merchant-facing pay-link  behavior with a 15-minute TTL ceiling.
  Values:
  - `CheckoutLink`: Standard merchant-facing pay-link session. Short TTL (60s to 900s).
  - `Invoice`: Session created by the invoicing module's send flow to back an invoice's pay-link. Long TTL (1h to 90d) so the link survives net-30 invoice terms with margin for late payment.
  - `PaymentMethodCapture`: Session created by the Merchant Billing module to back a stored-payment-method capture link emailed to a merchant. Long TTL (1h to 90d) so a link sent now can be completed days later, on the merchant's own time, rather than synchronously by an operator.
- `level3Data` ([Level3Data](https://devportal.demo.winkpg.io/docs/api/models/Level3Data.md)): Optional Level 3 commercial-card data: header fields (PO/invoice numbers, ship-from /  destination ZIPs, freight/duty/discount) plus a list of line items. When supplied, the  data is persisted on the session, surfaced read-only on the HPP for the cardholder, and  forwarded onto the resulting `Transaction.Level3Data` so the existing TSYS L3  interchange-qualification mapping consumes it unchanged. `null` (the default)  preserves the historical L2-only flow. Conditional: When Level3Data is not null.
  Fields:
  - `shipFromZip` (string): The ZIP code from where the item is shipped. Conditional: When ShipFromZip is not null. Max length: 10. (nullable, max length 10)
  - `destinationZip` (string): The destination ZIP code for the shipment. Conditional: When DestinationZip is not null. Max length: 10. (nullable, max length 10)
  - `destinationCountryCode` (string): The destination country code for the shipment (ISO 3166-1 alpha-3 format). Conditional: When DestinationCountryCode is not empty. Pattern: ^[A-Z]{3}$. (nullable, max length 3)
  - `invoiceNumber` (string): The invoice number associated with the transaction. Conditional: When InvoiceNumber is not null. Max length: 25. (nullable, max length 25)
  - `orderNumber` (string): The order number for the transaction. Conditional: When OrderNumber is not null. Max length: 25. (nullable, max length 25)
  - `dutyAmount` (number(double)): The total duty amount for the transaction. Must be non-negative. Conditional: When DutyAmount is not null. Must be >= 0. (nullable)
  - `freightAmount` (number(double)): The total freight/shipping amount for the transaction. Must be non-negative. Conditional: When FreightAmount is not null. Must be >= 0. (nullable)
  - `discountAmount` (number(double)): The total discount amount applied at the header level. Must be non-negative. Conditional: When DiscountAmount is not null. Must be >= 0. (nullable)
  - `lineItems` (array<[Level3LineItem](https://devportal.demo.winkpg.io/docs/api/models/Level3LineItem.md)>) required: The list of line items associated with the transaction. (nullable, max items 250)
- `pagePurpose` ([HppPagePurpose](https://devportal.demo.winkpg.io/docs/api/models/HppPagePurpose.md)): Optional per-session override for the targeted page's purpose. `null` (the default) uses  the page's own purpose, so existing integrations are unaffected. A non-null value takes  precedence, letting one page back Payment, SaveCard, and SaveCardWithInitialCharge sessions  without maintaining parallel page instances. Conditional: When PagePurpose is not null. (nullable)
  Values:
  - `Payment`: The default, historical behavior: a chargeable payment page. null resolves here, so every pre-existing HPP instance keeps behaving exactly as before.
  - `SaveCard`: A card-capture page: the cardholder's card is validated via a zero-dollar account verification (authorize $0 + AVS/CVV, immediate void), then vaulted with stored-credential consent. No charge is taken. Amount-bearing configuration (tax, shipping, convenience fee, invoice amounts, donations, tip) is irrelevant and is hidden in the builder / rejected by the validator. Requires the merchant's processor to advertise SupportsZeroDollarVerification.
  - `SaveCardWithInitialCharge`: A card-capture page that also charges the first of a recurring schedule. The cardholder's card is charged a real initial sale (the first payment of a referenced reusable recurring plan, framed as the customer-initiated transaction that establishes the credential-on-file agreement), vaulted with Recurring stored-credential consent, and a customer Contract is created for the remaining payments. Like SaveCard, the amount-bearing builder configuration (tax, shipping, convenience, invoice, donations, tip, customer-set amount) is hidden: the charged amount is driven entirely by the inlined recurring definition, not by per-page amount fields. Requires a complete inlined recurring definition (RecurringPlan).
- `recurringPlan` ([HppRecurringPlan](https://devportal.demo.winkpg.io/docs/api/models/HppRecurringPlan.md)): Optional per-session recurring definition, used only when the resolved `pagePurpose`  is `SaveCardWithInitialCharge`; it then overrides the page's inlined  plan. Required (from this override or the page) for that purpose and validated for complete  economics. Supplying it for any other resolved purpose is rejected. Conditional: Conditional (see validator source).
  Fields:
  - `recurringAmount` (number(double)): The amount charged on each recurring payment after the initial charge. (greater than 0)
  - `frequency` ([Frequency](https://devportal.demo.winkpg.io/docs/api/models/Frequency.md)): The frequency unit of the recurring schedule (e.g. Monthly, Weekly).
    Values:
    - `Secondly`
    - `Minutely`
    - `Hourly`
    - `Daily`
    - `Weekly`
    - `Monthly`
    - `Yearly`
  - `interval` (integer(int32)): The interval multiplier for `frequency` (e.g. `Frequency = Monthly` with  `Interval = 3` is every three months). Defaults to 1. (min 1)
  - `numberOfPayments` (integer(int32)): Total number of payments in the schedule, <b>including</b> the initial charge. A Contract  instantiated from this definition covers the payments after the initial charge  (`GetScheduledPaymentCount`). (min 2)
  - `initialChargeAmount` (number(double)): Optional override for the first (initial) charge amount. When `null`, the initial charge  uses `recurringAmount`. Lets a merchant charge a different amount up front (e.g. a  setup fee bundled into payment one) while the recurring schedule stays at  `recurringAmount`. Conditional: When InitialChargeAmount is not null. Must be > 0. (nullable)
- `captureMode` ([HppCaptureMode](https://devportal.demo.winkpg.io/docs/api/models/HppCaptureMode.md)): Optional per-session capture-mode override for the chargeable Payment flow.  `Sale` authorizes and captures now (the default; `null` uses the  page value); `Authorize` authorizes now and defers capture. Only valid  on the Payment purpose; an explicit Authorize override against a card-capture purpose is rejected. Conditional: When CaptureMode is not null. Conditional: When IsCardCapture(PagePurpose) is true. (nullable)
  Values:
  - `Sale`: Authorize and capture funds in a single step (the historical, default behavior). null resolves here, so every pre-existing chargeable page keeps doing an implicit Sale.
  - `Authorize`: Authorize now, capture later (delayed capture). The resulting transaction is an Authorization in an Authorized state, eligible for capture and void per the action matrix; the capture is completed later against the resulting transaction id via the existing capture action path (including the Apple Pay single-use token case).
- `amountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): How this session treats the payment amount, overriding whether the targeted page would let the  cardholder set it. `CustomerEntered` (the default) leaves the  page's own amount behavior in charge; `Suggested` pre-fills an  editable amount (e.g. a suggested donation); `Locked` pre-fills  a read-only amount the cardholder cannot change (e.g. paying an invoice) that the server verifies  on submit. The amount value is supplied via the `base_amount` prefilled field. Conditional: Conditional (see validator source). Must equal CustomerEntered.
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `taxAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): Optional. How this session treats the tax amount, independently of `amountMode`.  Omit it (`null`) and the tax follows `amountMode`, which is the behavior every  integration had before this property existed: a `tax` prefill on a Locked amount is  locked, and on any other amount mode it is a starting value the payer may change.  `Suggested` pre-fills an editable tax from the `tax`  prefilled field; `Locked` pre-fills a read-only tax that the  server charges whatever the payer submits; `CustomerEntered`  leaves the tax to the payer, even on a Locked amount. Conditional: When TaxAmountMode is not null. Conditional: Conditional (see validator source). (nullable)
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `shippingAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): Optional. How this session treats the shipping amount, independently of  `amountMode`. Omit it (`null`) and the shipping amount follows  `amountMode`, exactly as before this property existed.  `Suggested` pre-fills an editable amount from the  `shipping` prefilled field; `Locked` pre-fills a read-only  amount that the server charges whatever the payer submits;  `CustomerEntered` leaves the shipping amount to the payer,  even on a Locked amount. Conditional: When ShippingAmountMode is not null. Conditional: Conditional (see validator source). (nullable)
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `lockShippingAddress` (boolean): Optional. When `true`, the shipping address supplied through the `shipping_*`  prefilled fields is shown to the payer display-only: they cannot edit it on the hosted page,  and the transaction records that address whatever the submit carried. `false` (the  default) keeps the historical behavior, where a prefilled shipping address is a starting value  the payer may change.
- `customerEmailVisibility` ([HppFieldVisibility](https://devportal.demo.winkpg.io/docs/api/models/HppFieldVisibility.md)): Optional per-session override for whether the payer-facing customer-email field is collected.  `null` (the default) inherits the targeted page's own `FieldCustomerEmail`  configuration, which is what every existing integration gets.  `Hidden` drops the field on a page that shows it,  `Optional` shows it without demanding a value, and  `Required` shows it and blocks submit until it is filled, even  on a page that hides it. One page can therefore serve both a known-customer flow and an  anonymous email-matched flow without a second page configuration. Conditional: When CustomerEmailVisibility is not null. (nullable)
  Values:
  - `Hidden`
  - `Optional`
  - `Required`
- `campaignId` (string(uuid)): Optional campaign this payment link belongs to. Omit it and the session inherits the targeted  page's own campaign, so a merchant can attach a page to a campaign once instead of naming the  campaign on every link it issues. Supply a value and it wins over the page's. (nullable)
- `reusable` (boolean): Whether this link accepts many payers rather than one. Defaults to `false`, which is  the single-use link every caller written before this field existed asks for.
- `maxCompletions` (integer(int32)): How many payments a reusable link accepts before it closes, or `null` for a link that  keeps accepting payers until it is revoked or expires. (nullable)
- `currency` (string): Optional ISO 4217 currency code this link is denominated in, for example `USD`. Omit it  and the session takes the merchant's own configured currency, which is what every link got  before this field existed. Either way the created session reports the currency it settled on,  so an integrator can read it back rather than infer it. Conditional: When Currency is not null. (nullable, max length 3)
- `campaignSource` (string): Optional per-channel source tag for this link: `email`, `sms`, `qr`,  `social`, or whatever short label names the channel you are issuing it through. Mint one  link per channel with a different tag and the campaign report compares them side by side. Conditional: When CampaignSource is not null. (nullable, max length 32)
- `products` (array<[HppSessionProductInput](https://devportal.demo.winkpg.io/docs/api/models/HppSessionProductInput.md)>): Optional cart for a page that sells catalog products: the products to start the order at and  their quantities. Each entry must name a product on the page. A quantity of zero leaves an  optional product out; a required product cannot be left out. Products the cart does not  mention start at the page's default quantity. Rejected on a page that sells no products. Conditional: When Products is not null. (nullable, max items 50)
  Fields of each item:
  - `productId` (string(uuid)) required: The catalog product, which must be one the page sells.
  - `quantity` (number(double)): The quantity to start the order at. Zero leaves an optional product out of the order (the  payer can add it back); a required product needs a quantity inside its limits. (min 0, max 9999)

_Example: Basic checkout session_

The simplest session: a single-use pay link for the targeted hosted page. The page's own configuration decides everything else (amount fields, capture mode, card storage). The label and correlation id are optional merchant-side references for tracing. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Prefilled checkout session_

Pre-fills form fields so the customer only enters card details. Use the well-known field keys (base_amount, invoice_number, customer_email, billing/shipping address fields) or an enabled merchant custom field name. A well-known key is rejected when its field is hidden on the targeted page's configuration (base_amount is always accepted); unrecognized keys are ignored when the form renders. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Lock shipping address_

For a cart that already collected the shipping address and priced shipping from it. lockShippingAddress shows the prefilled shipping address display-only, with a note to return to the cart to change it, and the transaction records that address whatever the submit carried. It requires non-blank shipping_address1 and shipping_zip prefilled fields and a page that shows the shipping address. "Same as billing" is not offered, and a digital wallet is not asked for a delivery address. To change the address, create a new session. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": true
}
```

_Example: Retry-safe session for an order_

Adds an idempotencyKey so the call is safe to resend. The same key with the same body returns the session already opened, with the same session id, link and expiry, instead of opening a second one; the response's idempotencyStatus reports Replayed when that happens, and KeyIgnored when create idempotency is not yet enabled for the merchant. Reusing the key for a different body is refused, so derive it from the thing you are collecting payment for. Up to 128 characters of letters, digits and '.', '_', ':' or '-'. It is a body field: no Idempotency-Key header is read.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Embedded iframe checkout_

A session embedded in an iframe on the merchant's site. parentOrigin must be a strict HTTPS origin (scheme + host, no path) whose host matches the page's allowed embedding domains; it enables postMessage lifecycle events (ready, payment_succeeded, etc.). enableFieldEvents opts into per-field focus/blur events (field identifiers only, never values). presentationMode=Embedded drops the standalone page chrome (surface, full-viewport height, page gutters) so the form sits flush inside the frame and stays readable down to 320 px; it is chrome only, so the flow, the collected fields and every compliance disclosure are unchanged, and it is independent of parentOrigin (an iframe that wants no postMessage events may set it alone). expirySeconds must stay within the purpose's TTL bounds (60 to 900 seconds for the default CheckoutLink purpose). Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": true,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Charge and store the card (card on file)_

Charges the customer and vaults the card as a reusable payment token when the payment succeeds. requestedCredentialStorage renders the consent prompt on the checkout page and records the consented usage scopes; UnscheduledCOF permits later merchant-initiated charges for pre-agreed events (top-ups, no-show fees, delayed charges). If tokenizeOnPayment is true with no declared scope, the scope defaults to UnscheduledCOF server-side. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Charge and store for recurring billing (bound customer)_

Vaults the card with consent scoped to both scheduled recurring charges and unscheduled card-on-file charges. The flags combine, so declare every scope the stored credential will be used under: a later merchant-initiated charge is rejected unless its reason maps to a consented scope. customerId binds the session to a known customer so the stored credential and consent land on exactly that record instead of an email-based find-or-create; the id must be an active customer under the page's merchant. requireConsent true forces the consent box to be mandatory: submit is blocked until the cardholder ticks it, regardless of the tenant StoredCredentialConsents.RequireConsent default. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": true,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Offer optional card saving_

Shows the save-card box but leaves it optional: requireConsent false renders the prompt yet lets the cardholder decline. Declining completes a normal sale with no token vaulted and no consent record written; ticking it vaults the card for later unscheduled card-on-file charges. Omitting requireConsent instead inherits the tenant StoredCredentialConsents.RequireConsent setting. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": false,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Never store the card_

Forces tokenization off for this session regardless of the merchant's tokenize-on-payment default. Use for one-off payments where a stored credential is not wanted. Leaving tokenizeOnPayment null instead defers to the merchant default. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": false,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Save a card without charging (save card only)_

Stores the card with no charge: the gateway runs a zero-dollar account verification (authorize $0 with AVS/CVV, immediate void), vaults the card, and captures consent. Requires the merchant's processor to support zero-dollar verification; the request is rejected at creation otherwise. Must not be combined with a non-zero base_amount prefill. The consent scope defaults to UnscheduledCOF when none is declared. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Save Card purpose override on a Payment page_

Runs a save-card-only flow against a page whose own purpose is Payment, so one page can back both checkout and card-capture sessions without a second page instance. The resolved purpose drives everything downstream (zero-dollar verification, consent, tokenization), and the same fail-closed checks a dedicated Save Card page enforces are re-checked at session create. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
}
```

_Example: Save card with initial charge (subscription enrollment)_

Charges the first payment of a recurring schedule as a real sale, vaults the card with Recurring consent, and creates a contract for the remaining payments. The recurring plan override is required for this purpose and must have complete economics: a positive recurring amount, an interval of at least 1, and at least two total payments (the initial charge plus one recurring payment). initialChargeAmount optionally overrides payment one (for example a bundled setup fee); the target page must accept card only. Recurring consent is always captured because the initial charge depends on it; a requestedCredentialStorage sent alongside is unioned in rather than replacing Recurring, so declaring UnscheduledCOF here lets the vaulted card also authorize later ad-hoc merchant-initiated charges beyond the schedule. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
}
```

_Example: Authorize now, capture later_

Overrides the capture mode for this session: the payment is authorized in full but not captured, leaving an Authorization eligible for a later capture or void through the standard transaction actions. Only valid for the chargeable Payment purpose. A null captureMode inherits the page's configured mode; send Sale explicitly to force authorize-and-capture on a page configured for delayed capture. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
}
```

_Example: Fixed amount the customer cannot change (invoice)_

Locks the amount for this session: base_amount is required and renders read-only on the page, and the server verifies the submitted amount matches before creating the transaction. Use for paying an exact invoice. Amount mode is only valid on the chargeable Payment flow; it is rejected on a save-card / card-capture session. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
}
```

_Example: Suggested amount the customer can change (donation)_

Pre-fills base_amount as a suggested amount but leaves it editable, so the cardholder can accept it or enter their own. Use for a suggested donation. base_amount is required for Suggested mode. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
}
```

_Example: Cart-priced tax and shipping on an editable amount_

Your checkout already priced the tax and the delivery, so both are locked: they render read-only and the server charges the session's tax and shipping whatever the page submits. The amount itself stays a suggestion the payer may change. A locked shipping amount wins over the page's flat rate, and zero is free shipping. Omit taxAmountMode or shippingAmountMode and that fee follows amountMode, as it always has. lockShippingAddress fixes the address the shipping was priced for. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": true
}
```

_Example: Link denominated in an explicit currency_

Names the currency the link is denominated in instead of leaving it implied. Only the merchant's own configured currency is accepted: a different code is rejected at create with HostedPaymentPage:HppSession:CurrencyNotSupported rather than accepted and then ignored at charge time. Omit the field and the session takes the merchant's currency anyway; either way the created session reports the currency it settled on in its effective block, so it can be read back rather than inferred. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
}
```

_Example: Cart on a page that sells catalog products_

Starts the order on a page that sells catalog products at chosen quantities: two of the first product, and the optional add-on left out (quantity 0). Each entry must name a product the page sells. Quantities only: the prices are snapshotted from the catalog when this link is created, the amount mode is read as Locked on the server-computed subtotal, a supplied base_amount is overwritten, and the customer's final quantities are priced again at submit. Omit the field to start at the page's default quantities. Replace hostedPageId with the id of your page and each productId with a product it sells.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
}
```

**Content type:** `application/*+json`

Schema: [`CreateHppSessionInput`](https://devportal.demo.winkpg.io/docs/api/models/CreateHppSessionInput.md)

Properties:
- `hostedPageId` (string(uuid)) required: The HPP configuration ID this session targets. Required.
- `customerId` (string(uuid)): Optional trusted customer id the merchant binds to this session. When supplied, the  session relates to exactly this one customer: customer resolution at consent-capture  time uses this id exclusively and never falls back to the ambiguous cardholder-email  find-or-create, and the resulting transaction's `InvoiceData.CustomerId` is stamped  from it server-side. The id is validated at session-create time to exist, be active, and  belong to the session's merchant; a missing, inactive/soft-deleted, or cross-merchant id  is rejected. `null` (the default) preserves the historical anonymous behavior where  the customer is found-or-created from the cardholder email collected on the form. (nullable)
- `prefilledFields` (object): Pre-populated field key/value pairs used to pre-fill the payment form. Use one of the well-known  field keys listed below, or an enabled merchant custom field name. Keys reserved for sensitive card  data or internal identifiers are always rejected.     Handling of an unrecognized custom-field key depends on whether the target page scopes its custom  fields (see the hosted page's custom-field allow-list). When the page does NOT scope its custom  fields, an unrecognized key is accepted but ignored when the form is rendered (unchanged behavior).  When the page DOES scope its custom fields, a key that is not a well-known field and not one of the  page's allowed custom fields is rejected at session create with an error naming the offending key.       A custom field the merchant has configured to accept a value from this API without showing it to  the payer is also accepted here. Its value is applied to the resulting transaction server-side,  exactly as supplied, and the field is not rendered on the payment form. Because the payer never  sees it and nobody can correct it later, the value is validated against the field's own rules  (maximum length, regular expression, numeric range) at session create: a value that breaks one of  them is rejected here rather than being carried and dropped at payment time. Conditional: Conditional (see validator source). Conditional: When AmountMode == Suggested. Conditional: When RequiresPrefill(TaxAmountMode) is true. Conditional: When RequiresPrefill(ShippingAmountMode) is true. Conditional: When LockShippingAddress is true. Conditional: When PrefilledFields is not null.  Valid keys (in addition to enabled merchant custom field names):  - `allowed_payment_methods`: Comma-separated list of payment-method keys the session permits (e.g. `card,ach`). A directive, not a form field: it narrows the page's rendered payment methods for this session (intersection with the page's configured + capability-filtered methods). Empty or absent means no session-level restriction. Values must be recognized payment-method keys; session creation rejects unrecognized entries - `base_amount`: Base transaction amount - `billing_address1`: Billing address line 1 - `billing_address2`: Billing address line 2 - `billing_city`: Billing city - `billing_country`: Billing country - `billing_first_name`: Billing first name - `billing_last_name`: Billing last name - `billing_phone`: Billing phone number - `billing_state`: Billing state or province - `billing_zip`: Billing ZIP or postal code - `convenience_fee`: Convenience fee amount. Pins the fee on a session whose amount mode is Locked; on any other amount mode the page ignores it and applies the merchant's configured fee - `customer_email`: Customer email address - `invoice_number`: Invoice number - `order_summary`: JSON-serialized order summary containing line items and totals. Used to render a detailed summary panel on the payment page. Value must be a valid `HppOrderSummaryDto` JSON string - `po_number`: Customer code / purchase order number. Applied to `Level2Data.PoNumber` (max 25 characters, alphanumeric), the same slot the Virtual Terminal writes. Gated by the page's `FieldPurchaseOrder` visibility exactly as `InvoiceNumber` is gated by `FieldInvoice` - `promo_code`: A promotion code the link carries, so a merchant can send a payer a link that arrives with the discount already in the box. It is a <b>suggestion</b>, not a grant: the surfaces still evaluate it server-side against the promotion the merchant owns, and a code that no longer applies is refused exactly as a typed one is. Nothing about a prefilled code is trusted, which is why it can safely ride a URL a payer can edit - `shipping`: Shipping amount - `shipping_address1`: Shipping address line 1 - `shipping_address2`: Shipping address line 2 - `shipping_city`: Shipping city - `shipping_country`: Shipping country - `shipping_first_name`: Shipping first name - `shipping_last_name`: Shipping last name - `shipping_phone`: Shipping phone number - `shipping_state`: Shipping state or province - `shipping_zip`: Shipping ZIP or postal code - `suppress_convenience_fee`: Declares that this session authoritatively carries <b>no</b> convenience fee, so the public surfaces must neither seed one from the merchant's convenience-fee configuration nor recompute a percentage one, and must render no fee line and no disclosure block. A directive, not a form field: it states a producer's decision rather than a value the payer sees. Must be one of the `HppConvenienceFeeSuppressionPolicy` values (`true`, `false`); session creation rejects anything else. Absent means no declaration, which is the pre-directive behaviour: the surfaces resolve the fee themselves - `surcharge`: Surcharge amount - `tax`: Tax amount - `tip`: Tip amount - `vault_restriction`: Vault-restriction directive controlling how the payment instrument may be stored for this session. Must be one of the `VaultRestrictionValues` constants (`any`, `allow_new_and_saved`, `saved_only`). A directive, not a form field. Absent means no restriction (`any`) (nullable)
- `prefilledListFields` (object): Optional list-valued prefills for the merchant's multi-value custom fields, keyed by the  custom-field name. Each key must name a merchant custom field flagged multi-value; a key that  names a single-value field, a well-known field, a blocked key, or a key also present in  `prefilledFields` is rejected. The list is checked against the field's own rules  (value cap, maximum length per item, regular expression) at session create, and every item  must be non-empty. The payer sees the values read-only, one per line, and the resulting  transaction carries them as `customFields[].values`. A field configured to accept a  value from this API without showing it to the payer is accepted here too and stamped  server-side. Conditional: When PrefilledListFields is not null. (nullable)
- `expirySeconds` (integer(int32)): Requested session TTL in seconds. If `null`, the default for the resolved  `linkLifetime` is used. Must be within the bounds that lifetime allows, and must  be omitted entirely for `Permanent`, which has no expiry to set. Conditional: When ExpirySeconds is not null. (nullable)
- `linkLifetime` ([HppLinkLifetime](https://devportal.demo.winkpg.io/docs/api/models/HppLinkLifetime.md)): How long this payment link stays payable.  `Session` (the default) is the historical short single-use  checkout session: 60 to 900 seconds, defaulting to 600, still capped by the tenant's own TTL  settings. `Extended` widens the window to 1 hour through 90 days  for a link that is emailed or texted and paid later. `Permanent`  mints a link with no expiry at all, which stops being payable only when it is paid,  cancelled, or revoked.
  Values:
  - `Session`: The historical short single-use checkout session: 60 to 900 seconds, defaulting to 600, and still subject to the tenant's own min/max/default TTL settings. What every caller that does not name a lifetime gets.
  - `Extended`: A long-lived link, bounded by ExtendedMinTtlSeconds and ExtendedMaxTtlSeconds (1 hour to 90 days), for a link that is emailed or texted and paid later. The upper bound is capped by the tenant's HostedPaymentPageSettings.SessionMaxExtendedTtlSeconds setting.
  - `Permanent`: A link that never expires on its own. The session carries no expiry instant and no document time-to-live; it stops being payable only when it is paid, cancelled, or revoked. Requesting an expiry in seconds alongside this is rejected rather than ignored.
- `correlationId` (string): Optional merchant-supplied correlation ID for tracing/debugging. Conditional: When CorrelationId is not null. Max length: 200. (nullable, max length 200)
- `label` (string): Optional human-readable label for identifying this session (e.g., "Invoice #1234"). Conditional: When Label is not null. Max length: 100. (nullable)
- `cancelUrl` (string): Optional address the payer's Back link returns to from the payment form, before paying, such as  your cart page. Overrides the page's own `PageActions.Cancel` address for this session. Conditional: When CancelUrl is not empty. Max length: 200. (nullable)
- `inactiveMessage` (string): Optional plain-text message a payer sees instead of the generic out-of-service copy once this  link is paused, or once a reusable link has filled its completion cap. (nullable, max length 500)
- `idempotencyKey` (string): Optional caller-supplied key that makes this create safe to retry. Send the same key with the  same request body and the gateway returns the session it already opened instead of opening a  second one, so a timeout or a network retry cannot leave you with two payment links for one  order. Conditional: When IdempotencyKey is not empty. (nullable)
- `requestedCredentialStorage` ([StoredCredentialUsage](https://devportal.demo.winkpg.io/docs/api/models/StoredCredentialUsage.md)): Optional. Declares that the merchant intends to capture stored-credential consent on  this session. When set, the HPP checkout page renders the consent prompt; whether the  cardholder must tick it before submitting is governed by `requireConsent` (or  the tenant default when that is unset). `null` or  `None` leaves the prompt off. The flags describe the  permitted usage scopes the cardholder is being asked to consent to (`Recurring`,  `Installment`, `UnscheduledCOF`, `OneTimeFuture`) and are persisted on the  resulting `StoredCredentialConsent` record. Values combine: send one or more member names separated by a comma and a space, or the integer sum of their values. Responses carry the names. (nullable)
  Values:
  - `None`: No permitted uses. Consent record is effectively inactive.
  - `Recurring`: Scheduled recurring charges (subscriptions, membership dues). Maps to Recurring.
  - `Installment`: Fixed-schedule installment plans dividing a known total. Maps to Installment.
  - `UnscheduledCOF`: Merchant-triggered charges for pre-agreed events (balance top-ups, usage overages, no-show fees, delayed charges). Maps to several MIT reason codes including UnscheduledCOF, NoShow, DelayedCharge, and IncrementalAuth.
  - `OneTimeFuture`: A single known future cardholder-initiated purchase the credential was saved for (e.g. "save card for my next order"). Not used for MITs.
- `requireConsent` (boolean): Optional per-session override for whether ticking the stored-credential consent box is  mandatory before the payment can be submitted. `null` (the default) inherits the  tenant-level `StoredCredentialConsents.RequireConsent` setting, preserving historical  behavior. `true` forces consent to be required for this session (the cardholder must  tick the box to pay); `false` makes it optional (the box is still shown, but the  cardholder may submit a normal sale without ticking it: no token is vaulted and no consent  record is written when they decline). (nullable)
- `declineRetryMode` ([HppDeclineRetryMode](https://devportal.demo.winkpg.io/docs/api/models/HppDeclineRetryMode.md)): Optional per-session override of what this payment link does when the processor declines a  payment. `null` (the default) resolves the policy from the targeted hosted page and  then the tenant default, which is the historical single-use behavior. Conditional: When DeclineRetryMode is not null. (nullable)
  Values:
  - `ConsumeOnDecline`: The historical behavior, and the default at every level of the resolution chain. Any processor answer spends the link permanently: the session stays Consumed and a payer who reloads it is told the link has already been used.
  - `ReleaseOnDecline`: A definite issuer decline hands the link back to Pending so the payer can correct their card and resubmit on the same URL, instead of having to ask the merchant for a new link.
- `tokenizeOnPayment` (boolean): Optional: declares whether the cardholder's card should be vaulted as a reusable  `PaymentToken` when this session's payment succeeds. `null` (the default) defers  to the merchant's `MerchantFeatureSettings.HppTokenizeOnPaymentDefault` flag;  `true` forces tokenization on for this session; `false` forces it off regardless  of the merchant default. (nullable)
- `saveCardOnly` (boolean): Optional: when `true`, this session saves the cardholder's card <b>without</b>  charging it: the gateway runs a zero-dollar account verification (authorize $0 +  AVS/CVV, immediate void) instead of a payment, then vaults the card and captures  consent. Used for "save card now, charge later" onboarding / add-card-to-wallet.  Default `false` (a normal chargeable session). (nullable)
- `parentOrigin` (string): Optional HTTPS origin (scheme + host, e.g. `https://shop.example.com`) of the parent  page that will embed this HPP session in an iframe. When set, the HPP page emits  `postMessage` lifecycle events (`ready`, `session_loaded`,  `payment_succeeded`, etc.) using this value as the `targetOrigin`; never `'*'`.  The host portion must match an entry in the resolved page's `AllowedEmbeddingDomains`  (wildcard subdomains supported). When `null`, postMessage emission is disabled  (fail-closed) and the iframe still works for non-embedded use. Conditional: When ParentOrigin is not empty. Max length: 2048. Conditional: When HostChannel == NativeWebView. (nullable)
- `enableFieldEvents` (boolean): Optional opt-in to per-field `field_focused` / `field_blurred` postMessage  events. When `true`, the iframe attaches a single delegated  focus/blur listener on the form root and emits one envelope per focus boundary with the  element's `data-hpp-field` identifier. <strong>Field values are NEVER included.</strong>  When `false` (the default) no listener is registered, so there is no JS boundary at  which a value could leak. Requires `parentOrigin` to be set: otherwise the  emitter is fail-closed and emits nothing regardless of this flag.
- `presentationMode` ([HppPresentationMode](https://devportal.demo.winkpg.io/docs/api/models/HppPresentationMode.md)): How the public page presents itself for this session.  `Standalone` (the default) renders the full standalone page  exactly as today. `Embedded` renders a chrome-less compact  surface intended for mounting inside a merchant-site iframe: no standalone page background or  full-viewport height, tight paddings, and narrow-width behavior that stays readable at 320 px  without horizontal scrolling.
  Values:
  - `Standalone`: The default, historical behavior: a full standalone page with the surface background, full-viewport minimum height, and the standard outer page padding. null (legacy sessions) resolves here.
  - `Embedded`: A chrome-less compact presentation for mounting inside a merchant-site iframe: no standalone page surface or full-viewport height, tight paddings, and narrow-width behavior that stays readable down to a 320 px viewport without horizontal scrolling.
- `hostChannel` ([HppHostChannel](https://devportal.demo.winkpg.io/docs/api/models/HppHostChannel.md)): Where this session delivers its `postMessage` lifecycle envelopes, and where it accepts  commands from. `ParentWindow` (the default) is the historical  iframe integration: envelopes go to `window.parent` targeted at  `parentOrigin`. `NativeWebView` is for a native iOS,  Android or Flutter app that loads the page in a WebView, where envelopes go to the single  message handler the host app installed instead.
  Values:
  - `ParentWindow`: The default, historical behavior: envelopes are posted to window.parent with the session's parent origin as the target origin, and inbound commands arrive as window messages from that same origin. Emission fails closed when the session carries no parent origin. null (legacy sessions) resolves here.
  - `NativeWebView`: The page is a top-level document inside a native app's WebView, so it has no parent window and no parent origin. Envelopes are delivered to the single message handler the host app installed under the protocol's handler name, and commands arrive through the protocol's entry-point function the host app calls. The host app is the trust boundary: there is no origin to validate, and the embedding-domain allow-list does not apply.
- `purpose` ([HppSessionPurpose](https://devportal.demo.winkpg.io/docs/api/models/HppSessionPurpose.md)): Classifies the calling context that produced this session, which selects the allowed  TTL bounds and any other purpose-specific policy. Defaults to  `CheckoutLink`: the historical merchant-facing pay-link  behavior with a 15-minute TTL ceiling.
  Values:
  - `CheckoutLink`: Standard merchant-facing pay-link session. Short TTL (60s to 900s).
  - `Invoice`: Session created by the invoicing module's send flow to back an invoice's pay-link. Long TTL (1h to 90d) so the link survives net-30 invoice terms with margin for late payment.
  - `PaymentMethodCapture`: Session created by the Merchant Billing module to back a stored-payment-method capture link emailed to a merchant. Long TTL (1h to 90d) so a link sent now can be completed days later, on the merchant's own time, rather than synchronously by an operator.
- `level3Data` ([Level3Data](https://devportal.demo.winkpg.io/docs/api/models/Level3Data.md)): Optional Level 3 commercial-card data: header fields (PO/invoice numbers, ship-from /  destination ZIPs, freight/duty/discount) plus a list of line items. When supplied, the  data is persisted on the session, surfaced read-only on the HPP for the cardholder, and  forwarded onto the resulting `Transaction.Level3Data` so the existing TSYS L3  interchange-qualification mapping consumes it unchanged. `null` (the default)  preserves the historical L2-only flow. Conditional: When Level3Data is not null.
  Fields:
  - `shipFromZip` (string): The ZIP code from where the item is shipped. Conditional: When ShipFromZip is not null. Max length: 10. (nullable, max length 10)
  - `destinationZip` (string): The destination ZIP code for the shipment. Conditional: When DestinationZip is not null. Max length: 10. (nullable, max length 10)
  - `destinationCountryCode` (string): The destination country code for the shipment (ISO 3166-1 alpha-3 format). Conditional: When DestinationCountryCode is not empty. Pattern: ^[A-Z]{3}$. (nullable, max length 3)
  - `invoiceNumber` (string): The invoice number associated with the transaction. Conditional: When InvoiceNumber is not null. Max length: 25. (nullable, max length 25)
  - `orderNumber` (string): The order number for the transaction. Conditional: When OrderNumber is not null. Max length: 25. (nullable, max length 25)
  - `dutyAmount` (number(double)): The total duty amount for the transaction. Must be non-negative. Conditional: When DutyAmount is not null. Must be >= 0. (nullable)
  - `freightAmount` (number(double)): The total freight/shipping amount for the transaction. Must be non-negative. Conditional: When FreightAmount is not null. Must be >= 0. (nullable)
  - `discountAmount` (number(double)): The total discount amount applied at the header level. Must be non-negative. Conditional: When DiscountAmount is not null. Must be >= 0. (nullable)
  - `lineItems` (array<[Level3LineItem](https://devportal.demo.winkpg.io/docs/api/models/Level3LineItem.md)>) required: The list of line items associated with the transaction. (nullable, max items 250)
- `pagePurpose` ([HppPagePurpose](https://devportal.demo.winkpg.io/docs/api/models/HppPagePurpose.md)): Optional per-session override for the targeted page's purpose. `null` (the default) uses  the page's own purpose, so existing integrations are unaffected. A non-null value takes  precedence, letting one page back Payment, SaveCard, and SaveCardWithInitialCharge sessions  without maintaining parallel page instances. Conditional: When PagePurpose is not null. (nullable)
  Values:
  - `Payment`: The default, historical behavior: a chargeable payment page. null resolves here, so every pre-existing HPP instance keeps behaving exactly as before.
  - `SaveCard`: A card-capture page: the cardholder's card is validated via a zero-dollar account verification (authorize $0 + AVS/CVV, immediate void), then vaulted with stored-credential consent. No charge is taken. Amount-bearing configuration (tax, shipping, convenience fee, invoice amounts, donations, tip) is irrelevant and is hidden in the builder / rejected by the validator. Requires the merchant's processor to advertise SupportsZeroDollarVerification.
  - `SaveCardWithInitialCharge`: A card-capture page that also charges the first of a recurring schedule. The cardholder's card is charged a real initial sale (the first payment of a referenced reusable recurring plan, framed as the customer-initiated transaction that establishes the credential-on-file agreement), vaulted with Recurring stored-credential consent, and a customer Contract is created for the remaining payments. Like SaveCard, the amount-bearing builder configuration (tax, shipping, convenience, invoice, donations, tip, customer-set amount) is hidden: the charged amount is driven entirely by the inlined recurring definition, not by per-page amount fields. Requires a complete inlined recurring definition (RecurringPlan).
- `recurringPlan` ([HppRecurringPlan](https://devportal.demo.winkpg.io/docs/api/models/HppRecurringPlan.md)): Optional per-session recurring definition, used only when the resolved `pagePurpose`  is `SaveCardWithInitialCharge`; it then overrides the page's inlined  plan. Required (from this override or the page) for that purpose and validated for complete  economics. Supplying it for any other resolved purpose is rejected. Conditional: Conditional (see validator source).
  Fields:
  - `recurringAmount` (number(double)): The amount charged on each recurring payment after the initial charge. (greater than 0)
  - `frequency` ([Frequency](https://devportal.demo.winkpg.io/docs/api/models/Frequency.md)): The frequency unit of the recurring schedule (e.g. Monthly, Weekly).
    Values:
    - `Secondly`
    - `Minutely`
    - `Hourly`
    - `Daily`
    - `Weekly`
    - `Monthly`
    - `Yearly`
  - `interval` (integer(int32)): The interval multiplier for `frequency` (e.g. `Frequency = Monthly` with  `Interval = 3` is every three months). Defaults to 1. (min 1)
  - `numberOfPayments` (integer(int32)): Total number of payments in the schedule, <b>including</b> the initial charge. A Contract  instantiated from this definition covers the payments after the initial charge  (`GetScheduledPaymentCount`). (min 2)
  - `initialChargeAmount` (number(double)): Optional override for the first (initial) charge amount. When `null`, the initial charge  uses `recurringAmount`. Lets a merchant charge a different amount up front (e.g. a  setup fee bundled into payment one) while the recurring schedule stays at  `recurringAmount`. Conditional: When InitialChargeAmount is not null. Must be > 0. (nullable)
- `captureMode` ([HppCaptureMode](https://devportal.demo.winkpg.io/docs/api/models/HppCaptureMode.md)): Optional per-session capture-mode override for the chargeable Payment flow.  `Sale` authorizes and captures now (the default; `null` uses the  page value); `Authorize` authorizes now and defers capture. Only valid  on the Payment purpose; an explicit Authorize override against a card-capture purpose is rejected. Conditional: When CaptureMode is not null. Conditional: When IsCardCapture(PagePurpose) is true. (nullable)
  Values:
  - `Sale`: Authorize and capture funds in a single step (the historical, default behavior). null resolves here, so every pre-existing chargeable page keeps doing an implicit Sale.
  - `Authorize`: Authorize now, capture later (delayed capture). The resulting transaction is an Authorization in an Authorized state, eligible for capture and void per the action matrix; the capture is completed later against the resulting transaction id via the existing capture action path (including the Apple Pay single-use token case).
- `amountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): How this session treats the payment amount, overriding whether the targeted page would let the  cardholder set it. `CustomerEntered` (the default) leaves the  page's own amount behavior in charge; `Suggested` pre-fills an  editable amount (e.g. a suggested donation); `Locked` pre-fills  a read-only amount the cardholder cannot change (e.g. paying an invoice) that the server verifies  on submit. The amount value is supplied via the `base_amount` prefilled field. Conditional: Conditional (see validator source). Must equal CustomerEntered.
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `taxAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): Optional. How this session treats the tax amount, independently of `amountMode`.  Omit it (`null`) and the tax follows `amountMode`, which is the behavior every  integration had before this property existed: a `tax` prefill on a Locked amount is  locked, and on any other amount mode it is a starting value the payer may change.  `Suggested` pre-fills an editable tax from the `tax`  prefilled field; `Locked` pre-fills a read-only tax that the  server charges whatever the payer submits; `CustomerEntered`  leaves the tax to the payer, even on a Locked amount. Conditional: When TaxAmountMode is not null. Conditional: Conditional (see validator source). (nullable)
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `shippingAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): Optional. How this session treats the shipping amount, independently of  `amountMode`. Omit it (`null`) and the shipping amount follows  `amountMode`, exactly as before this property existed.  `Suggested` pre-fills an editable amount from the  `shipping` prefilled field; `Locked` pre-fills a read-only  amount that the server charges whatever the payer submits;  `CustomerEntered` leaves the shipping amount to the payer,  even on a Locked amount. Conditional: When ShippingAmountMode is not null. Conditional: Conditional (see validator source). (nullable)
  Values:
  - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
  - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
  - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
- `lockShippingAddress` (boolean): Optional. When `true`, the shipping address supplied through the `shipping_*`  prefilled fields is shown to the payer display-only: they cannot edit it on the hosted page,  and the transaction records that address whatever the submit carried. `false` (the  default) keeps the historical behavior, where a prefilled shipping address is a starting value  the payer may change.
- `customerEmailVisibility` ([HppFieldVisibility](https://devportal.demo.winkpg.io/docs/api/models/HppFieldVisibility.md)): Optional per-session override for whether the payer-facing customer-email field is collected.  `null` (the default) inherits the targeted page's own `FieldCustomerEmail`  configuration, which is what every existing integration gets.  `Hidden` drops the field on a page that shows it,  `Optional` shows it without demanding a value, and  `Required` shows it and blocks submit until it is filled, even  on a page that hides it. One page can therefore serve both a known-customer flow and an  anonymous email-matched flow without a second page configuration. Conditional: When CustomerEmailVisibility is not null. (nullable)
  Values:
  - `Hidden`
  - `Optional`
  - `Required`
- `campaignId` (string(uuid)): Optional campaign this payment link belongs to. Omit it and the session inherits the targeted  page's own campaign, so a merchant can attach a page to a campaign once instead of naming the  campaign on every link it issues. Supply a value and it wins over the page's. (nullable)
- `reusable` (boolean): Whether this link accepts many payers rather than one. Defaults to `false`, which is  the single-use link every caller written before this field existed asks for.
- `maxCompletions` (integer(int32)): How many payments a reusable link accepts before it closes, or `null` for a link that  keeps accepting payers until it is revoked or expires. (nullable)
- `currency` (string): Optional ISO 4217 currency code this link is denominated in, for example `USD`. Omit it  and the session takes the merchant's own configured currency, which is what every link got  before this field existed. Either way the created session reports the currency it settled on,  so an integrator can read it back rather than infer it. Conditional: When Currency is not null. (nullable, max length 3)
- `campaignSource` (string): Optional per-channel source tag for this link: `email`, `sms`, `qr`,  `social`, or whatever short label names the channel you are issuing it through. Mint one  link per channel with a different tag and the campaign report compares them side by side. Conditional: When CampaignSource is not null. (nullable, max length 32)
- `products` (array<[HppSessionProductInput](https://devportal.demo.winkpg.io/docs/api/models/HppSessionProductInput.md)>): Optional cart for a page that sells catalog products: the products to start the order at and  their quantities. Each entry must name a product on the page. A quantity of zero leaves an  optional product out; a required product cannot be left out. Products the cart does not  mention start at the page's default quantity. Rejected on a page that sells no products. Conditional: When Products is not null. (nullable, max items 50)
  Fields of each item:
  - `productId` (string(uuid)) required: The catalog product, which must be one the page sells.
  - `quantity` (number(double)): The quantity to start the order at. Zero leaves an optional product out of the order (the  payer can add it back); a required product needs a quantity inside its limits. (min 0, max 9999)

_Example: Basic checkout session_

The simplest session: a single-use pay link for the targeted hosted page. The page's own configuration decides everything else (amount fields, capture mode, card storage). The label and correlation id are optional merchant-side references for tracing. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Prefilled checkout session_

Pre-fills form fields so the customer only enters card details. Use the well-known field keys (base_amount, invoice_number, customer_email, billing/shipping address fields) or an enabled merchant custom field name. A well-known key is rejected when its field is hidden on the targeted page's configuration (base_amount is always accepted); unrecognized keys are ignored when the form renders. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Lock shipping address_

For a cart that already collected the shipping address and priced shipping from it. lockShippingAddress shows the prefilled shipping address display-only, with a note to return to the cart to change it, and the transaction records that address whatever the submit carried. It requires non-blank shipping_address1 and shipping_zip prefilled fields and a page that shows the shipping address. "Same as billing" is not offered, and a digital wallet is not asked for a delivery address. To change the address, create a new session. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": true
}
```

_Example: Retry-safe session for an order_

Adds an idempotencyKey so the call is safe to resend. The same key with the same body returns the session already opened, with the same session id, link and expiry, instead of opening a second one; the response's idempotencyStatus reports Replayed when that happens, and KeyIgnored when create idempotency is not yet enabled for the merchant. Reusing the key for a different body is refused, so derive it from the thing you are collecting payment for. Up to 128 characters of letters, digits and '.', '_', ':' or '-'. It is a body field: no Idempotency-Key header is read.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Embedded iframe checkout_

A session embedded in an iframe on the merchant's site. parentOrigin must be a strict HTTPS origin (scheme + host, no path) whose host matches the page's allowed embedding domains; it enables postMessage lifecycle events (ready, payment_succeeded, etc.). enableFieldEvents opts into per-field focus/blur events (field identifiers only, never values). presentationMode=Embedded drops the standalone page chrome (surface, full-viewport height, page gutters) so the form sits flush inside the frame and stays readable down to 320 px; it is chrome only, so the flow, the collected fields and every compliance disclosure are unchanged, and it is independent of parentOrigin (an iframe that wants no postMessage events may set it alone). expirySeconds must stay within the purpose's TTL bounds (60 to 900 seconds for the default CheckoutLink purpose). Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": true,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Charge and store the card (card on file)_

Charges the customer and vaults the card as a reusable payment token when the payment succeeds. requestedCredentialStorage renders the consent prompt on the checkout page and records the consented usage scopes; UnscheduledCOF permits later merchant-initiated charges for pre-agreed events (top-ups, no-show fees, delayed charges). If tokenizeOnPayment is true with no declared scope, the scope defaults to UnscheduledCOF server-side. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Charge and store for recurring billing (bound customer)_

Vaults the card with consent scoped to both scheduled recurring charges and unscheduled card-on-file charges. The flags combine, so declare every scope the stored credential will be used under: a later merchant-initiated charge is rejected unless its reason maps to a consented scope. customerId binds the session to a known customer so the stored credential and consent land on exactly that record instead of an email-based find-or-create; the id must be an active customer under the page's merchant. requireConsent true forces the consent box to be mandatory: submit is blocked until the cardholder ticks it, regardless of the tenant StoredCredentialConsents.RequireConsent default. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": true,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Offer optional card saving_

Shows the save-card box but leaves it optional: requireConsent false renders the prompt yet lets the cardholder decline. Declining completes a normal sale with no token vaulted and no consent record written; ticking it vaults the card for later unscheduled card-on-file charges. Omitting requireConsent instead inherits the tenant StoredCredentialConsents.RequireConsent setting. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": false,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Never store the card_

Forces tokenization off for this session regardless of the merchant's tokenize-on-payment default. Use for one-off payments where a stored credential is not wanted. Leaving tokenizeOnPayment null instead defers to the merchant default. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": false,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Save a card without charging (save card only)_

Stores the card with no charge: the gateway runs a zero-dollar account verification (authorize $0 with AVS/CVV, immediate void), vaults the card, and captures consent. Requires the merchant's processor to support zero-dollar verification; the request is rejected at creation otherwise. Must not be combined with a non-zero base_amount prefill. The consent scope defaults to UnscheduledCOF when none is declared. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
```

_Example: Save Card purpose override on a Payment page_

Runs a save-card-only flow against a page whose own purpose is Payment, so one page can back both checkout and card-capture sessions without a second page instance. The resolved purpose drives everything downstream (zero-dollar verification, consent, tokenization), and the same fail-closed checks a dedicated Save Card page enforces are re-checked at session create. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
}
```

_Example: Save card with initial charge (subscription enrollment)_

Charges the first payment of a recurring schedule as a real sale, vaults the card with Recurring consent, and creates a contract for the remaining payments. The recurring plan override is required for this purpose and must have complete economics: a positive recurring amount, an interval of at least 1, and at least two total payments (the initial charge plus one recurring payment). initialChargeAmount optionally overrides payment one (for example a bundled setup fee); the target page must accept card only. Recurring consent is always captured because the initial charge depends on it; a requestedCredentialStorage sent alongside is unioned in rather than replacing Recurring, so declaring UnscheduledCOF here lets the vaulted card also authorize later ad-hoc merchant-initiated charges beyond the schedule. Replace hostedPageId with the id of your page and customerId with the id of your customer.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
}
```

_Example: Authorize now, capture later_

Overrides the capture mode for this session: the payment is authorized in full but not captured, leaving an Authorization eligible for a later capture or void through the standard transaction actions. Only valid for the chargeable Payment purpose. A null captureMode inherits the page's configured mode; send Sale explicitly to force authorize-and-capture on a page configured for delayed capture. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
}
```

_Example: Fixed amount the customer cannot change (invoice)_

Locks the amount for this session: base_amount is required and renders read-only on the page, and the server verifies the submitted amount matches before creating the transaction. Use for paying an exact invoice. Amount mode is only valid on the chargeable Payment flow; it is rejected on a save-card / card-capture session. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
}
```

_Example: Suggested amount the customer can change (donation)_

Pre-fills base_amount as a suggested amount but leaves it editable, so the cardholder can accept it or enter their own. Use for a suggested donation. base_amount is required for Suggested mode. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
}
```

_Example: Cart-priced tax and shipping on an editable amount_

Your checkout already priced the tax and the delivery, so both are locked: they render read-only and the server charges the session's tax and shipping whatever the page submits. The amount itself stays a suggestion the payer may change. A locked shipping amount wins over the page's flat rate, and zero is free shipping. Omit taxAmountMode or shippingAmountMode and that fee follows amountMode, as it always has. lockShippingAddress fixes the address the shipping was priced for. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": true
}
```

_Example: Link denominated in an explicit currency_

Names the currency the link is denominated in instead of leaving it implied. Only the merchant's own configured currency is accepted: a different code is rejected at create with HostedPaymentPage:HppSession:CurrencyNotSupported rather than accepted and then ignored at charge time. Omit the field and the session takes the merchant's currency anyway; either way the created session reports the currency it settled on in its effective block, so it can be read back rather than inferred. Replace hostedPageId with the id of your page.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
}
```

_Example: Cart on a page that sells catalog products_

Starts the order on a page that sells catalog products at chosen quantities: two of the first product, and the optional add-on left out (quantity 0). Each entry must name a product the page sells. Quantities only: the prices are snapshotted from the catalog when this link is created, the amount mode is read as Locked on the server-computed subtotal, a supplied base_amount is overwritten, and the customer's final quantities are priced again at submit. Omit the field to start at the page's default quantities. Replace hostedPageId with the id of your page and each productId with a product it sells.

```json
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
}
```

## Responses

### 200

OK

**Content type:** `application/json`

Schema: [`HppSessionCreatedDto`](https://devportal.demo.winkpg.io/docs/api/models/HppSessionCreatedDto.md)

Properties:
- `sessionId` (string(uuid)): The unique session identifier.
- `shortToken` (string): The short URL token for this session (12-character Base62). (nullable)
- `hppUrl` (string): The absolute HPP payment-link URL using the short token, resolved against the platform's  configured public base URL (`HostedPaymentPage:PublicBaseUrl`, falling back to  `App:SelfUrl`). Example: `https://pay.example.com/pay/s/aB3kX9mZqR`. Callers can use  it directly without resolving it against the host they requested from. (nullable)
- `expiresAt` (string(date-time)): UTC timestamp when the session will expire, or `null` when the session was created with  the `Permanent` lifetime and never expires on its own. Only a  request that explicitly asked for that lifetime can receive `null` here. (nullable)
- `correlationId` (string): The correlation ID echoed back from the request, if provided. (nullable)
- `idempotencyStatus` ([HppSessionCreateIdempotencyStatus](https://devportal.demo.winkpg.io/docs/api/models/HppSessionCreateIdempotencyStatus.md)): What the `idempotencyKey` on the request actually did. `Replayed` means this response  is a session that already existed and nothing new was created; `KeyIgnored` means the key  was accepted but bought nothing, because create idempotency is not enabled for this merchant.  Always populated.
  Values:
  - `NotRequested`: The request carried no idempotencyKey, so there was nothing to deduplicate. A repeat send opens a second session.
  - `KeyIgnored`: The request carried an idempotencyKey, but deduplication is not enabled for this merchant, so the key had no effect and a repeat send opens a second session. Ask your provider to enable create idempotency for the merchant.
  - `KeyAccepted`: The request carried an idempotencyKey, deduplication is enabled, and this request claimed the key. This is the original create; a repeat send of the same request inside the deduplication window replays this session instead of opening another.
  - `Replayed`: The idempotencyKey matched a create that had already completed for this merchant and credential, so no new session was opened. The response body is that original session exactly as it stands, with its original identifiers, short token, link and expiry.
- `label` (string): The label echoed back from the request, if provided. (nullable)
- `cancelUrl` (string): The Back link address stored on the created session, if one was supplied. (nullable)
- `effective` ([HppSessionEffectiveShapeDto](https://devportal.demo.winkpg.io/docs/api/models/HppSessionEffectiveShapeDto.md)): The shape the server actually resolved this session into, read off the created session rather  than off the request. Several inputs are legitimately accepted and then overridden (a  card-capture purpose forces required consent, a page can veto tokenization, a fixed-amount  page derives an amount mode, a credential-storage scope is defaulted or widened), and the  call still returns 2xx either way. Read this block to confirm the session is shaped the way  you intended, instead of loading the hosted page and inspecting it.
  Fields:
  - `consentRequired` (boolean): Whether the cardholder <b>must</b> tick the stored-credential consent box before the payment  can be submitted.
  - `consentPromptShown` (boolean): Whether the public page renders the stored-credential consent prompt for this session, i.e.  whether the session declared any credential-storage intent. When `false`, consent plays  no part in the flow and `consentRequired` is `false`.
  - `requestedCredentialStorage` ([StoredCredentialUsage](https://devportal.demo.winkpg.io/docs/api/models/StoredCredentialUsage.md)): The stored-credential usage scopes the cardholder is being asked to consent to, after  server-side defaulting and purpose forcing: a tokenizing session that declared no scope  resolves to `UnscheduledCOF`, and a Save Card with Initial  Charge session always has `Recurring` unioned in on top of  whatever the request asked for. `null` means no credential storage was requested. Values combine: send one or more member names separated by a comma and a space, or the integer sum of their values. Responses carry the names. (nullable)
    Values:
    - `None`: No permitted uses. Consent record is effectively inactive.
    - `Recurring`: Scheduled recurring charges (subscriptions, membership dues). Maps to Recurring.
    - `Installment`: Fixed-schedule installment plans dividing a known total. Maps to Installment.
    - `UnscheduledCOF`: Merchant-triggered charges for pre-agreed events (balance top-ups, usage overages, no-show fees, delayed charges). Maps to several MIT reason codes including UnscheduledCOF, NoShow, DelayedCharge, and IncrementalAuth.
    - `OneTimeFuture`: A single known future cardholder-initiated purchase the credential was saved for (e.g. "save card for my next order"). Not used for MITs.
  - `pagePurpose` ([HppPagePurpose](https://devportal.demo.winkpg.io/docs/api/models/HppPagePurpose.md)): The page purpose this session actually runs, after applying the request's optional  `pagePurpose` override over the targeted page's own configured purpose. A page with no  configured purpose resolves to `Payment`.
    Values:
    - `Payment`: The default, historical behavior: a chargeable payment page. null resolves here, so every pre-existing HPP instance keeps behaving exactly as before.
    - `SaveCard`: A card-capture page: the cardholder's card is validated via a zero-dollar account verification (authorize $0 + AVS/CVV, immediate void), then vaulted with stored-credential consent. No charge is taken. Amount-bearing configuration (tax, shipping, convenience fee, invoice amounts, donations, tip) is irrelevant and is hidden in the builder / rejected by the validator. Requires the merchant's processor to advertise SupportsZeroDollarVerification.
    - `SaveCardWithInitialCharge`: A card-capture page that also charges the first of a recurring schedule. The cardholder's card is charged a real initial sale (the first payment of a referenced reusable recurring plan, framed as the customer-initiated transaction that establishes the credential-on-file agreement), vaulted with Recurring stored-credential consent, and a customer Contract is created for the remaining payments. Like SaveCard, the amount-bearing builder configuration (tax, shipping, convenience, invoice, donations, tip, customer-set amount) is hidden: the charged amount is driven entirely by the inlined recurring definition, not by per-page amount fields. Requires a complete inlined recurring definition (RecurringPlan).
  - `tokenizeOnPayment` (boolean): Whether the cardholder's payment method will be vaulted as a reusable token. This is the  effective decision after the merchant's tokenize-by-default setting is applied and after the  targeted page's tokenization toggle (`SupportTokenization` / `SavePaymentDetails`)  has had its say. A request that asked for `tokenizeOnPayment: true` against a page whose  merchant disabled tokenization reports `false` here: that is the veto, which previously  only appeared in a server-side log line. Always `true` for a card-capture purpose, which  exists to store the credential.
  - `saveCardOnly` (boolean): Whether this session stores the payment method with no charge at all. `true` for a  resolved `SaveCard` purpose or an explicit `saveCardOnly`  request on a Payment page. Deliberately `false` for  `SaveCardWithInitialCharge`, which takes a real initial charge, so  a request that sent `saveCardOnly: true` against that purpose reports `false` here.
  - `captureMode` ([HppCaptureMode](https://devportal.demo.winkpg.io/docs/api/models/HppCaptureMode.md)): Capture timing for the chargeable Payment flow: `Sale` authorizes  and captures in one step, `Authorize` authorizes now for a later  capture. Resolved from the request's optional override over the page's configured value.  Reported as `Sale` for a card-capture purpose, where capture  timing is not an axis: those purposes set their own transaction shape.
    Values:
    - `Sale`: Authorize and capture funds in a single step (the historical, default behavior). null resolves here, so every pre-existing chargeable page keeps doing an implicit Sale.
    - `Authorize`: Authorize now, capture later (delayed capture). The resulting transaction is an Authorization in an Authorized state, eligible for capture and void per the action matrix; the capture is completed later against the resulting transaction id via the existing capture action path (including the Apple Pay single-use token case).
  - `amountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): How the cardholder may set the amount. `CustomerEntered`  leaves the page's own amount behaviour in charge; `Suggested`  pre-fills an editable amount; `Locked` pre-fills a read-only  amount that the server verifies on submit.
    Values:
    - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
    - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
    - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
  - `taxAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): The mode the tax line runs under. This is the resolved value: a request that omitted  `taxAmountMode` reports the effective `amountMode` here, because that is what  the tax follows. `Locked` means the server charges the  session's `tax` prefill whatever the payer submits.
    Values:
    - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
    - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
    - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
  - `shippingAmountMode` ([HppSessionAmountMode](https://devportal.demo.winkpg.io/docs/api/models/HppSessionAmountMode.md)): The mode the shipping amount runs under, resolved the same way as `taxAmountMode`.  `Locked` means the server charges the session's  `shipping` prefill, ahead of any flat shipping rate the page configures.
    Values:
    - `CustomerEntered`: The default, historical behavior: the session specifies no amount and the cardholder types it on the page. null (legacy sessions) resolves here.
    - `Suggested`: The session specifies an amount (via the prefilled base_amount) that pre-fills the page, but the cardholder may edit it (e.g. a suggested donation).
    - `Locked`: The session specifies an amount (via the prefilled base_amount) that the cardholder cannot change (e.g. paying an invoice). The amount renders read-only but visible, and the server verifies on submit that the charged base amount matches the session's specified amount.
  - `lockShippingAddress` (boolean): Whether this session shows its prefilled shipping address display-only and records that  address on the transaction whatever the payer submits.
  - `customerEmailVisibility` ([HppFieldVisibility](https://devportal.demo.winkpg.io/docs/api/models/HppFieldVisibility.md)): The per-session visibility of the payer-facing customer-email field, as it was accepted and  stored. `null` means the session did not override it and the page's own  `FieldCustomerEmail` applies. (nullable)
    Values:
    - `Hidden`
    - `Optional`
    - `Required`
  - `declineRetryMode` ([HppDeclineRetryMode](https://devportal.demo.winkpg.io/docs/api/models/HppDeclineRetryMode.md)): What this session does with its payment link when a payment is declined, as resolved from  the per-session override, the hosted page and the tenant default when the session was  created.
    Values:
    - `ConsumeOnDecline`: The historical behavior, and the default at every level of the resolution chain. Any processor answer spends the link permanently: the session stays Consumed and a payer who reloads it is told the link has already been used.
    - `ReleaseOnDecline`: A definite issuer decline hands the link back to Pending so the payer can correct their card and resubmit on the same URL, instead of having to ask the merchant for a new link.
  - `linkLifetime` ([HppLinkLifetime](https://devportal.demo.winkpg.io/docs/api/models/HppLinkLifetime.md)): The link lifetime the session was actually minted with, read off the created session rather  than off the request. `Session` is what a request that named no  lifetime resolves to.
    Values:
    - `Session`: The historical short single-use checkout session: 60 to 900 seconds, defaulting to 600, and still subject to the tenant's own min/max/default TTL settings. What every caller that does not name a lifetime gets.
    - `Extended`: A long-lived link, bounded by ExtendedMinTtlSeconds and ExtendedMaxTtlSeconds (1 hour to 90 days), for a link that is emailed or texted and paid later. The upper bound is capped by the tenant's HostedPaymentPageSettings.SessionMaxExtendedTtlSeconds setting.
    - `Permanent`: A link that never expires on its own. The session carries no expiry instant and no document time-to-live; it stops being payable only when it is paid, cancelled, or revoked. Requesting an expiry in seconds alongside this is rejected rather than ignored.
  - `remainingDeclineReleases` (integer(int32)): How many further times this link may be reopened after a declined payment. Always zero when  `declineRetryMode` is `ConsumeOnDecline`, and it  counts down as a payer spends attempts.
  - `currency` (string): The ISO 4217 currency this session is denominated in, upper-cased. A request that named one  gets it back verbatim, having been checked against the merchant's configured currency; a  request that named none gets the merchant's currency, which is the defaulting this block  exists to report. `null` only when the merchant has no currency configured at all, which  is the state every session predating this field is in. (nullable)
  - `customerId` (string(uuid)): The customer this session is bound to, echoed from the request's `customerId` after it  was validated to exist, be active, and belong to the page's merchant. `null` for an  unbound session, where the cardholder's email is the only customer-resolution anchor. (nullable)
  - `prefilledFieldKeys` (array<string>): The prefilled field keys stored on the session, in no particular order. Values are never  echoed. Compare against what was sent to confirm every key was accepted, and note that the  server may add one: deriving a locked amount from a fixed-amount page stamps  `base_amount` even when the request supplied none. (nullable)
  - `ignoredPrefillKeys` (array<string>): The subset of `prefilledFieldKeys` that the hosted page will silently ignore  when it renders: keys that are neither a well-known field nor an enabled, page-visible  merchant custom field. They are accepted and persisted rather than rejected, for backward  compatibility, so this list is the only signal that a key will have no effect (usually a  typo, or a custom field that is disabled or hidden for hosted pages). (nullable)

### 409

The idempotency key was already used for a different request
(`HostedPaymentPage:HppSession:CreateIdempotencyKeyReused`), the create it belongs to is
still running (`HostedPaymentPage:HppSession:CreateIdempotencyInProgress`), or the key can
no longer be resolved to a session and so cannot answer for it again
(`HostedPaymentPage:HppSession:CreateIdempotencySessionGone`, which calls for a new key
rather than a retry). Nothing was created in any of the three.

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

### 403

Forbidden

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

### 401

Unauthorized

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

### 400

Bad Request

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

_Example: Request failed validation_

One or more fields were rejected before any rule ran. error.code is null on the envelope and the causes are in validationErrors, one entry per failure. An entry carries code when the validator declared a publishable one, and memberPaths when the full path says more than members does (an element inside a collection, for example). Both keys are omitted when they would add nothing.

```json
{
  "error": {
    "code": "null",
    "message": "Your request is not valid!",
    "details": "The following errors were detected during validation.",
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": [
      {
        "message": "'Amount' must be greater than 0.",
        "members": [
          "amount"
        ]
      },
      {
        "message": "The card has expired.",
        "members": [
          "expirationDate"
        ],
        "code": "CardData:CardExpired"
      },
      {
        "message": "'Value' must not be empty.",
        "members": [
          "value"
        ],
        "memberPaths": [
          "customFields[2].value"
        ]
      }
    ]
  }
}
```

### 404

Not Found

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

_Example: Resource not found_

The id in the path names no record the caller can see. Most resources answer with a null error.code and a message naming the resource type and the id; a resource that publishes a not-found code in the catalog above carries it in error.code instead, so check for one before matching on the message.

```json
{
  "error": {
    "code": "null",
    "message": "There is no entity Transaction with id = 7d1f3a52-0c4e-4b8a-9e21-5a6b7c8d9e00!",
    "details": "null",
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": "null"
  }
}
```

### 501

Not Implemented

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

### 500

Internal Server Error

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

### default

The request failed. The body carries the standard error envelope: a machine-readable `error.code`, a human-readable `error.message`, and `error.validationErrors` when the failure was a validation rejection. The [error reference](https://devportal.demo.winkpg.io/docs/errors.md) lists the values `error.code` can take.

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

_Example: Business rule refused the request_

The request was understood and rejected by a platform rule. This is the shape that carries a code from the catalog above, so branch on error.code rather than on the message text. The HTTP status is the one listed against the code.

```json
{
  "error": {
    "code": "Transactions:OperationNotAllowed",
    "message": "The operation 'Void' is not allowed on a Sale transaction with result 'Approved' and settlement state 'Settled'.",
    "details": "null",
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": "null"
  }
}
```

### 429

The request was refused because a rate limit was exceeded, or because something a later retry can clear stopped it. A rate limit refusal carries an `application/problem+json` body: wait at least the interval `Retry-After` names before retrying, then back off. Limits are tuned per deployment, so read the allowance from the response headers rather than assuming a fixed ceiling. Any other refusal carries the standard error envelope as `application/json`, and its `error.code` names the cause.

**Content type:** `application/problem+json`

Schema: [`RateLimitProblemDetails`](https://devportal.demo.winkpg.io/docs/api/models/RateLimitProblemDetails.md)

Properties:
- `type` (string) required: The problem type identifier. Always the same value: the failure is the status code itself,  so there is no sub-type for a caller to branch on. (nullable)
- `title` (string) required: A short, human-readable summary of the problem type. (nullable)
- `status` (integer(int32)) required: The HTTP status code, repeated in the body as the problem-details format defines.
- `detail` (string) required: A human-readable explanation of this occurrence of the problem. (nullable)
- `retryAfterSeconds` (integer(int32)) required: How long to wait before retrying, in whole seconds, carrying the same figure as the  `Retry-After` header. Always at least one: a value of zero would invite an immediate  retry that is certain to be rejected again.

**Content type:** `application/json`

Schema: [`RemoteServiceErrorResponse`](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorResponse.md)

Properties:
- `error` ([RemoteServiceErrorInfo](https://devportal.demo.winkpg.io/docs/api/models/RemoteServiceErrorInfo.md))

## Example request

Every block below sends the same request. Replace {{BASE_URL}} with the address of the API you are calling and {{API_KEY}} with your own key.

The request body is a CreateHppSessionInput. See the Request body section below for its fields.

These blocks send the Basic checkout session example. The other examples follow under Other request examples.

The simplest session: a single-use pay link for the targeted hosted page. The page's own configuration decides everything else (amount fields, capture mode, card storage). The label and correlation id are optional merchant-side references for tracing. Replace hostedPageId with the id of your page.

### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "linkLifetime": "Session",
    "correlationId": "order-1042",
    "label": "Order #1042",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "correlationId": "order-1042",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "correlationId": "order-1042",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "correlationId": "order-1042",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "correlationId": "order-1042",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

## Other request examples

Each example below is one request, shown in every language with the same placeholders.

### Prefilled checkout session

Pre-fills form fields so the customer only enters card details. Use the well-known field keys (base_amount, invoice_number, customer_email, billing/shipping address fields) or an enabled merchant custom field name. A well-known key is rejected when its field is hidden on the targeted page's configuration (base_amount is always accepted); unrecognized keys are ignored when the form renders. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "125.00",
      "invoice_number": "INV-2071",
      "customer_email": "pat.smith@example.com",
      "billing_zip": "55401"
    },
    "linkLifetime": "Session",
    "label": "Invoice INV-2071",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "invoice_number": "INV-2071",
        "customer_email": "pat.smith@example.com",
        "billing_zip": "55401"
      },
      "linkLifetime": "Session",
      "label": "Invoice INV-2071",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "invoice_number": "INV-2071",
        "customer_email": "pat.smith@example.com",
        "billing_zip": "55401"
      },
      "linkLifetime": "Session",
      "label": "Invoice INV-2071",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "invoice_number": "INV-2071",
        "customer_email": "pat.smith@example.com",
        "billing_zip": "55401"
      },
      "linkLifetime": "Session",
      "label": "Invoice INV-2071",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071",
    "customer_email": "pat.smith@example.com",
    "billing_zip": "55401"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Lock shipping address

For a cart that already collected the shipping address and priced shipping from it. lockShippingAddress shows the prefilled shipping address display-only, with a note to return to the cart to change it, and the transaction records that address whatever the submit carried. It requires non-blank shipping_address1 and shipping_zip prefilled fields and a page that shows the shipping address. "Same as billing" is not offered, and a digital wallet is not asked for a delivery address. To change the address, create a new session. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": true
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": true
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": true
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "125.00",
      "shipping_address1": "100 Main St",
      "shipping_city": "Minneapolis",
      "shipping_state": "MN",
      "shipping_zip": "55401",
      "shipping_country": "US"
    },
    "linkLifetime": "Session",
    "label": "Order #1042",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered",
    "lockShippingAddress": true
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "shipping_address1": "100 Main St",
        "shipping_city": "Minneapolis",
        "shipping_state": "MN",
        "shipping_zip": "55401",
        "shipping_country": "US"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "lockShippingAddress": true
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "shipping_address1": "100 Main St",
        "shipping_city": "Minneapolis",
        "shipping_state": "MN",
        "shipping_zip": "55401",
        "shipping_country": "US"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "lockShippingAddress": true
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "shipping_address1": "100 Main St",
        "shipping_city": "Minneapolis",
        "shipping_state": "MN",
        "shipping_zip": "55401",
        "shipping_country": "US"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "lockShippingAddress": True
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "shipping_address1": "100 Main St",
    "shipping_city": "Minneapolis",
    "shipping_state": "MN",
    "shipping_zip": "55401",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "lockShippingAddress": True
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Retry-safe session for an order

Adds an idempotencyKey so the call is safe to resend. The same key with the same body returns the session already opened, with the same session id, link and expiry, instead of opening a second one; the response's idempotencyStatus reports Replayed when that happens, and KeyIgnored when create idempotency is not yet enabled for the merchant. Reusing the key for a different body is refused, so derive it from the thing you are collecting payment for. Up to 128 characters of letters, digits and '.', '_', ':' or '-'. It is a body field: no Idempotency-Key header is read.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "125.00"
    },
    "linkLifetime": "Session",
    "label": "Order #1042",
    "idempotencyKey": "order-1042",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "idempotencyKey": "order-1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "idempotencyKey": "order-1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "idempotencyKey": "order-1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "idempotencyKey": "order-1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Embedded iframe checkout

A session embedded in an iframe on the merchant's site. parentOrigin must be a strict HTTPS origin (scheme + host, no path) whose host matches the page's allowed embedding domains; it enables postMessage lifecycle events (ready, payment_succeeded, etc.). enableFieldEvents opts into per-field focus/blur events (field identifiers only, never values). presentationMode=Embedded drops the standalone page chrome (surface, full-viewport height, page gutters) so the form sits flush inside the frame and stays readable down to 320 px; it is chrome only, so the flow, the collected fields and every compliance disclosure are unchanged, and it is independent of parentOrigin (an iframe that wants no postMessage events may set it alone). expirySeconds must stay within the purpose's TTL bounds (60 to 900 seconds for the default CheckoutLink purpose). Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": true,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": true,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": true,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "expirySeconds": 900,
    "linkLifetime": "Session",
    "correlationId": "cart-7c1f2a",
    "parentOrigin": "https://shop.acme.example.com",
    "enableFieldEvents": true,
    "presentationMode": "Embedded",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "expirySeconds": 900,
      "linkLifetime": "Session",
      "correlationId": "cart-7c1f2a",
      "parentOrigin": "https://shop.acme.example.com",
      "enableFieldEvents": true,
      "presentationMode": "Embedded",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "expirySeconds": 900,
      "linkLifetime": "Session",
      "correlationId": "cart-7c1f2a",
      "parentOrigin": "https://shop.acme.example.com",
      "enableFieldEvents": true,
      "presentationMode": "Embedded",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "expirySeconds": 900,
      "linkLifetime": "Session",
      "correlationId": "cart-7c1f2a",
      "parentOrigin": "https://shop.acme.example.com",
      "enableFieldEvents": True,
      "presentationMode": "Embedded",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "expirySeconds": 900,
  "linkLifetime": "Session",
  "correlationId": "cart-7c1f2a",
  "parentOrigin": "https://shop.acme.example.com",
  "enableFieldEvents": True,
  "presentationMode": "Embedded",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Charge and store the card (card on file)

Charges the customer and vaults the card as a reusable payment token when the payment succeeds. requestedCredentialStorage renders the consent prompt on the checkout page and records the consented usage scopes; UnscheduledCOF permits later merchant-initiated charges for pre-agreed events (top-ups, no-show fees, delayed charges). If tokenizeOnPayment is true with no declared scope, the scope defaults to UnscheduledCOF server-side. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "linkLifetime": "Session",
    "label": "Account top-up with card on file",
    "requestedCredentialStorage": "UnscheduledCOF",
    "tokenizeOnPayment": true,
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Account top-up with card on file",
      "requestedCredentialStorage": "UnscheduledCOF",
      "tokenizeOnPayment": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Account top-up with card on file",
      "requestedCredentialStorage": "UnscheduledCOF",
      "tokenizeOnPayment": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Account top-up with card on file",
      "requestedCredentialStorage": "UnscheduledCOF",
      "tokenizeOnPayment": True,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Account top-up with card on file",
  "requestedCredentialStorage": "UnscheduledCOF",
  "tokenizeOnPayment": True,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Charge and store for recurring billing (bound customer)

Vaults the card with consent scoped to both scheduled recurring charges and unscheduled card-on-file charges. The flags combine, so declare every scope the stored credential will be used under: a later merchant-initiated charge is rejected unless its reason maps to a consented scope. customerId binds the session to a known customer so the stored credential and consent land on exactly that record instead of an email-based find-or-create; the id must be an active customer under the page's merchant. requireConsent true forces the consent box to be mandatory: submit is blocked until the cardholder ticks it, regardless of the tenant StoredCredentialConsents.RequireConsent default. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": true,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": true,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": true,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "customerId": "00000000-0000-0000-0000-00000000000b",
    "linkLifetime": "Session",
    "label": "Membership renewal with stored card",
    "requestedCredentialStorage": "Recurring, UnscheduledCOF",
    "requireConsent": true,
    "tokenizeOnPayment": true,
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Membership renewal with stored card",
      "requestedCredentialStorage": "Recurring, UnscheduledCOF",
      "requireConsent": true,
      "tokenizeOnPayment": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Membership renewal with stored card",
      "requestedCredentialStorage": "Recurring, UnscheduledCOF",
      "requireConsent": true,
      "tokenizeOnPayment": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Membership renewal with stored card",
      "requestedCredentialStorage": "Recurring, UnscheduledCOF",
      "requireConsent": True,
      "tokenizeOnPayment": True,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Membership renewal with stored card",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "requireConsent": True,
  "tokenizeOnPayment": True,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Offer optional card saving

Shows the save-card box but leaves it optional: requireConsent false renders the prompt yet lets the cardholder decline. Declining completes a normal sale with no token vaulted and no consent record written; ticking it vaults the card for later unscheduled card-on-file charges. Omitting requireConsent instead inherits the tenant StoredCredentialConsents.RequireConsent setting. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": false,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": false,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": false,
  "tokenizeOnPayment": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "linkLifetime": "Session",
    "label": "Checkout with optional card saving",
    "requestedCredentialStorage": "UnscheduledCOF",
    "requireConsent": false,
    "tokenizeOnPayment": true,
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Checkout with optional card saving",
      "requestedCredentialStorage": "UnscheduledCOF",
      "requireConsent": false,
      "tokenizeOnPayment": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Checkout with optional card saving",
      "requestedCredentialStorage": "UnscheduledCOF",
      "requireConsent": false,
      "tokenizeOnPayment": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Checkout with optional card saving",
      "requestedCredentialStorage": "UnscheduledCOF",
      "requireConsent": False,
      "tokenizeOnPayment": True,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Checkout with optional card saving",
  "requestedCredentialStorage": "UnscheduledCOF",
  "requireConsent": False,
  "tokenizeOnPayment": True,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Never store the card

Forces tokenization off for this session regardless of the merchant's tokenize-on-payment default. Use for one-off payments where a stored credential is not wanted. Leaving tokenizeOnPayment null instead defers to the merchant default. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": false,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": false,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": false,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "linkLifetime": "Session",
    "label": "One-time guest payment",
    "tokenizeOnPayment": false,
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "One-time guest payment",
      "tokenizeOnPayment": false,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "One-time guest payment",
      "tokenizeOnPayment": false,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "One-time guest payment",
      "tokenizeOnPayment": False,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "One-time guest payment",
  "tokenizeOnPayment": False,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Save a card without charging (save card only)

Stores the card with no charge: the gateway runs a zero-dollar account verification (authorize $0 with AVS/CVV, immediate void), vaults the card, and captures consent. Requires the merchant's processor to support zero-dollar verification; the request is rejected at creation otherwise. Must not be combined with a non-zero base_amount prefill. The consent scope defaults to UnscheduledCOF when none is declared. Replace hostedPageId with the id of your page and customerId with the id of your customer.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": true,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "customerId": "00000000-0000-0000-0000-00000000000b",
    "linkLifetime": "Session",
    "label": "Add card to wallet",
    "saveCardOnly": true,
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Add card to wallet",
      "saveCardOnly": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Add card to wallet",
      "saveCardOnly": true,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Add card to wallet",
      "saveCardOnly": True,
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Add card to wallet",
  "saveCardOnly": True,
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Save Card purpose override on a Payment page

Runs a save-card-only flow against a page whose own purpose is Payment, so one page can back both checkout and card-capture sessions without a second page instance. The resolved purpose drives everything downstream (zero-dollar verification, consent, tokenization), and the same fail-closed checks a dedicated Save Card page enforces are re-checked at session create. Replace hostedPageId with the id of your page and customerId with the id of your customer.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "customerId": "00000000-0000-0000-0000-00000000000b",
    "linkLifetime": "Session",
    "label": "Capture payment method",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "pagePurpose": "SaveCard",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Capture payment method",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "pagePurpose": "SaveCard",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Capture payment method",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "pagePurpose": "SaveCard",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Capture payment method",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "pagePurpose": "SaveCard",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Capture payment method",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCard",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Save card with initial charge (subscription enrollment)

Charges the first payment of a recurring schedule as a real sale, vaults the card with Recurring consent, and creates a contract for the remaining payments. The recurring plan override is required for this purpose and must have complete economics: a positive recurring amount, an interval of at least 1, and at least two total payments (the initial charge plus one recurring payment). initialChargeAmount optionally overrides payment one (for example a bundled setup fee); the target page must accept card only. Recurring consent is always captured because the initial charge depends on it; a requestedCredentialStorage sent alongside is unioned in rather than replacing Recurring, so declaring UnscheduledCOF here lets the vaulted card also authorize later ad-hoc merchant-initiated charges beyond the schedule. Replace hostedPageId with the id of your page and customerId with the id of your customer.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "customerId": "00000000-0000-0000-0000-00000000000b",
    "linkLifetime": "Session",
    "label": "Gold plan enrollment",
    "requestedCredentialStorage": "Recurring, UnscheduledCOF",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "pagePurpose": "SaveCardWithInitialCharge",
    "recurringPlan": {
      "recurringAmount": 29.99,
      "frequency": "Monthly",
      "interval": 1,
      "numberOfPayments": 12,
      "initialChargeAmount": 49.99
    },
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Gold plan enrollment",
      "requestedCredentialStorage": "Recurring, UnscheduledCOF",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "pagePurpose": "SaveCardWithInitialCharge",
      "recurringPlan": {
        "recurringAmount": 29.99,
        "frequency": "Monthly",
        "interval": 1,
        "numberOfPayments": 12,
        "initialChargeAmount": 49.99
      },
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Gold plan enrollment",
      "requestedCredentialStorage": "Recurring, UnscheduledCOF",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "pagePurpose": "SaveCardWithInitialCharge",
      "recurringPlan": {
        "recurringAmount": 29.99,
        "frequency": "Monthly",
        "interval": 1,
        "numberOfPayments": 12,
        "initialChargeAmount": 49.99
      },
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "customerId": "00000000-0000-0000-0000-00000000000b",
      "linkLifetime": "Session",
      "label": "Gold plan enrollment",
      "requestedCredentialStorage": "Recurring, UnscheduledCOF",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "pagePurpose": "SaveCardWithInitialCharge",
      "recurringPlan": {
        "recurringAmount": 29.99,
        "frequency": "Monthly",
        "interval": 1,
        "numberOfPayments": 12,
        "initialChargeAmount": 49.99
      },
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "customerId": "00000000-0000-0000-0000-00000000000b",
  "linkLifetime": "Session",
  "label": "Gold plan enrollment",
  "requestedCredentialStorage": "Recurring, UnscheduledCOF",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "pagePurpose": "SaveCardWithInitialCharge",
  "recurringPlan": {
    "recurringAmount": 29.99,
    "frequency": "Monthly",
    "interval": 1,
    "numberOfPayments": 12,
    "initialChargeAmount": 49.99
  },
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Authorize now, capture later

Overrides the capture mode for this session: the payment is authorized in full but not captured, leaving an Authorization eligible for a later capture or void through the standard transaction actions. Only valid for the chargeable Payment purpose. A null captureMode inherits the page's configured mode; send Sale explicitly to force authorize-and-capture on a page configured for delayed capture. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "linkLifetime": "Session",
    "label": "Pre-order hold",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "captureMode": "Authorize",
    "amountMode": "CustomerEntered"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Pre-order hold",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "captureMode": "Authorize",
      "amountMode": "CustomerEntered"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Pre-order hold",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "captureMode": "Authorize",
      "amountMode": "CustomerEntered"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Pre-order hold",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "captureMode": "Authorize",
      "amountMode": "CustomerEntered"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Pre-order hold",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "captureMode": "Authorize",
  "amountMode": "CustomerEntered"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Fixed amount the customer cannot change (invoice)

Locks the amount for this session: base_amount is required and renders read-only on the page, and the server verifies the submitted amount matches before creating the transaction. Use for paying an exact invoice. Amount mode is only valid on the chargeable Payment flow; it is rejected on a save-card / card-capture session. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "125.00",
      "invoice_number": "INV-2071"
    },
    "linkLifetime": "Session",
    "label": "Invoice INV-2071",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "Locked"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "invoice_number": "INV-2071"
      },
      "linkLifetime": "Session",
      "label": "Invoice INV-2071",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Locked"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "invoice_number": "INV-2071"
      },
      "linkLifetime": "Session",
      "label": "Invoice INV-2071",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Locked"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00",
        "invoice_number": "INV-2071"
      },
      "linkLifetime": "Session",
      "label": "Invoice INV-2071",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Locked"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00",
    "invoice_number": "INV-2071"
  },
  "linkLifetime": "Session",
  "label": "Invoice INV-2071",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Locked"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Suggested amount the customer can change (donation)

Pre-fills base_amount as a suggested amount but leaves it editable, so the cardholder can accept it or enter their own. Use for a suggested donation. base_amount is required for Suggested mode. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "50.00"
    },
    "linkLifetime": "Session",
    "label": "Suggested donation",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "Suggested"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "50.00"
      },
      "linkLifetime": "Session",
      "label": "Suggested donation",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Suggested"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "50.00"
      },
      "linkLifetime": "Session",
      "label": "Suggested donation",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Suggested"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "50.00"
      },
      "linkLifetime": "Session",
      "label": "Suggested donation",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Suggested"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "50.00"
  },
  "linkLifetime": "Session",
  "label": "Suggested donation",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Cart-priced tax and shipping on an editable amount

Your checkout already priced the tax and the delivery, so both are locked: they render read-only and the server charges the session's tax and shipping whatever the page submits. The amount itself stays a suggestion the payer may change. A locked shipping amount wins over the page's flat rate, and zero is free shipping. Omit taxAmountMode or shippingAmountMode and that fee follows amountMode, as it always has. lockShippingAddress fixes the address the shipping was priced for. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": true
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": true
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": true
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "80.00",
      "tax": "6.40",
      "shipping": "0",
      "shipping_address1": "100 Main St",
      "shipping_city": "Phoenix",
      "shipping_state": "AZ",
      "shipping_zip": "85004",
      "shipping_country": "US"
    },
    "linkLifetime": "Session",
    "label": "Cart 4417",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "Suggested",
    "taxAmountMode": "Locked",
    "shippingAmountMode": "Locked",
    "lockShippingAddress": true
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "80.00",
        "tax": "6.40",
        "shipping": "0",
        "shipping_address1": "100 Main St",
        "shipping_city": "Phoenix",
        "shipping_state": "AZ",
        "shipping_zip": "85004",
        "shipping_country": "US"
      },
      "linkLifetime": "Session",
      "label": "Cart 4417",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Suggested",
      "taxAmountMode": "Locked",
      "shippingAmountMode": "Locked",
      "lockShippingAddress": true
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "80.00",
        "tax": "6.40",
        "shipping": "0",
        "shipping_address1": "100 Main St",
        "shipping_city": "Phoenix",
        "shipping_state": "AZ",
        "shipping_zip": "85004",
        "shipping_country": "US"
      },
      "linkLifetime": "Session",
      "label": "Cart 4417",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Suggested",
      "taxAmountMode": "Locked",
      "shippingAmountMode": "Locked",
      "lockShippingAddress": true
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "80.00",
        "tax": "6.40",
        "shipping": "0",
        "shipping_address1": "100 Main St",
        "shipping_city": "Phoenix",
        "shipping_state": "AZ",
        "shipping_zip": "85004",
        "shipping_country": "US"
      },
      "linkLifetime": "Session",
      "label": "Cart 4417",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "Suggested",
      "taxAmountMode": "Locked",
      "shippingAmountMode": "Locked",
      "lockShippingAddress": True
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "80.00",
    "tax": "6.40",
    "shipping": "0",
    "shipping_address1": "100 Main St",
    "shipping_city": "Phoenix",
    "shipping_state": "AZ",
    "shipping_zip": "85004",
    "shipping_country": "US"
  },
  "linkLifetime": "Session",
  "label": "Cart 4417",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "Suggested",
  "taxAmountMode": "Locked",
  "shippingAmountMode": "Locked",
  "lockShippingAddress": True
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Link denominated in an explicit currency

Names the currency the link is denominated in instead of leaving it implied. Only the merchant's own configured currency is accepted: a different code is rejected at create with HostedPaymentPage:HppSession:CurrencyNotSupported rather than accepted and then ignored at charge time. Omit the field and the session takes the merchant's currency anyway; either way the created session reports the currency it settled on in its effective block, so it can be read back rather than inferred. Replace hostedPageId with the id of your page.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "prefilledFields": {
      "base_amount": "125.00"
    },
    "linkLifetime": "Session",
    "label": "Order #1042",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered",
    "currency": "USD"
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "currency": "USD"
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "currency": "USD"
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "prefilledFields": {
        "base_amount": "125.00"
      },
      "linkLifetime": "Session",
      "label": "Order #1042",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "currency": "USD"
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "prefilledFields": {
    "base_amount": "125.00"
  },
  "linkLifetime": "Session",
  "label": "Order #1042",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "currency": "USD"
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

### Cart on a page that sells catalog products

Starts the order on a page that sells catalog products at chosen quantities: two of the first product, and the optional add-on left out (quantity 0). Each entry must name a product the page sells. Quantities only: the prices are snapshotted from the catalog when this link is created, the amount mode is read as Locked on the server-computed subtotal, a supplied base_amount is overwritten, and the customer's final quantities are priced again at submit. Omit the field to start at the page's default quantities. Replace hostedPageId with the id of your page and each productId with a product it sells.

#### cURL

```bash
curl -X POST "{{BASE_URL}}/api/hostedpaymentpages/sessions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
}'
```

#### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/hostedpaymentpages/sessions' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

#### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, HostedPaymentPagesApi } from '@winkpg/winkpg-api';

const api = new HostedPaymentPagesApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.hppSessionCreate({
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
});
```

#### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/hostedpaymentpages/sessions', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostedPageId": "00000000-0000-0000-0000-00000000000a",
    "linkLifetime": "Session",
    "label": "Order #1043",
    "presentationMode": "Standalone",
    "hostChannel": "ParentWindow",
    "purpose": "CheckoutLink",
    "amountMode": "CustomerEntered",
    "products": [
      {
        "productId": "00000000-0000-0000-0000-00000000000c",
        "quantity": 2
      },
      {
        "productId": "00000000-0000-0000-0000-00000000000d"
      }
    ]
  }),
});

const data = await response.json();
```

#### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new HostedPaymentPagesApi(config);
var body = JsonSerializer.Deserialize<CreateHppSessionInput>("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Order #1043",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "products": [
        {
          "productId": "00000000-0000-0000-0000-00000000000c",
          "quantity": 2
        },
        {
          "productId": "00000000-0000-0000-0000-00000000000d"
        }
      ]
    }
    """);

var result = await api.HppSessionCreateAsync(body);
```

#### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/hostedpaymentpages/sessions");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Order #1043",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "products": [
        {
          "productId": "00000000-0000-0000-0000-00000000000c",
          "quantity": 2
        },
        {
          "productId": "00000000-0000-0000-0000-00000000000d"
        }
      ]
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

#### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.HostedPaymentPagesApi(client)
    body = winkpg_api.CreateHppSessionInput.from_dict({
      "hostedPageId": "00000000-0000-0000-0000-00000000000a",
      "linkLifetime": "Session",
      "label": "Order #1043",
      "presentationMode": "Standalone",
      "hostChannel": "ParentWindow",
      "purpose": "CheckoutLink",
      "amountMode": "CustomerEntered",
      "products": [
        {
          "productId": "00000000-0000-0000-0000-00000000000c",
          "quantity": 2
        },
        {
          "productId": "00000000-0000-0000-0000-00000000000d"
        }
      ]
    })
    result = api.hpp_session_create(body)
```

#### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "hostedPageId": "00000000-0000-0000-0000-00000000000a",
  "linkLifetime": "Session",
  "label": "Order #1043",
  "presentationMode": "Standalone",
  "hostChannel": "ParentWindow",
  "purpose": "CheckoutLink",
  "amountMode": "CustomerEntered",
  "products": [
    {
      "productId": "00000000-0000-0000-0000-00000000000c",
      "quantity": 2
    },
    {
      "productId": "00000000-0000-0000-0000-00000000000d"
    }
  ]
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/hostedpaymentpages/sessions",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

## See also

- [All documentation](https://devportal.demo.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
