Skip to main content

Overview

Create a payment link for collecting payments from customers. The link can be one-time (default) or recurring. Same handler and validation as the internal create payment link; default behavior is one-time with no breaking changes. Endpoint: POST /v1/ext/collections/payment-link Auth: Bearer token with scope external.apis (authorizer: walletos-account-service-{stage}-authorizer).

Default vs recurring

ModeHow to requestResult
One-time (default)Omit checkoutType and recurringCharge, or send checkoutType: "generic".Single-use or multi-use payment link; customer pays once (or multiple times if isOpenLink is true).
RecurringSend checkoutType: "recurring" and a valid recurringCharge object.Payment link that creates a subscription (fixed, consumption, or seating) after the customer completes checkout.
No breaking changes: Existing callers that do not send checkoutType or recurringCharge continue to get one-time payment links.

Request

URL

POST {{LIQUIDITY_URL}}/v1/ext/collections/payment-link

Headers

HeaderRequiredDescription
AuthorizationYesBearer <token> with scope external.apis.
Content-TypeYesapplication/json.

Body (JSON) – common fields

FieldTypeRequiredDescription
currencystringYesCurrency code. Supported: USD, NGN, CAD, GBP, EUR.
namestringNoDisplay name for the link (default: "page").
amountnumberYes*Amount per payment. *For one-time: min 0.5. For recurring subscription: required (min 0.5). For consumption/seating: optional (min 0).
customUrlPathstringNoSlug for the URL (default: "page").
redirectTostringNoURI to redirect after payment (default: "https://www.centryos.xyz").
expiredAtstringNoISO date when the link expires (default: one year from now).
checkoutTypestringNo"generic" (default), "ecommerce", or "recurring". Use "recurring" for recurring payment links.
recurringChargeobjectNo**Required when checkoutType === "recurring". Forbidden otherwise. See Recurring body.
isOpenLinkbooleanNoIf true, link can be used multiple times (default: true).
customerPaysbooleanNoIf true, customer pays fees (default: true).
orderIdstringNoYour order/reference id.
dataCollectionsarrayNoExtra data to collect (e.g. ["Phone number"]). Base set always includes Email, First name, Last name, Phone number.
customFieldsarrayNoCustom field names.
acceptedPaymentOptionsarrayNoe.g. ["card"] (default).
notifyPayeebooleanNoNotify payee (default: false).
agentobjectNoAgent details (type, email, name, etc.) for notifications.
brandingConfigobjectNoLogo, colors, etc.
advancedConfigobjectNowebsiteUrl, webhookPath, webhookSecret for external webhook registration when using /ext path.
customDataobjectNoKey-value metadata (string values only).
cartItemsarrayNoCart items; if present, total amount is derived from cart and amount can be omitted per validation.
externalIdstringNoYour external reference.

Recurring body

When checkoutType is "recurring", you must send recurringCharge with this shape:
FieldTypeRequiredDescription
typestringYes"subscription", "consumption", or "seating".
startDatestringYesISO date when billing starts (trial end). Must be before expiredAt.
intervalobjectYesBilling interval.
interval.typestringYes"day", "week", "month", or "year".
interval.countnumberYese.g. 1 for monthly.
productIdstringNoProduct ID for recurring billing (optional).
consumptionDetailsobjectYes if type is consumptionSee below.
seatDetailsobjectYes if type is seatingSee below.
When type === "consumption":
FieldTypeRequiredDescription
unitstringYese.g. "API call", "GB".
costPerUnitnumberYesPrice per unit.
minimumUnitsnumberNoIncluded units before charges apply.
baseFeenumberConditionalRequired if minimumUnits is set.
When type === "seating":
FieldTypeRequiredDescription
baseFeenumberYesFixed fee per period.
costPerSeatnumberYesPrice per seat.
newSeatBillingTypestringYes"prorate", "next_cycle", or "immediate".

Examples

curl -X POST "{{LIQUIDITY_URL}}/v1/ext/collections/payment-link" \
  -H "Authorization: Bearer {{accessToken}}" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "name": "One-time donation",
    "amount": 25.00,
    "customUrlPath": "donate",
    "redirectTo": "https://example.com/thanks"
  }'
Omit checkoutType and recurringCharge to get the existing one-time behavior.

Recurring – fixed subscription

{
  "currency": "USD",
  "name": "Monthly plan",
  "amount": 29.99,
  "customUrlPath": "monthly-plan",
  "redirectTo": "https://example.com/thanks",
  "checkoutType": "recurring",
  "recurringCharge": {
    "type": "subscription",
    "startDate": "2026-02-01",
    "interval": { "type": "month", "count": 1 }
  }
}

Recurring – consumption (usage-based)

{
  "currency": "USD",
  "name": "API usage",
  "amount": 0,
  "customUrlPath": "api-usage",
  "redirectTo": "https://example.com/thanks",
  "checkoutType": "recurring",
  "recurringCharge": {
    "type": "consumption",
    "startDate": "2026-02-01",
    "interval": { "type": "month", "count": 1 },
    "consumptionDetails": {
      "unit": "API call",
      "costPerUnit": 0.10,
      "minimumUnits": 1000,
      "baseFee": 20
    }
  }
}

Recurring – seating (per-seat)

{
  "currency": "USD",
  "name": "Team plan",
  "amount": 0,
  "customUrlPath": "team-plan",
  "redirectTo": "https://example.com/thanks",
  "checkoutType": "recurring",
  "recurringCharge": {
    "type": "seating",
    "startDate": "2026-02-01",
    "interval": { "type": "month", "count": 1 },
    "seatDetails": {
      "baseFee": 20,
      "costPerSeat": 5,
      "newSeatBillingType": "prorate"
    }
  }
}

Response

Success (200)

{
  "success": true,
  "data": {
    "url": "<checkout-widget-url>",
    "application": {
      "id": "<payment-link-uuid>",
      "token": "<token>",
      "tokenType": "<type>",
      "expiredAt": "<ISO-date>",
      "valid": true
    }
  }
}
  • data.url is the payment link URL to share with customers.
  • For recurring links, after the customer completes checkout (payment method saved), a subscription is created and billing follows the configured interval and type.

Error (4xx / 5xx)

{
  "success": false,
  "message": "<error detail or validation errors>",
  "fatal": true
}
SituationHTTPNotes
Unauthorized401Invalid or expired token, or missing external.apis scope.
Validation error400Missing/invalid fields (e.g. amount, currency, or recurringCharge when checkoutType is recurring).
recurringCharge when not recurring400recurringCharge is only allowed when checkoutType is "recurring".
Missing recurringCharge400When checkoutType is "recurring", recurringCharge is required with valid type, startDate, interval, and type-specific details.
Collection account not found400No collection account for the business/currency.
Server error500e.g. database or downstream failure.

Validation summary

  • currency – Required, one of supported types.
  • amount – Required for one-time and for recurring subscription (min 0.5). Optional for consumption/seating (min 0).
  • checkoutType – Optional. Default: "generic". Use "recurring" for recurring payment links.
  • recurringCharge – Allowed only when checkoutType === "recurring". Required then, with type, startDate, interval; plus consumptionDetails or seatDetails depending on type.
  • startDate – Must be before expiredAt.

Recurring billing behavior

For recurring links, the customer is first asked to save a payment method (no charge at link use). When the setup succeeds, a subscription is created and charges follow the configured model (subscription, consumption, or seating). Billing cycles, seat changes, and usage reporting follow the platform’s recurring payments behavior.

Changelog

  • Recurring via External API: Create payment link supports checkoutType: "recurring" and recurringCharge on POST /v1/ext/collections/payment-link. Default remains one-time when these fields are omitted (no breaking changes).