curl --request POST \
--url https://tapipay-facade.homo.tapila.cloud/subscriptions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--header 'x-authorization-token: <api-key>' \
--data '
{
"externalSubscriptionId": "<string>",
"amount": 1.01,
"intervalCount": 2,
"startDate": "2023-12-25",
"productName": "<string>",
"externalClientId": "<string>",
"currency": "MXN",
"totalCycles": 260,
"endDate": "2023-12-25",
"description": "<string>",
"allowOverduePayment": true,
"allowPartialPayments": true,
"enforcePaymentOrder": false,
"autopay": false,
"paymentMethods": [],
"additionalData": {
"source": "crm",
"folio": "F-2026-0042"
}
}
'import requests
url = "https://tapipay-facade.homo.tapila.cloud/subscriptions"
payload = {
"externalSubscriptionId": "<string>",
"amount": 1.01,
"intervalCount": 2,
"startDate": "2023-12-25",
"productName": "<string>",
"externalClientId": "<string>",
"currency": "MXN",
"totalCycles": 260,
"endDate": "2023-12-25",
"description": "<string>",
"allowOverduePayment": True,
"allowPartialPayments": True,
"enforcePaymentOrder": False,
"autopay": False,
"paymentMethods": [],
"additionalData": {
"source": "crm",
"folio": "F-2026-0042"
}
}
headers = {
"x-api-key": "<api-key>",
"x-authorization-token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-api-key': '<api-key>',
'x-authorization-token': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
externalSubscriptionId: '<string>',
amount: 1.01,
intervalCount: 2,
startDate: '2023-12-25',
productName: '<string>',
externalClientId: '<string>',
currency: 'MXN',
totalCycles: 260,
endDate: '2023-12-25',
description: '<string>',
allowOverduePayment: true,
allowPartialPayments: true,
enforcePaymentOrder: false,
autopay: false,
paymentMethods: [],
additionalData: {source: 'crm', folio: 'F-2026-0042'}
})
};
fetch('https://tapipay-facade.homo.tapila.cloud/subscriptions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://tapipay-facade.homo.tapila.cloud/subscriptions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'externalSubscriptionId' => '<string>',
'amount' => 1.01,
'intervalCount' => 2,
'startDate' => '2023-12-25',
'productName' => '<string>',
'externalClientId' => '<string>',
'currency' => 'MXN',
'totalCycles' => 260,
'endDate' => '2023-12-25',
'description' => '<string>',
'allowOverduePayment' => true,
'allowPartialPayments' => true,
'enforcePaymentOrder' => false,
'autopay' => false,
'paymentMethods' => [
],
'additionalData' => [
'source' => 'crm',
'folio' => 'F-2026-0042'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>",
"x-authorization-token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://tapipay-facade.homo.tapila.cloud/subscriptions"
payload := strings.NewReader("{\n \"externalSubscriptionId\": \"<string>\",\n \"amount\": 1.01,\n \"intervalCount\": 2,\n \"startDate\": \"2023-12-25\",\n \"productName\": \"<string>\",\n \"externalClientId\": \"<string>\",\n \"currency\": \"MXN\",\n \"totalCycles\": 260,\n \"endDate\": \"2023-12-25\",\n \"description\": \"<string>\",\n \"allowOverduePayment\": true,\n \"allowPartialPayments\": true,\n \"enforcePaymentOrder\": false,\n \"autopay\": false,\n \"paymentMethods\": [],\n \"additionalData\": {\n \"source\": \"crm\",\n \"folio\": \"F-2026-0042\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("x-authorization-token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://tapipay-facade.homo.tapila.cloud/subscriptions")
.header("x-api-key", "<api-key>")
.header("x-authorization-token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"externalSubscriptionId\": \"<string>\",\n \"amount\": 1.01,\n \"intervalCount\": 2,\n \"startDate\": \"2023-12-25\",\n \"productName\": \"<string>\",\n \"externalClientId\": \"<string>\",\n \"currency\": \"MXN\",\n \"totalCycles\": 260,\n \"endDate\": \"2023-12-25\",\n \"description\": \"<string>\",\n \"allowOverduePayment\": true,\n \"allowPartialPayments\": true,\n \"enforcePaymentOrder\": false,\n \"autopay\": false,\n \"paymentMethods\": [],\n \"additionalData\": {\n \"source\": \"crm\",\n \"folio\": \"F-2026-0042\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://tapipay-facade.homo.tapila.cloud/subscriptions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["x-authorization-token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalSubscriptionId\": \"<string>\",\n \"amount\": 1.01,\n \"intervalCount\": 2,\n \"startDate\": \"2023-12-25\",\n \"productName\": \"<string>\",\n \"externalClientId\": \"<string>\",\n \"currency\": \"MXN\",\n \"totalCycles\": 260,\n \"endDate\": \"2023-12-25\",\n \"description\": \"<string>\",\n \"allowOverduePayment\": true,\n \"allowPartialPayments\": true,\n \"enforcePaymentOrder\": false,\n \"autopay\": false,\n \"paymentMethods\": [],\n \"additionalData\": {\n \"source\": \"crm\",\n \"folio\": \"F-2026-0042\"\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"subscriptionId": "sub_839d28e1-a175-40c7-a54b-55f6c17b7964",
"externalSubscriptionId": "SUB-2026-007",
"status": "ACTIVE",
"scheduling": {
"intervalUnit": "month",
"intervalCount": 1,
"startDate": "2026-06-15",
"totalCycles": 12,
"endDate": null,
"billingDay": {
"day": 15
}
},
"externalClientId": "CLI-00042",
"amount": 999,
"currency": "MXN",
"paymentUrl": "https://app.tapipay.la/s/acme-corp/portal/aB3dE5fG7h/debts",
"enforcePaymentOrder": false,
"autopay": false,
"allowOverduePayment": true,
"allowPartialPayments": true,
"paymentMethods": [
"CASH",
"CARD"
],
"description": "Plan Gimnasio",
"additionalData": {
"source": "crm"
},
"contact": {
"contactId": "con_9aL3kP2m",
"externalClientId": "CLI-00042",
"createdInline": false
},
"product": {
"productId": "prd_42",
"name": "Plan Gimnasio",
"active": true,
"createdInline": true
},
"createdAt": "2026-06-03T18:15:00.000Z",
"updatedAt": "2026-06-03T18:15:00.000Z"
},
"requestId": "<string>"
}Crear suscripción
Crea un cobro recurrente; genera todas las deudas del ciclo por adelantado.
curl --request POST \
--url https://tapipay-facade.homo.tapila.cloud/subscriptions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--header 'x-authorization-token: <api-key>' \
--data '
{
"externalSubscriptionId": "<string>",
"amount": 1.01,
"intervalCount": 2,
"startDate": "2023-12-25",
"productName": "<string>",
"externalClientId": "<string>",
"currency": "MXN",
"totalCycles": 260,
"endDate": "2023-12-25",
"description": "<string>",
"allowOverduePayment": true,
"allowPartialPayments": true,
"enforcePaymentOrder": false,
"autopay": false,
"paymentMethods": [],
"additionalData": {
"source": "crm",
"folio": "F-2026-0042"
}
}
'import requests
url = "https://tapipay-facade.homo.tapila.cloud/subscriptions"
payload = {
"externalSubscriptionId": "<string>",
"amount": 1.01,
"intervalCount": 2,
"startDate": "2023-12-25",
"productName": "<string>",
"externalClientId": "<string>",
"currency": "MXN",
"totalCycles": 260,
"endDate": "2023-12-25",
"description": "<string>",
"allowOverduePayment": True,
"allowPartialPayments": True,
"enforcePaymentOrder": False,
"autopay": False,
"paymentMethods": [],
"additionalData": {
"source": "crm",
"folio": "F-2026-0042"
}
}
headers = {
"x-api-key": "<api-key>",
"x-authorization-token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-api-key': '<api-key>',
'x-authorization-token': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
externalSubscriptionId: '<string>',
amount: 1.01,
intervalCount: 2,
startDate: '2023-12-25',
productName: '<string>',
externalClientId: '<string>',
currency: 'MXN',
totalCycles: 260,
endDate: '2023-12-25',
description: '<string>',
allowOverduePayment: true,
allowPartialPayments: true,
enforcePaymentOrder: false,
autopay: false,
paymentMethods: [],
additionalData: {source: 'crm', folio: 'F-2026-0042'}
})
};
fetch('https://tapipay-facade.homo.tapila.cloud/subscriptions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://tapipay-facade.homo.tapila.cloud/subscriptions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'externalSubscriptionId' => '<string>',
'amount' => 1.01,
'intervalCount' => 2,
'startDate' => '2023-12-25',
'productName' => '<string>',
'externalClientId' => '<string>',
'currency' => 'MXN',
'totalCycles' => 260,
'endDate' => '2023-12-25',
'description' => '<string>',
'allowOverduePayment' => true,
'allowPartialPayments' => true,
'enforcePaymentOrder' => false,
'autopay' => false,
'paymentMethods' => [
],
'additionalData' => [
'source' => 'crm',
'folio' => 'F-2026-0042'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>",
"x-authorization-token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://tapipay-facade.homo.tapila.cloud/subscriptions"
payload := strings.NewReader("{\n \"externalSubscriptionId\": \"<string>\",\n \"amount\": 1.01,\n \"intervalCount\": 2,\n \"startDate\": \"2023-12-25\",\n \"productName\": \"<string>\",\n \"externalClientId\": \"<string>\",\n \"currency\": \"MXN\",\n \"totalCycles\": 260,\n \"endDate\": \"2023-12-25\",\n \"description\": \"<string>\",\n \"allowOverduePayment\": true,\n \"allowPartialPayments\": true,\n \"enforcePaymentOrder\": false,\n \"autopay\": false,\n \"paymentMethods\": [],\n \"additionalData\": {\n \"source\": \"crm\",\n \"folio\": \"F-2026-0042\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("x-authorization-token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://tapipay-facade.homo.tapila.cloud/subscriptions")
.header("x-api-key", "<api-key>")
.header("x-authorization-token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"externalSubscriptionId\": \"<string>\",\n \"amount\": 1.01,\n \"intervalCount\": 2,\n \"startDate\": \"2023-12-25\",\n \"productName\": \"<string>\",\n \"externalClientId\": \"<string>\",\n \"currency\": \"MXN\",\n \"totalCycles\": 260,\n \"endDate\": \"2023-12-25\",\n \"description\": \"<string>\",\n \"allowOverduePayment\": true,\n \"allowPartialPayments\": true,\n \"enforcePaymentOrder\": false,\n \"autopay\": false,\n \"paymentMethods\": [],\n \"additionalData\": {\n \"source\": \"crm\",\n \"folio\": \"F-2026-0042\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://tapipay-facade.homo.tapila.cloud/subscriptions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["x-authorization-token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalSubscriptionId\": \"<string>\",\n \"amount\": 1.01,\n \"intervalCount\": 2,\n \"startDate\": \"2023-12-25\",\n \"productName\": \"<string>\",\n \"externalClientId\": \"<string>\",\n \"currency\": \"MXN\",\n \"totalCycles\": 260,\n \"endDate\": \"2023-12-25\",\n \"description\": \"<string>\",\n \"allowOverduePayment\": true,\n \"allowPartialPayments\": true,\n \"enforcePaymentOrder\": false,\n \"autopay\": false,\n \"paymentMethods\": [],\n \"additionalData\": {\n \"source\": \"crm\",\n \"folio\": \"F-2026-0042\"\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"subscriptionId": "sub_839d28e1-a175-40c7-a54b-55f6c17b7964",
"externalSubscriptionId": "SUB-2026-007",
"status": "ACTIVE",
"scheduling": {
"intervalUnit": "month",
"intervalCount": 1,
"startDate": "2026-06-15",
"totalCycles": 12,
"endDate": null,
"billingDay": {
"day": 15
}
},
"externalClientId": "CLI-00042",
"amount": 999,
"currency": "MXN",
"paymentUrl": "https://app.tapipay.la/s/acme-corp/portal/aB3dE5fG7h/debts",
"enforcePaymentOrder": false,
"autopay": false,
"allowOverduePayment": true,
"allowPartialPayments": true,
"paymentMethods": [
"CASH",
"CARD"
],
"description": "Plan Gimnasio",
"additionalData": {
"source": "crm"
},
"contact": {
"contactId": "con_9aL3kP2m",
"externalClientId": "CLI-00042",
"createdInline": false
},
"product": {
"productId": "prd_42",
"name": "Plan Gimnasio",
"active": true,
"createdInline": true
},
"createdAt": "2026-06-03T18:15:00.000Z",
"updatedAt": "2026-06-03T18:15:00.000Z"
},
"requestId": "<string>"
}Autorizaciones
API key del API Gateway de Tapi. Requerida en todas las operaciones. Distinta por ambiente (desarrollo, homologación, producción).
Token TAPI JWT. Requerido en todas las operaciones. Se envía sin prefijo "Bearer". Ej: x-authorization-token: eyJhbGci...
Cuerpo
Uno de externalClientId o contactData es obligatorio, y son excluyentes. Los campos no declarados se ignoran. Si la programación (intervalUnit, intervalCount, startDate, totalCycles, endDate, billingDay) es inválida, no se crea nada: ni el producto ni el contacto inline. La llave de idempotencia es externalSubscriptionId: repetirla devuelve 409 RESOURCE_CONFLICT. Las deudas generadas usan externalRequestId = <externalSubscriptionId>-cycle-<n>, con n desde 0 (la del primer ciclo es -cycle-0).
Llave natural de la suscripción. Única por organización. Repetirla devuelve 409 RESOURCE_CONFLICT.
Monto por ciclo en pesos (hasta 2 decimales, mínimo 0.01)
x >= 0.01Debe ser un múltiplo de 0.01Unidad del intervalo, en minúsculas exactas (day, week, month, year). Es case-sensitive: MONTH devuelve 400.
day, week, month, year Cantidad de unidades por ciclo (entero mayor o igual a 1)
x >= 1Fecha de inicio de la suscripción (YYYY-MM-DD, fecha de calendario válida)
Nombre del producto a asociar (obligatorio). Se resuelve por name o se crea inline si no existe. Identificado por name único por companyCode.
1Identificador del deudor (externalClientId o contactData, no ambos).
Datos del deudor inline (alternativa a externalClientId)
Show child attributes
Show child attributes
Código de moneda ISO 4217. Whitelist de monedas aceptadas por la API. Default MXN. Si se envía un valor fuera de este whitelist, la API devuelve 400 INVALID_REQUEST.
MXN, ARS, PEN, COP, CLP, USD Total de ciclos (1 a 520). Mutuamente excluyente con endDate.
1 <= x <= 520Fecha de fin de la suscripción. Debe ser posterior a startDate. Mutuamente excluyente con totalCycles.
Override del día de cobro. Por default se infiere del startDate. Solo es válido con intervalUnit month o year; billingDay.month solo con year.
Show child attributes
Show child attributes
Descripción (opcional). Debe ser string (si no, 400).
Solo acepta true/false literal ("true", 1, etc. devuelven 400).
Habilita pagos parciales en las deudas generadas. Solo acepta true/false literal ("true", 1, etc. devuelven 400).
Si true, el deudor debe pagar en orden cronológico. Solo acepta true/false literal ("true", 1, etc. devuelven 400).
Solo acepta true/false literal ("true", 1, etc. devuelven 400). autopay: true y paymentMethods son excluyentes (400).
Medios de pago habilitados. Excluyente con autopay: true (400).
CASH, CARD, TRANSFER, WALLET, BANK_TRANSFER Datos adicionales propios del cliente. Debe ser un objeto plano; un escalar (string, número o booleano) devuelve 400 INVALID_REQUEST. No puede usar claves reservadas, que TapiPay escribe internamente: description, paymentMethods, productName, externalProductId, debtExpirationDate, overduePayment, allowPartialPayments, recurringDebt, debtReference. Si aparece alguna, la API responde 400 INVALID_REQUEST con details[].field = additionalData. Se persiste en la deuda (en ligas de pago, en la deuda asociada al link; en suscripciones, en las deudas generadas).
{ "source": "crm", "folio": "F-2026-0042" }
Respuesta
Suscripción creada
description, additionalData y paymentMethods solo vienen si tienen valor. Una suscripción ACTIVE cuya fecha de fin ya pasó se devuelve como ENDED.
Show child attributes
Show child attributes
{
"subscriptionId": "sub_839d28e1-a175-40c7-a54b-55f6c17b7964",
"externalSubscriptionId": "SUB-2026-007",
"status": "ACTIVE",
"scheduling": {
"intervalUnit": "month",
"intervalCount": 1,
"startDate": "2026-06-15",
"totalCycles": 12,
"endDate": null,
"billingDay": { "day": 15 }
},
"externalClientId": "CLI-00042",
"amount": 999,
"currency": "MXN",
"paymentUrl": "https://app.tapipay.la/s/acme-corp/portal/aB3dE5fG7h/debts",
"enforcePaymentOrder": false,
"autopay": false,
"allowOverduePayment": true,
"allowPartialPayments": true,
"paymentMethods": ["CASH", "CARD"],
"description": "Plan Gimnasio",
"additionalData": { "source": "crm" },
"contact": {
"contactId": "con_9aL3kP2m",
"externalClientId": "CLI-00042",
"createdInline": false
},
"product": {
"productId": "prd_42",
"name": "Plan Gimnasio",
"active": true,
"createdInline": true
},
"createdAt": "2026-06-03T18:15:00.000Z",
"updatedAt": "2026-06-03T18:15:00.000Z"
}

