UlasendUlasendDocs

Dynamic payment links

Use one reusable payment link to issue a secure checkout URL for each order.

Create the payment link once

Create a link under Dashboard → Payment links and enable Dynamic amount, or create it from your server with the API. Keep your API key out of browser code.

curl -X POST https://ulasend.com/api/v1/payment-links \
  -H "Authorization: Bearer $ULASEND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dynamic-store-checkout" \
  -d '{
    "name": "Store checkout",
    "usd_amount": "1.00",
    "asset": "USDC_BASE",
    "allow_dynamic_amount": true
  }'

usd_amount is the fallback when a checkout does not provide its own total. Save the url returned in the response:

{
  "id": "…",
  "slug": "pl_ab12cd34ef",
  "url": "https://ulasend.com/l/pl_ab12cd34ef",
  "usd_amount": "1.00",
  "allow_dynamic_amount": true
}

Create a secure checkout URL for each order

From your server, send the order reference and current cart total to the reusable link's checkout-sessions endpoint. This creates a short-lived signed URL; it does not create a payment before the buyer continues.

curl -X POST \
  https://ulasend.com/api/v1/payment-links/{payment_link_id}/checkout-sessions \
  -H "Authorization: Bearer $ULASEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order_reference": "order_123",
    "usd_amount": "49.99",
    "expires_in_minutes": 30
  }'

Redirect the buyer to the returned checkout_url unchanged. Its opaque token is bound to this payment link, merchant, mode, order reference, USD total, and expiry. The URL does not expose the order reference or amount.

{
  "id": "…",
  "payment_link_id": "…",
  "order_reference": "order_123",
  "usd_amount": "49.99",
  "expires_at": "…",
  "livemode": true,
  "checkout_url": "https://ulasend.com/l/pl_ab12cd34ef?checkout_token=…",
  "replayed": false
}

Repeating the same order returns the existing session. Reusing an order reference with another link, amount, duration, or mode is rejected. The returned expires_at is the final payment deadline and is not renewed when the buyer opens the URL. After the session expires, issue a new order reference.

Choose the payment window

The payment link remains reusable. Each secure checkout session can set its own payment window. If you omit that override, it uses the link's configured duration, then the merchant default under Settings → Payment rules. You can offer 15 minutes, 30 minutes, 1 hour, 3 hours, 12 hours, or 24 hours. An unpaid checkout that expires can be reopened with a fresh checkout and current price.

For Bitcoin, Ethereum, and Litecoin, Ulasend fixes the coin amount when the personal checkout starts and keeps it fixed for the entire payment window. Coin values can rise or fall before the buyer pays. The merchant is responsible for that market movement; shorter windows reduce the risk. More frequent payment scanning does not update the fixed coin amount. USDC is designed to track USD but can also fluctuate.

For Bitcoin, Litecoin, Ethereum, Base ETH, and Solana, automatic discovery continues until one hour after the checkout expires or is cancelled. A payment detected by then continues through confirmation afterward. A new arrival after that discovery horizon requires manual recovery. Base USDC uses a separate checkpointed chain-log listener with no per-address cutoff.

Buyer-visible amounts (compatibility mode)

Existing integrations can append ?usd=49.99or use the drop-in button's data-usd-amount. Those values are visible and editable by the buyer, so use this only when you do not need a trusted order reference or when your server independently reconciles the payment before fulfillment.

<script
  async
  src="https://ulasend.com/embed.js"
  data-ulasend-link="pl_ab12cd34ef"
  data-label="Pay with crypto"
  data-usd-amount="49.99"
></script>

Verify the amount before fulfillment

Store the expected order on your server. When you receive the signedinvoice.paid webhook, match its order_reference, expected_usd_amount, and checkout_session_id to that order before fulfillment. Also confirm livemode, payment status, and your own order state. Never fulfill from a return URL or buyer-visible query parameter. See Webhooks.

Amount rules

  • A per-order dynamic amount can be from $0.50 to $100,000.
  • A checkout session can be valid for 5 to 1,440minutes; omit the override to use the link's configured duration.
  • The fallback usd_amountmust meet the selected asset's normal minimum.
  • The selected asset must be enabled under Settings → Accepted coins.