Docs
BasicsAPI Reference
BasicsAPI Reference
  1. Integration Guide
  • Integration Guide
    • Payment Links
    • Charges
    • Webhooks
  • PaymentLinks
    • Create payment link
      POST
    • List payment links
      GET
    • Get payment link
      GET
    • Cancel payment link
      POST
  • Charges
    • List charges
      GET
    • Get charge
      GET
  • Webhooks
    • Payment Link
    • Charge
  1. Integration Guide

Payment Links

Accept stablecoin payments from customers via a hosted checkout page. You create a payment link — Woldy handles the rest.

What a payment link is#

A payment link is a request for payment: the amount you're charging, an optional message and order reference, and a deadline. It gives you a hosted checkout URL to send to your customer.
The link itself doesn't carry money. Each attempt to pay it creates a charge — the record of what actually happened on-chain. One link can produce several charges: if an attempt doesn't go through, the customer's next attempt creates a new one. See Charges for how to read them.
A payment link is single-use by result, not by attempt: once one charge succeeds, the link is spent and no further attempts are accepted.

Two Ways to Create a Payment Link#

DashboardREST API
Best forNon-technical merchants, one-off paymentsDevelopers, automated checkout flows
Setup time< 1 min~30 min
Requires codeNoYes
Webhooks✅✅
Links created through the Dashboard come back with "source": "DASHBOARD", links created through the API with "source": "API".
Links created through the API are always "type": "FIXED" — you set the amount. Flexible links, where the customer chooses how much to pay, can only be created from the Dashboard.

Quick Start#

Step 1 — Create a payment link#

POST /v1/payment-links with price_amount, price_currency, and expires_at. Optionally pass message (shown on the checkout page), return_url (where the customer lands after paying), and external_id — your own order reference, stored as-is.
FieldConstraint
price_amountDecimal string, greater than zero
price_currencyUSD only
expires_atIn the future, no more than 31 days out
messageMax 512 characters
return_urlAbsolute http/https URL, max 2048 characters
external_idMax 255 characters
The response carries the checkout URL in payment_url:
{
  "id": "pl_AAAA...",
  "branch_id": "cb_AAAA...",
  "reference_type": "PAYMENT_LINK",
  "status": "PENDING",
  "source": "API",
  "type": "FIXED",
  "chain_id": null,
  "price_currency": "USD",
  "price_amount": "10.00",
  "pay_currency": null,
  "pay_amount": null,
  "message": "Order #12345",
  "payment_url": "https://gateway.woldy.xyz/pl/AZ-X2wuJfQ-aFbMIEo_Klg",
  "return_url": "https://example.com/success",
  "external_id": "order_123",
  "expires_at": "2026-08-01T00:00:00Z",
  "created_at": "2026-07-20T14:15:22.123Z",
  "updated_at": "2026-07-20T14:15:22.123Z"
}
There is no idempotency key — sending the same body twice creates two separate links.
Optional fields you didn't set come back as empty strings rather than null, so check for both if you branch on them.
See API Reference: Create payment link

Step 2 — Send the customer to payment_url#

Redirect your customer to the payment_url from the response. On that page the customer will:
1.
Connect their wallet via WalletConnect
2.
Review the payment amount
3.
Sign and submit the transaction
Woldy handles everything from here. No additional code needed on your side.

Step 3 — Listen for the result#

Subscribe to payment_link.* events to follow the request through its lifecycle:
EventFires whenLink status after
payment_link.createdThe link was createdPENDING
payment_link.processingA customer started paying — a charge is in flightPROCESSING
payment_link.attempt_failedThat attempt failed or was abandonedback to PENDING
payment_link.completedAn attempt succeeded — fulfil the order hereCOMPLETED
payment_link.expiredThe deadline passed with no successful paymentEXPIRED
payment_link.cancelledYou cancelled the linkCANCELLED
processing and attempt_failed can repeat: each retry produces another pair. Only completed means you've been paid.
To also see the individual attempts — including the exact token and network paid — subscribe to charge.* as well.
See Webhooks for setup, event payloads, and signature verification.

Step 4 — (Optional) Poll for status#

GET /v1/payment-links/{payment_link_id} returns the link's current status — whether the request was fulfilled. For the attempt-by-attempt history, query charges by reference — see Charges.
See API Reference: Get payment link

Amounts#

FieldMeaning
price_amount / price_currencyWhat you charged. price_currency is USD today
pay_amount / pay_currencyWhat was actually paid, copied from the successful charge. null until the link is COMPLETED
chain_idThe network of the link's most recent charge, in CAIP-2 format — for example eip155:8453 for Base. null until someone has attempted payment
pay_* is a convenience copy so you can read the outcome without a second request. The source of truth for what was paid is always the charge itself.
USD and stablecoins are treated 1:1 today, so pay_amount matches price_amount numerically. When fiat pricing arrives these will diverge — read them as separate values from the start.

Statuses#

StatusMeaning
PENDINGWaiting to be paid
PROCESSINGA customer is paying — an attempt is in flight
COMPLETEDAn attempt succeeded — the link is spent
EXPIREDexpires_at passed with no successful payment
CANCELLEDYou cancelled it
PENDING and PROCESSING are both open states: the link hasn't been paid yet. A failed attempt returns the link to PENDING, ready for the customer to try again — only a successful charge moves it to COMPLETED, no matter how many attempts came before.
Use PROCESSING to show "payment in progress" in your UI, but wait for COMPLETED before fulfilling the order.

Listing links#

GET /v1/payment-links returns the links belonging to your branch:
FilterValue
statusesComma-separated statuses, e.g. PENDING,COMPLETED
sourcesComma-separated: DASHBOARD, API
idsComma-separated link UIDs
external_idExact match on your own reference
created_at_from / created_at_toISO-8601 range
limit / offsetlimit 1–100, defaults to 50
Unrecognized filter values are ignored rather than rejected, so a typo returns unfiltered results instead of an error.
meta.next is always present in the response and doesn't indicate that more results exist — compare offset + limit against meta.total to know when to stop.
See API Reference: List payment links

Cancelling a link#

POST /v1/payment-links/{payment_link_id}/cancel takes no body.
Only PENDING links can be cancelled — a link that is PROCESSING, COMPLETED, EXPIRED, or already CANCELLED returns 400. Cancelling closes any charge still open. Amount and deadline can't be edited — create a new link instead.
See API Reference: Cancel payment link

API Reference#

EndpointMethodDescription
/v1/payment-linksPOSTCreate payment link
/v1/payment-linksGETList payment links
/v1/payment-links/{payment_link_id}GETGet payment link
/v1/payment-links/{payment_link_id}/cancelPOSTCancel payment link

Errors#

Every error uses the same envelope. Validation failures list the offending fields in source:
{
  "code": 400,
  "message": "One or more fields have an error",
  "source": {
    "price_amount": "Must be greater than zero",
    "expires_at": "Must be in future"
  }
}
StatusMeaning
400 Bad RequestValidation failed, or the link isn't PENDING and can't be cancelled
401 UnauthorizedMissing or invalid API key, wrong environment, or blocked by the token's IP allowlist
403 ForbiddenThe token is read-only and this is a write operation
404 Not FoundNo link with that ID — including links that belong to another branch
429 Too Many RequestsRate limit exceeded. Check the Retry-After header
500 Internal Server ErrorSomething went wrong on our end — contact support
Modified at 2026-08-19 16:09:48
Next
Charges
Built with