Payment links & invoices
Share a hosted link the customer opens to pay, or bill an amount with a deposit address up front.
A payment link mints an unguessable token and a hosted page at /pay/<token> where your customer
pays. There are two symmetric resources — pick by whether the payer chooses the network or you fix it:
| Resource | Create with | Use it for |
|---|---|---|
| checkout | POST /v1/checkouts (ap.checkouts.create) | A shareable hosted link — the payer chooses which token/chain to pay with. |
| invoice | POST /v1/invoices (ap.invoices.create, chain required) | Billing an amount on a specific chain — mints the deposit address up front. |
Both settle the same way and fire the same payment.succeeded webhook. Each is its own
resource with the same create / list / get / update / del methods — the examples below
use checkouts; swap in ap.invoices.* (on /v1/invoices) for the address flow.
Before you start: creating, updating, and voiding need the invoices:write scope; listing and
reading need invoices:read (Authentication).
Create a hosted checkout link
Returns a hosted URL to send the customer to; they pick the asset on the page.
const link = await ap.checkouts.create({
reference: "order-123",
amount: { amount: "25.00", currency: "USDT" },
});
console.log(link.token, link.checkoutUrl);link = ap.checkouts.create(
reference="order-123",
amount={"amount": "25.00", "currency": "USDT"},
)
print(link["token"], link["checkoutUrl"])link, err := ap.Checkouts.Create(ctx, absolutepay.CheckoutParams{
Reference: "order-123",
Amount: absolutepay.Money{Amount: "25.00", Currency: "USDT"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(link.Token, link.CheckoutURL)BODY='{"reference":"order-123","amount":{"amount":"25.00","currency":"USDT"}}'
curl https://api.absolutepay.io/v1/checkouts \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
$(sign POST /v1/checkouts "$BODY") -d "$BODY"The reference is your reconciliation handle — keep it unique per order. Amounts are decimal strings
("25.00"), ≤ 6 fraction digits.
Create an invoice (mint a deposit address up front)
An invoice always mints the on-chain deposit address up front for the chain you pass (required), and
the payer sends funds directly to it. If you'd rather let the payer pick the network on the hosted page,
create a checkout link instead.
const invoice = await ap.invoices.create({
reference: "order-123",
amount: { amount: "25.00", currency: "USDT" },
chain: "MATIC", // mint the deposit address now
});
console.log(invoice.token, invoice.address);invoice = ap.invoices.create(
reference="order-123",
amount={"amount": "25.00", "currency": "USDT"},
chain="MATIC", # mint the deposit address now
)
print(invoice["token"], invoice["address"])invoice, err := ap.Invoices.Create(ctx, absolutepay.InvoiceParams{
Reference: "order-123",
Amount: absolutepay.Money{Amount: "25.00", Currency: "USDT"},
Chain: "MATIC", // mint the deposit address now
})
if err != nil {
log.Fatal(err)
}
fmt.Println(invoice.Token, invoice.Address)BODY='{"reference":"order-123","amount":{"amount":"25.00","currency":"USDT"},"chain":"MATIC"}'
curl https://api.absolutepay.io/v1/invoices \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
$(sign POST /v1/invoices "$BODY") -d "$BODY"Redirect the payer back after payment
Set an optional redirectUrl on create (works for both checkout links and invoices). Once the hosted
page reaches a terminal state it sends the payer back to that URL with the outcome as query params —
so your success page knows which order completed and how it ended.
const link = await ap.checkouts.create({
reference: "order-123",
amount: { amount: "25.00", currency: "USDT" },
redirectUrl: "https://yourstore.com/thank-you",
});link = ap.checkouts.create(
reference="order-123",
amount={"amount": "25.00", "currency": "USDT"},
redirect_url="https://yourstore.com/thank-you",
)link, err := ap.Checkouts.Create(ctx, absolutepay.CheckoutParams{
Reference: "order-123",
Amount: absolutepay.Money{Amount: "25.00", Currency: "USDT"},
RedirectURL: "https://yourstore.com/thank-you",
})BODY='{"reference":"order-123","amount":{"amount":"25.00","currency":"USDT"},"redirectUrl":"https://yourstore.com/thank-you"}'
curl https://api.absolutepay.io/v1/checkouts \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
$(sign POST /v1/checkouts "$BODY") -d "$BODY"After the payment settles, the payer lands on https://yourstore.com/thank-you?token=<token>&status=SUCCESS.
The token is the link token (the same id in /pay/<token>); status is the final outcome:
| Link state | status |
|---|---|
PAID | SUCCESS |
EXPIRED | EXPIRED |
VOID | CANCELED |
Any query string you already put on redirectUrl is preserved (?ref=42 → ?ref=42&token=…&status=SUCCESS).
Confirm settlement server-side
The redirect is a browser navigation — treat token/status as untrusted display hints, never fulfill
an order off them alone. Confirm the real outcome server-side via the payment.succeeded webhook
(primary) or ap.checkouts.get("<token>"). redirectUrl must be an http(s) URL.
List, get, update & void
Every list returns { items, nextCursor } — page by echoing nextCursor back as before
(see Pagination). Filter by status (OPEN | PAID | EXPIRED | VOID) and sort
with order (asc | desc); ?status=PAID is your settled pay-ins.
// List (newest first) and page:
const { items, nextCursor } = await ap.checkouts.list({ status: "OPEN", order: "desc" });
// next page: ap.checkouts.list({ status: "OPEN", before: nextCursor })
// Read one:
const link = await ap.checkouts.get("<token>");
// Pause / resume (reversible):
await ap.checkouts.update("<token>", { paused: true }); // stop accepting payment
await ap.checkouts.update("<token>", { paused: false }); // resume
// Void (terminal — cannot be paid):
await ap.checkouts.del("<token>");# List (newest first) and page:
result = ap.checkouts.list(status="OPEN", order="desc")
items, next_cursor = result["items"], result["nextCursor"]
# next page: ap.checkouts.list(status="OPEN", before=next_cursor)
# Read one:
link = ap.checkouts.get("<token>")
# Pause / resume (reversible):
ap.checkouts.update("<token>", paused=True) # stop accepting payment
ap.checkouts.update("<token>", paused=False) # resume
# Void (terminal — cannot be paid):
ap.checkouts.delete("<token>")// List (newest first) and page:
page, err := ap.Checkouts.List(ctx, absolutepay.PageQuery{Status: "OPEN", Order: "desc"})
if err != nil {
log.Fatal(err)
}
fmt.Println(page.Items, page.NextCursor)
// Read one:
link, err := ap.Checkouts.Get(ctx, "<token>")
// Pause / resume (reversible):
_, err = ap.Checkouts.Update(ctx, "<token>", absolutepay.CheckoutPatch{Paused: absolutepay.Bool(true)})
// Void (terminal — cannot be paid):
_, err = ap.Checkouts.Delete(ctx, "<token>")# List + filter + sort:
curl "https://api.absolutepay.io/v1/checkouts?status=OPEN&order=desc&limit=50" \
-H "Authorization: Bearer $APP_TOKEN" $(sign GET "/v1/checkouts?status=OPEN&order=desc&limit=50" "")
# Get one:
curl "https://api.absolutepay.io/v1/checkouts/<token>" \
-H "Authorization: Bearer $APP_TOKEN" $(sign GET "/v1/checkouts/<token>" "")
# Pause (PATCH):
BODY='{"paused":true}'
curl -X PATCH https://api.absolutepay.io/v1/checkouts/<token> \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
$(sign PATCH /v1/checkouts/<token> "$BODY") -d "$BODY"
# Void (DELETE — terminal):
curl -X DELETE https://api.absolutepay.io/v1/checkouts/<token> \
-H "Authorization: Bearer $APP_TOKEN" $(sign DELETE /v1/checkouts/<token> "")update patches a link in place — paused toggles whether it accepts payment (reversible), and you
can also revise redirectUrl, expiresAt, or description (pass null to clear a field). del
voids the link: it's terminal and can never be paid. The same methods exist on ap.invoices.*.
Confirm settlement
Don't fulfill until a link reaches PAID. Use the payment.succeeded webhook as the
primary signal; as a fallback, read the link back with ap.checkouts.get("<token>") (or
ap.invoices.get) and check status === "PAID".
Gotchas
- The token IS the credential. Anyone with the link can view and pay it — share the URL only with the intended payer, and void links you no longer want payable.
- Send the exact quoted amount to the deposit address. The quote is live; the invoice response includes the precise amount the payer must transfer.
- Amounts are decimal strings (
"25.00"), positive, ≤ 6 fraction digits. - In the sandbox, links don't settle on their own — trigger settlement yourself ("Simulate
payment" in the dashboard); the same
payment.succeededwebhook fires.
Next
- Accept payments — the hosted checkout flow for in-app orders.
- Receiving deposits — permanent top-up addresses for your own workspace balance.
- Webhooks — fulfill on
payment.succeeded. - API reference — every field on checkouts + invoices.