Debt) is a one-time charge to a known debtor: a fixed amount charged to an identified person or entity, with a due date and configurable payment methods. It is the most common use case of the API.
You specify who pays, how much, and when it is due. TapiPay resolves all of the complexity internally (your organization, the modalities, and the internal data of the collections system) and returns a clean contract with the payment URL ready to share.
When to use a debt
Debt
A single charge to someone you know: an invoice, an installment, a one-time service.
Subscription
If the charge repeats over time (monthly payments, installments).
Payment link
If you do not know the debtor in advance (donations, one-off sales).
Create a debt
To create a debt you need, at a minimum, to identify the debtor, the amount, and the due date. The optionalIdempotency-Key header is used as the debt’s externalRequestId, and the resulting debtId is debt_<key> (in the example, debt_INV-2026-001). If you don’t send it, the backend generates the externalRequestId. See Idempotency.
Identify the debtor
You must send one of these two fields (not both, not neither):
See Contacts for details on the reusable debtor.
A contact may have more than one identifier. If you reference it by a secondary one, the debt is created with its primary identifier and
contact.externalClientId in the response returns that value, not the one you sent.Main fields
number
required
Amount in pesos, with up to 2 decimal places (minimum
0.01). For example, 1500.50 equals $1,500.50 MXN.string
required
Due date in
YYYY-MM-DD format. It must be a valid calendar date (2026-02-30 returns 400). It is not validated to be in the future.string
default:"MXN"
Currency code. Allowed values:
MXN, ARS, PEN, COP, CLP, USD. Default MXN.string
Description visible to the debtor. It must be a string (any other type returns
400).string
ID of an existing Tapi product, in
prd_<number> format (for example, prd_12985). Any other format returns 400 INVALID_REQUEST. Mutually exclusive with externalProductId (sending both returns 400); never creates a product. See Products.string
Your identifier of an existing product. Mutually exclusive with
productId; never creates a product. It does not accept the characters ', ", \, ; (400).boolean
default:"true"
Enables partial payments. With
true, the debt moves to PARTIALLY_PAID until it is settled. Only the literal true or false is accepted: "true", 1, etc. return 400 with details[].boolean
default:"true"
If
false, the debt cannot be paid after the dueDate. Only the literal true or false is accepted, same as allowPartialPayments.object
Data of your own system that you want to store on the debt. It must be a flat object (a scalar, such as a string or a number, returns
400) and it cannot use the reserved keys TapiPay writes internally: description, paymentMethods, productName, externalProductId, debtExpirationDate, overduePayment, allowPartialPayments, recurringDebt, debtReference. If any of them is present, the API responds 400 INVALID_REQUEST with details[].field = additionalData.The debt does not declare a payment method: automatic collection is governed by the default payment method each user registers once. Referencing a product (
productId/externalProductId) is what enables that. The paymentMethods and autopay fields only apply to subscriptions. See Direct debit for the full picture.You never send or receive internal system data (organization, modalities,
generationData). You work only with business concepts.The response
The creation and query response includespaymentUrl (the URL ready to share with your debtor; it is null only if the portal integration fails) and amountPaid (the accumulated amount paid). The creation response also includes autopay (whether automatic collection is active) and, when it is false, the reason in autopayReason. If the debt does not reference a product, the reason is NO_PRODUCT. See Direct debit.
debtIdis alwaysdebt_+externalRequestId.descriptionis returned on creation and on reads (GET /debts/{id},GET /debts) when the debt has one; otherwise the field is absent.contact.contactIdcarries thecon_prefix, also when the contact is created inline.date-timevalues come in UTC with milliseconds andZ. See API conventions.
Payment deep link
ThepaymentUrl is a deep link that points directly to that specific debt. It is an opaque URL: share it as-is with your debtor, and don’t parse it or build it by hand (its format may change). Debts generated by a subscription also carry their own deep link (you see it in GET /subscriptions/{id}/debts).
Lifecycle
Retrieve and list
GET /debts/{id} accepts the debtId (debt_...) or the externalRequestId without prefix. It also returns cancelled debts (status: CANCELLED).
GET /debts returns your debts from newest to oldest, paginated with page and limit (see API conventions). It does not include cancelled debts: to read a cancelled one, use GET /debts/{id}.
Date rule for createdAtFrom and createdAtTo
Date rule for createdAtFrom and createdAtTo
- They accept a date (
YYYY-MM-DD) or an RFC 3339 date-time. Any other format returns400. - A date alone covers the full UTC day, included at both ends:
createdAtFrom=2026-09-24starts at00:00:00.000ZandcreatedAtTo=2026-09-24ends at23:59:59.999Z. With the same date in both, you get the debts of that day. - A date-time is converted to UTC and the filter has daily precision: the full UTC day that contains it is included. A
createdAtToat exactly00:00:00Zdoes not include that day. - Without
createdAtTo, the range runs until today. A futurecreatedAtFromwithoutcreatedAtToreturns an empty list. createdAtFromlater thancreatedAtToreturns400.
Update and cancel
Update (PATCH /debts/{id})
Update (PATCH /debts/{id})
You can only update a debt in
PENDING status. Attempting it in another status, including a cancelled debt, returns 422 BUSINESS_RULE_VIOLATION.Editable fields: amount and dueDate. Send at least one: an empty body or any other field returns 400 INVALID_REQUEST. dueDate uses the YYYY-MM-DD format and must be a valid calendar date. companyCode in the body is taken as context, not as a field to modify.The PATCH does not modify allowPartialPayments. The response carries dueDate as YYYY-MM-DD and amountPaid with the current amount paid.Cancel (POST /debts/{id}/cancel)
Cancel (POST /debts/{id}/cancel)
You can cancel a debt in
PENDING, PARTIALLY_PAID, or OVERDUE status. You cannot cancel a debt that is already PAID or CANCELLED: cancelling it a second time returns 422 BUSINESS_RULE_VIOLATION.The endpoint takes no body (if you send one, it is ignored). The cancelled debt can still be read with GET /debts/{id}, but it no longer appears in GET /debts.Errors
On top of the general catalog, these are the most common error causes in the debt endpoints and theerror.code each one returns. The error.message is informational and may change: branch your logic on the status, error.code, and error.details[].field, not on the text.
Some real messages, for reference only:
When the failure is not a validation one, the API keeps the real status instead of always turning it into a
500, so you can also get a 409 RESOURCE_CONFLICT or a 422 BUSINESS_RULE_VIOLATION outside the cases in the table. These errors carry no error.details, because they do not point at a specific field of your payload.
Endpoints
Try it in the API Reference
Explore each endpoint with its interactive playground.

