> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.fluveo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.fluveo.com/_mcp/server.

# Payment Links

> **Status: `contracted_test`.**
> `/v1/payment_links` create / retrieve / update / list return the Stripe
> `payment_link` envelope under the active merchant, and the hosted page renders on
> `pay.fluveo.com/p/{id}` with signed explicit continuation. The suite is managed
> with a Fluveo `sk_test_*` merchant secret key. Processor keys are not public API
> credentials. See [API coverage](/api-coverage).

A Payment Link is a hosted URL you share with a customer — in an email, an
invoice, a chat — and they pay on a Fluveo-hosted page without any checkout UI on
your side. Each Fluveo link is a **reusable merchant-owned template**; every
explicit customer continuation creates a fresh Checkout Session or PaymentIntent
correlated to that template. It is the no-code sibling of
[Checkout Sessions](/checkout/checkout-sessions): a session is created per
purchase by your server, whereas a link is created once and reused.

## What works today

* **The CRUD API is live in test mode.** `POST /v1/payment_links` (create),
  `GET /v1/payment_links/:id` (retrieve), `POST /v1/payment_links/:id` (update,
  including `active=false` to deactivate), and `GET /v1/payment_links` (list, with
  cursor pagination) all return the Stripe `payment_link` envelope.
* **Line items are readable.** `GET /v1/payment_links/:id/line_items` returns the
  link's items.
* **The hosted page is live in test mode.** A link renders the same
  Fluveo-hosted payment experience as Checkout — see
  [the hosted page](/checkout/hosted-payment-page).
* **Create and manage links in the [dashboard](/dashboard/payment-links).**

## The Stripe shape

Create a link with Stripe's `line_items`: 1–20 items, each with its `quantity`
and either a catalog `price` ID or inline `price_data` (`currency`,
`unit_amount`, optional `product_data` with its required `name`). Every item uses
one currency, and Fluveo collapses the items into one authoritative total for
payment creation.

Public `/v1` accepts and returns only Stripe's names (ADR 0004 as amended). Flat
`amount` pricing, `description`, `expires_at`, customer-chosen amounts, a bound
`customer`, pricing edits and the `expire` operation are hidden because they do
not follow the Stripe parity ADR; the API answers them as Stripe answers an
unknown parameter or URL. The [dashboard](/dashboard/payment-links) still offers
them. A link created through the API never expires, as on Stripe (links created
before this change keep their former 24-hour expiry): it stays
payable until it is deactivated or reaches its completed-sessions limit.

The create body is `application/x-www-form-urlencoded` and the mutating create
honors the `Idempotency-Key` header. An identical retry replays the original
answer with `Idempotent-Replayed: true`; reusing the key with different parameters
is a `400 idempotency_error`, and a retry while the first request still runs is a
`409 idempotency_error`. Keys expire after 24 hours.

See [`/v1/payment_links`](/api-reference) in the API Reference for the full request
and object shape.

## Hidden from the public API

Each item below is hidden because it does not follow the Stripe parity ADR (ADR 0004 as amended). The [hidden public `/v1` registry](https://github.com/fluveohq/fluveo/blob/main/tests/api-contract/stripe/hidden-public-v1.json) lists every
hidden item. Public `/v1` answers a hidden operation as an unknown URL, refuses a hidden request
parameter or value with Stripe's `invalid_request_error`, and omits a hidden response key.

* the request parameter `amount` of `POST /v1/payment_links` (HIDE0-021)
* the request parameter `amount` of `POST /v1/payment_links` (HIDE0-022)
* the request parameter `description` of `POST /v1/payment_links` (HIDE0-023)
* the request parameter `expires_at` of `POST /v1/payment_links` (HIDE0-024)
* the response key `amount` of `POST /v1/payment_links` (HIDE0-025)
* the response key `created` of `POST /v1/payment_links` (HIDE0-026)
* the response key `description` of `POST /v1/payment_links` (HIDE0-027)
* the response key `expires_at` of `POST /v1/payment_links` (HIDE0-028)
* the operation `POST /v1/payment_links/{payment_link}/expire` (HIDE0-039)
* the query parameter `status` of `GET /v1/payment_links` (HIDE0-054)
* the response key `amount` of `GET /v1/payment_links` (HIDE0-055)
* the response key `created` of `GET /v1/payment_links` (HIDE0-056)
* the response key `description` of `GET /v1/payment_links` (HIDE0-057)
* the response key `expires_at` of `GET /v1/payment_links` (HIDE0-058)
* the response key `amount` of `GET /v1/payment_links/{payment_link}` (HIDE0-065)
* the response key `created` of `GET /v1/payment_links/{payment_link}` (HIDE0-066)
* the response key `description` of `GET /v1/payment_links/{payment_link}` (HIDE0-067)
* the response key `expires_at` of `GET /v1/payment_links/{payment_link}` (HIDE0-068)
* the request parameter `description` of `POST /v1/payment_links/{payment_link}` (HIDE0-076)
* the request parameter `expires_at` of `POST /v1/payment_links/{payment_link}` (HIDE0-077)
* the response key `amount` of `POST /v1/payment_links/{payment_link}` (HIDE0-078)
* the response key `created` of `POST /v1/payment_links/{payment_link}` (HIDE0-079)
* the response key `description` of `POST /v1/payment_links/{payment_link}` (HIDE0-080)
* the response key `expires_at` of `POST /v1/payment_links/{payment_link}` (HIDE0-081)