Checkout links API
Create, list, inspect, update, deactivate, and resolve reusable hosted checkout links.
Checkout links API
Checkout links are durable, client-owned offer records. A link stores the approved catalog selections and checkout options. Resolving it does not create a session; the hosted checkout creates a fresh session for each buyer journey.
For the production Dashboard workflow, surface decision, security rules, and troubleshooting, read Hosted checkout links.
Authentication and environments
Management endpoints accept client Basic authentication or an OAuth bearer token. For Basic authentication, use the client ID as the username and an API token as the password. Run these calls on a trusted server. The authenticated client owns every returned record; a request body cannot select another client.
The public resolve endpoint has no authentication. Its opaque token is the only public identifier it accepts.
| Environment | Management API | Hosted checkout |
|---|---|---|
| Staging | https://api.stage.flopay.com | https://checkout.stage.flopay.com |
| Production | https://api.flopay.com | https://checkout.flopay.com |
Credentials, catalog items, links, and sessions are isolated by environment. Always use the url returned by the matching API; never move a publicToken between hosts.
Create a link
POST /v1/checkout-links
POST /v1/checkout-links HTTP/1.1
Host: api.stage.flopay.com
Authorization: Basic <base64-of-client-id-and-api-token>
Content-Type: application/json
{
"currency": "USD",
"products": [{ "code": "example-product", "quantity": 1 }],
"couponCodes": [],
"successUrl": "https://merchant.example/checkout/success",
"cancelUrl": "https://merchant.example/checkout/cancel",
"checkoutMode": "full",
"captureMethod": "automatic",
"avsCheck": false,
"avsFields": null,
"surface": "full_detail",
"expiresAt": null
}Only currency and at least one products item are required. Omitted options resolve to full, automatic, false for AVS, full_detail, and null for both AVS fields and expiry.
| Field | Type | Rules |
|---|---|---|
currency | string | Three-letter ISO 4217 currency. Every selected product price must use it. |
products | array | At least one active catalog product. Each entry has code, optional variantId, and positive integer quantity. |
couponCodes | string[] | Optional active coupon codes from the same client catalog. Defaults to []. |
successUrl | string or null | Optional absolute HTTP or HTTPS destination without embedded credentials. |
cancelUrl | string or null | Optional absolute HTTP or HTTPS destination without embedded credentials. |
checkoutMode | string | Only full is supported for reusable links. |
captureMethod | string | automatic or manual. Defaults to automatic. |
avsCheck | boolean | Enables address verification fields. Defaults to false. |
avsFields | object or null | Optional true-valued line1, line2, city, state, and postal flags. Requires avsCheck: true. |
surface | string | full_detail or widget_only. Both surfaces are supported. full_detail is the default. It includes the order summary, while widget_only renders the payment embed. |
expiresAt | string or null | Optional future ISO 8601 timestamp. Defaults to no expiry. |
For an embedded payment form, create a widget_only link and use the returned url with ?presentation=widget as the iframe source. The parent page must use an HTTPS origin approved for the workspace. A full_detail link remains a complete hosted page and should not be framed.
201 Created returns the manageable record and hosted URL:
{
"uuid": "<checkout-link-uuid>",
"publicToken": "<opaque-link-token>",
"url": "https://checkout.stage.flopay.com/<opaque-link-token>",
"clientId": "<staging-client-id>",
"currency": "USD",
"products": [{ "code": "example-product", "quantity": 1 }],
"couponCodes": [],
"successUrl": "https://merchant.example/checkout/success",
"cancelUrl": "https://merchant.example/checkout/cancel",
"checkoutMode": "full",
"captureMethod": "automatic",
"avsCheck": false,
"avsFields": null,
"surface": "full_detail",
"isActive": true,
"expiresAt": null,
"createdAt": "<created-at>",
"updatedAt": "<updated-at>"
}Treat url as authoritative. uuid identifies the record to authenticated management operations; publicToken resolves only the buyer-facing offer.
List links
GET /v1/checkout-links
The endpoint returns only links owned by the authenticated client. Pagination is 1-based; page defaults to 1, limit defaults to 20, and the maximum limit is 100.
{
"data": [],
"page": 1,
"limit": 20,
"pages": 0,
"total": 0
}Common filters include isActive, currency, checkoutMode, captureMethod, surface, avsCheck, createdAt, updatedAt, and expiresAt. Use bracket operators for ranges, lists, search, and sorting, for example:
GET /v1/checkout-links?isActive=true&surface=full_detail&createdAt[gte]=2026-08-01T00:00:00.000Z&sort[desc]=createdAtRetrieve a link
GET /v1/checkout-links/:uuid
Returns one CheckoutLink record owned by the authenticated client. An unknown UUID and a record outside the client boundary both return the same 404 Not Found result.
Update a link
PATCH /v1/checkout-links/:uuid
Send only fields that should change. The endpoint accepts the create fields plus isActive. Use null to clear successUrl, cancelUrl, avsFields, or expiresAt.
PATCH /v1/checkout-links/<checkout-link-uuid> HTTP/1.1
Host: api.stage.flopay.com
Authorization: Basic <base64-of-client-id-and-api-token>
Content-Type: application/json
{
"expiresAt": "2026-12-31T23:59:59.000Z",
"avsCheck": true,
"avsFields": { "line1": true, "postal": true }
}The response is the updated record. Changes affect future resolves only and do not mutate an already-created checkout session.
Deactivate a link
DELETE /v1/checkout-links/:uuid
Returns 204 No Content. Deactivation is not a hard delete: the record remains manageable with isActive: false, but future public resolves are unavailable. An existing buyer session continues with its original snapshot.
Set isActive: true through PATCH /v1/checkout-links/:uuid to reactivate a valid, unexpired record.
Resolve a link
GET /v1/checkout-links/:publicToken/resolve
This endpoint is public, sends Cache-Control: no-store, and returns only the configuration the Flo-hosted buyer experience needs:
{
"clientId": "<staging-client-id>",
"currency": "USD",
"products": [{ "code": "example-product", "quantity": 1 }],
"couponCodes": [],
"successUrl": "https://merchant.example/checkout/success",
"cancelUrl": "https://merchant.example/checkout/cancel",
"checkoutMode": "full",
"captureMethod": "automatic",
"avsCheck": false,
"avsFields": null,
"surface": "full_detail",
"brand": null
}The response contains exactly the checkout fields shown above. A full_detail response renders the complete hosted page. A widget_only response renders the payment embed when the link is opened with presentation=widget from an approved parent origin.
Resolving does not create or change a checkout session. The hosted checkout creates a new session after a successful resolve, so each buyer open has isolated payment state.
Malformed, unknown, inactive, expired, cross-environment, and tampered tokens all return the same safe 404 Not Found response. Do not use this public response to diagnose records; use an authenticated read instead.
Error responses
| Status | Meaning | Action |
|---|---|---|
400 | Malformed body, URL, expiry, query, or pagination option. | Correct the request shape or value. |
401 | Missing or invalid client authentication on a management route. | Check the environment and server-side credential. |
404 | The owned management record was not found, or a public token is unavailable. | Check the authenticated client context or copy the returned hosted URL again. Do not probe for another client's record. |
422 | Catalog offer is unavailable, mode is unsupported, or advanced options conflict. | Correct the product, coupon, currency, checkoutMode, or AVS combination using the returned issue list. |
Provider-specific overrides are not part of the checkout-link API. Use only the fields listed on this page.