PaymentLink) is a public payment URL that you can share without knowing the debtor in advance. When you create the link, TapiPay generates an associated debt, which is the one that receives the payment; its ID comes in the debtId field of the response.
When to use it: charges without a debtor roster (donations, one-off sales, checkout links over WhatsApp or email), where you know the payer at the moment of payment, not before. If you already know the debtor, a debt is usually better.
Two usage modes
Single use
singleUse: true (default). Deactivated after the first payment. Ideal for a single invoice or one-off sale.Reusable
singleUse: false. Created without a debtor and accepts multiple payments while it stays active. Donations or payment gateway mode.Create a link
The minimum is the amount, a description visible on the payment page, and the expiration date.Main fields
number
required
Amount in pesos, with up to 2 decimal places (minimum
0.01).string
required
Description visible on the payment page. Must be a string (otherwise
400 INVALID_REQUEST).string
required
Expiration date and time, required and in the future. Must be an RFC 3339 date-time, with
Z or with an offset (2026-12-31T23:59:59Z, 2026-12-31T17:59:59-06:00). A value like 2026-12-31 23:59 or a date alone returns 400 INVALID_REQUEST. It is stored in UTC (see Link dates).string
Natural key of the link. Optional, but if you send it, it must be unique per organization: a duplicate returns
409 RESOURCE_CONFLICT and no debt is created. See Idempotency.boolean
default:"true"
true deactivates after the first payment; false accepts multiple payments.string
Your own identifier for the link (optional). It is sanitized to a URL-safe format (lowercase, alphanumerics and hyphens, maximum 64 characters). If it ends up empty after sanitizing, it returns
400 INVALID_REQUEST.string
Identifier of a debtor that already exists as a contact. Mutually exclusive with
contactData. If it does not match an existing contact, the API returns 422 BUSINESS_RULE_VIOLATION and nothing is created. To create the contact in the same request, use contactData.object
Inline debtor data (alternative to
externalClientId).string
Redirect URL when the payment is completed. If you do not send it, the Tapi page is used.
object
Data of your own system. Must be a flat object, with the same reserved keys as in debts (see API conventions). It is stored in the debt associated with the link and is not returned in the link.
currency (default MXN), productName, and metadata (flat object). Undeclared fields are ignored.
Idempotency
The idempotency key of a link is itsexternalPaymentLinkId:
- If you send it in the body, that value is used.
- If you do not send it but you send the
Idempotency-Keyheader, the header value is used as theexternalPaymentLinkId. If you send both, the header is ignored. - If you send neither, the link has no key of its own and each request creates a new link. In the response,
externalPaymentLinkIdisnull.
externalPaymentLinkId that already exists returns 409 RESOURCE_CONFLICT and no debt is created. To retrieve the original link, look it up with GET /payment-links?externalPaymentLinkId=....
The response
The link always includesexternalPaymentLinkId, expiresAt, successUrl, metadata, identifierValue, contact, and product, with null when there is no value. contact is always null in this version.
The identifierValue and the payment URL
The paymentUrl is an opaque URL: use it as it comes, without parsing it or building it yourself. With Private Links enabled it carries a short code and does not contain the identifierValue. Without Private Links, or if the short code cannot be generated, it is the URL in the portal payment link format. If the portal integration fails, it comes as null and the link is created anyway. Links created before this change keep their previous URL.
How the identifierValue is built depends on whether the link has a known debtor:
- Without a known debtor
- With a known debtor
This is the typical case for a link. The
identifierValue is exposed in the response, so you can use it to correlate the link with your system.- If you sent an
identifierValue, your sanitized value is used. - If not, TapiPay generates a synthetic one with the format
{3letters}-{random}(the 3 letters come from your organization’s name; for example, “Acme Corp” producesacm-x3kM9pQr).
Link dates
expiresAt is always returned in UTC with milliseconds and Z (YYYY-MM-DDTHH:mm:ss.sssZ), even if you sent it with an offset:
Older links stored with a date alone (
2026-12-01) are returned as the start of that UTC day (2026-12-01T00:00:00.000Z). The associated debt is due on the UTC date of expiresAt: in the first example, 2027-02-11.
Lifecycle
Update without invalidating the URL
PATCH /payment-links/{id} lets you change three fields without changing the paymentUrl: the link stays accessible from the same URL.
string
New description. Must be a non-empty string.
string
New expiration, an RFC 3339 date-time in the future.
null returns 400: links always expire. It is stored in UTC and also updates the due date of the associated debt to the UTC date of the new value (for example, 2027-02-10T23:30:00-06:00 makes the debt due on 2027-02-11).object
New metadata, a flat object. It fully replaces the previous one.
null or a value that is not an object returns 400.PATCH updates only the link. description and metadata only change the link, not its debt. An empty body or any other field (amount, currency, singleUse, etc.) returns 400 INVALID_REQUEST. A link in PAID, EXPIRED, or CANCELLED status cannot be edited: it returns 422 BUSINESS_RULE_VIOLATION, and that status is checked before the body.
Cancel
POST /payment-links/{id}/cancel moves the link to CANCELLED and also cancels its associated debt. If cancelling the debt fails, the link is cancelled anyway. Cancelling a link that is already cancelled returns 200 with no changes; a PAID or EXPIRED link cannot be cancelled (422 BUSINESS_RULE_VIOLATION). The response keeps the paymentUrl.
List links
GET /payment-links returns your links paginated, sorted by externalPaymentLinkId descending (not by creation date).
This is the only list whose
meta includes capped: it is true when meta.total is approximate because the maximum number of items the service scans was reached.
Payments of a link
GET /payment-links/{id}/payments returns the debts associated with the link, with their status and amountPaid. A newly created link returns its PENDING debt with amountPaid: 0. The paymentId is the internal identifier of that debt (debt_ + a number): it is not the link’s debtId, so use the debtId to read the debt with GET /debts/{id}. Cancelled debts are not listed.
GET /debts: status, createdAtFrom / createdAtTo (with the same UTC-day date rule), minAmount, maxAmount, externalClientId, batchId, page, and limit. Its meta does not include capped.
Endpoints
Try it in the API Reference
Explore each endpoint with its interactive playground.

