Skip to main content
BillingPayment Links

Payment Links.

A payment link is a reusable, shareable URL — https://opensettle.io/pay/<token> — that spawns a fresh checkout on every visit, so one link can collect many payments. Unlike a single-use checkout, it never expires after a payment: drop it in a pricing page, an email signature, a QR code, or a social bio and let buyers pay on their own time. Buyers check out as guests — no email or account required.

Amount modes

Every link fixes a settlement rail (chain + token, both required) and exactly one amount source:

  • amounta fixed charge in minor units (cents; USDC and USDT are USD-pegged, so 2500 = $25.00). Requires a description.
  • priceIda saved one-time price; the link charges that price's amount and currency.
  • openAmount: true“name your price” / top-up: the buyer types the amount on the pay page. Bound it with minAmount / maxAmount (minor units) and offer presetAmounts quick-pick chips (1–8 unique values). The floor defaults to $0.50 when you don't set minAmount. An open-amount link also requires a description.

A fixed-amount link can't be underpaid, and an open-amount link is validated against your bounds when each checkout spawns — so the rail and the limits you set always hold.

Create a link

Four SDKs, one shape. Supply exactly one amount source plus chain + token, then share the returned url. All amounts are in minor units (cents). The example below is an open-amount tip jar with a $5.00 floor.

create-payment-link.ts
import { OpenSettle } from "@opensettle/sdk";

const os = new OpenSettle({
  apiKey: process.env.OPENSETTLE_KEY!,
  workspaceId: process.env.OPENSETTLE_WORKSPACE!,
});

// Open amount — "name your price" / tip jar (amounts in minor units / cents)
const link = await os.paymentLinks.create({
  openAmount: true,
  minAmount: 500,                 // $5.00 floor
  presetAmounts: [500, 1000, 2500],
  description: "Tip jar",
  currency: "USD",
  chain: "base",
  token: "USDC",
});

// Share this anywhere — every visit spawns a fresh checkout.
console.log(link.url); // https://opensettle.io/pay/<token>
create_payment_link.py
import os
from opensettle import OpenSettle

client = OpenSettle(
    api_key=os.environ["OPENSETTLE_API_KEY"],
    workspace_id=os.environ["OPENSETTLE_WORKSPACE_ID"],
)

# Open amount — buyer names the price. Amounts are minor units (cents).
link = client.payment_links.create(
    openAmount=True,
    minAmount=500,                 # $5.00 floor
    presetAmounts=[500, 1000, 2500],
    description="Tip jar",
    currency="USD",
    chain="base",
    token="USDC",
)

print(link["url"])  # https://opensettle.io/pay/<token>
create_payment_link.go
client := opensettle.NewClient(
  os.Getenv("OPENSETTLE_API_KEY"),
  opensettle.WithWorkspace(os.Getenv("OPENSETTLE_WORKSPACE_ID")),
)

// Amount-bearing fields are *T pointers; tiny helpers keep the call tidy.
ptrBool := func(v bool) *bool { return &v }
ptrInt64 := func(v int64) *int64 { return &v }
ptrStr := func(v string) *string { return &v }

// Open amount — buyer names the price. Amounts are minor units (cents).
link, err := client.PaymentLinks.Create(ctx, opensettle.CreatePaymentLinkRequest{
  OpenAmount:    ptrBool(true),
  MinAmount:     ptrInt64(500), // $5.00 floor
  PresetAmounts: []int64{500, 1000, 2500},
  Description:   ptrStr("Tip jar"),
  Currency:      ptrStr("USD"),
  Chain:         opensettle.ChainBase,
  Token:         opensettle.TokenUSDC,
})
if err != nil {
  return err
}

fmt.Println(link.URL) // https://opensettle.io/pay/<token>
create_payment_link.rs
use opensettle::{Client, CreatePaymentLinkRequest, ChainId, TokenSymbol};

let client = Client::new(
    std::env::var("OPENSETTLE_API_KEY")?,
    std::env::var("OPENSETTLE_WORKSPACE_ID")?,
);

// CreatePaymentLinkRequest has no Default — construct every field.
// Amounts are minor units (cents).
let link = client
    .payment_links()
    .create(&CreatePaymentLinkRequest {
        amount: None,
        price_id: None,
        open_amount: Some(true),
        min_amount: Some(500), // $5.00 floor
        max_amount: None,
        preset_amounts: Some(vec![500, 1000, 2500]),
        description: Some("Tip jar".into()),
        currency: Some("USD".into()),
        chain: ChainId::Base,
        token: TokenSymbol::USDC,
        success_url: None,
        metadata: None,
    })
    .await?;

println!("{}", link.url); // https://opensettle.io/pay/<token>

Fixed amount

Swap the open-amount fields for a single amount(minor units) to collect a set price. The link can't be underpaid.

fixed-amount-link.ts
// $25.00 fixed — amount is in minor units (cents)
const link = await os.paymentLinks.create({
  amount: 2500,
  description: "Workshop ticket",
  currency: "USD",
  chain: "base",
  token: "USDC",
});

return link.url;

REST API

Under the hood the SDKs call POST /v1/workspaces/{workspaceId}/payment_links. The body takes exactly one of amount, priceId, or openAmount, plus the open-amount bounds (minAmount, maxAmount, presetAmounts), description, currency, the required chain + token, optional successUrl, and metadata.

bash
# Open-amount link with a $5.00 floor and quick-pick chips
curl -X POST \
  -H "Authorization: Bearer $OPENSETTLE_API_KEY" \
  -H "Content-Type: application/json" \
  "https://api.opensettle.io/v1/workspaces/$WORKSPACE_ID/payment_links" \
  -d '{
    "openAmount": true,
    "minAmount": 500,
    "presetAmounts": [500, 1000, 2500],
    "description": "Tip jar",
    "currency": "USD",
    "chain": "base",
    "token": "USDC"
  }'

The 201 response wraps the resource as { "paymentLink": { … } }:

  • id, url, description, priceId, amountMinor, openAmount, minAmountMinor, maxAmountMinor, presetAmounts, currency, chain, token, successUrl, active, createdAt

For a fixed or price-backed link openAmount is false and the open-amount fields are null; amountMinor is 0 for an open-amount or price-backed link.

List + deactivate

GET /payment_links returns every link in the workspace as a flat { "data": [ … ] } envelope (not cursor-paginated). DELETE /payment_links/{id} deactivates a link — its /pay URL stops spawning new checkouts, while checkouts already created from it are unaffected. The server responds { "ok": true }.

list-and-deactivate.ts
// List every payment link (not paginated)
const links = await os.paymentLinks.list();

// Stop a link from spawning new checkouts
await os.paymentLinks.deactivate(links[0].id);