API para desarrolladores
Versión 1 · Conecta el sitio web de un negocio con su cuenta de TwionX. · Versión en texto (Markdown)
Si el negocio vende o reserva en su propia página, esa página puede registrar a cada comprador como cliente del negocio en TwionX, con su autorización de datos, sin pedirle que llene un segundo formulario. El cliente aparece en la app con sus datos, su cumpleaños y la prueba de lo que aceptó.
Las reservas que se pagan en la página (noche, zona, personas y paquetes) también llegan a TwionX: en la puerta se busca o se escanea el código, sale en verde si puede entrar esa noche y la llegada se marca una sola vez.
Y cada venta pagada en la página puede salir con su factura electrónica a la DIAN desde TwionX, con su asiento contable, sin pasar por la caja.
Antes de empezar
- El administrador del negocio crea una llave en la app de TwionX: Ajustes › Integraciones › Tu página web (API). La llave empieza por
twx_y se muestra completa una sola vez. - Al crearla (o luego, con el botón de ajustes de la llave) elige qué puede hacer: clientes (registrar compradores), reservas (enviar y consultar reservas) y facturas (facturar las ventas de la página). Una llave sin el permiso recibe
403. - Guarda la llave en una variable de entorno del servidor de tu sitio (por ejemplo
TWIONX_API_KEY). Nunca la pongas en el navegador, en el código de la página ni en un repositorio. - Si una llave se expone o ya no se usa, el administrador la revoca desde la misma pantalla y deja de funcionar de inmediato.
Cada llave pertenece a un solo negocio y solo ve los clientes y las reservas de ese negocio. La API solo recibe datos de tu página y responde por lo que tu página envió: no entrega las ventas del local, la contabilidad ni la base de clientes.
Autenticación
Todas las peticiones van a https://app.twionx.com/api/v1 con la llave en la cabecera Authorization. El cuerpo va en JSON.
Authorization: Bearer twx_tu_llave
Content-Type: application/json
Fechas AAAA-MM-DD; horas HH:MM en 24 h; fecha y hora en ISO 8601 con zona (2026-10-17T21:04:00-05:00), hora de Colombia. El dinero va en pesos enteros, sin decimales (80000). Responde en menos de 8 segundos; si tarda más, corta y reintenta luego.
Límite: 120 peticiones por minuto por llave. Si lo pasas, la respuesta es 429; espera un minuto.
GET /estado
Comprueba que la llave funciona. Úsalo al configurar tu sitio.
curl https://app.twionx.com/api/v1/estado \
-H "Authorization: Bearer $TWIONX_API_KEY"
{ "ok": true, "empresa": "Clandestino", "llave": "Página de reservas", "permisos": ["clientes"] }
GET /clientes/terminos
Devuelve el enlace a los términos del registro y la autorización de datos del negocio. Ese es el texto que la persona acepta: enlázalo junto a la casilla de autorización en tu formulario.
{
"ok": true,
"version": "2026-09-28",
"titulo": "Términos del registro y autorización para el tratamiento de datos personales",
"url": "https://app.twionx.com/registro/clandestino-speakeasy/terminos",
"edad_minima": 18,
"responsable": "Clandestino"
}
GET /clientes/{documento}
Dice si esa persona ya está registrada en el negocio con su autorización. Úsalo para no volver a pedirle la fecha de nacimiento ni la autorización a quien ya está registrado. El documento va sin puntos.
curl https://app.twionx.com/api/v1/clientes/1144000777 \
-H "Authorization: Bearer $TWIONX_API_KEY"
{ "ok": true, "registrado": true, "cliente_id": 22622 }
Si expones esta consulta en tu sitio, hazlo desde tu servidor, con límite por IP y devolviendo solo sí o no.
POST /clientes
Registra a la persona como cliente del negocio y guarda la prueba de su autorización. Aplica las mismas reglas que el formulario de registro de TwionX.
| Campo | Obligatorio | Qué es |
|---|---|---|
nombre | Sí | Nombre y apellido, como en el documento. |
tipo_documento | Sí | CC, CE, PPT o PASAPORTE. |
documento | Sí | Número sin puntos. La cédula, de 5 a 10 dígitos. |
fecha_nacimiento | Sí | AAAA-MM-DD. Debe tener la edad mínima del negocio (18 años o más). |
telefono | Sí | Celular colombiano de 10 dígitos, o con indicativo +país. |
correo | Sí | Correo electrónico. Le llega la bienvenida del negocio. |
acepta_tratamiento | Sí | true: la persona marcó la autorización de datos. |
acepta_publicidad | No | true si aceptó recibir promociones. Por defecto, no. |
ip_cliente | Recomendado | IP de la persona (no la de tu servidor). Queda en la prueba de la autorización. |
navegador_cliente | Recomendado | El User-Agent del navegador de la persona. |
origen | Recomendado | URL de la página donde aceptó. |
referencia | No | Tu código de compra o reserva (hasta 80 caracteres). |
curl -X POST https://app.twionx.com/api/v1/clientes \
-H "Authorization: Bearer $TWIONX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"nombre": "Laura Gómez de la Cruz",
"tipo_documento": "CC",
"documento": "1144000777",
"fecha_nacimiento": "1995-04-20",
"telefono": "3001234567",
"correo": "laura@correo.com",
"acepta_tratamiento": true,
"acepta_publicidad": false,
"ip_cliente": "190.85.10.20",
"navegador_cliente": "Mozilla/5.0 (iPhone…)",
"origen": "https://tusitio.com/reservar",
"referencia": "CLD-AB12"
}'
{ "ok": true, "resultado": "nuevo", "cliente_id": 22622, "mensaje": "Cliente registrado." }
El campo resultado dice qué pasó:
nuevo: se creó el cliente.actualizado: ya existía con ese documento y el mismo correo o celular; se completaron sus datos vacíos y se guardó su elección sobre promociones. No se crea otro.documento_existente_sin_coincidencia: el documento ya está registrado con otro correo y otro celular. No se modifica nada (puede ser un documento ajeno), pero queda la constancia del intento.
Puedes llamar este endpoint en cada compra: una misma persona nunca queda dos veces.
POST /reservas
Envía cada reserva confirmada: cuando el pago se aprueba o cuando el equipo la confirma a mano. Las reservas sin pagar o vencidas no se envían. Requiere el permiso reservas.
| Campo | Obligatorio | Qué es |
|---|---|---|
codigo | Sí | El código de tu página (ej. CL-7KQ2MZ). Único por negocio: es la llave de todo lo demás. |
cliente | Sí | tipo_documento, documento, nombre, telefono, correo. Con el documento se une a la ficha creada con POST /clientes. |
noche | Sí | titulo («Reserva de mesa» o el nombre del evento) y es_evento. |
fecha | Sí | La noche reservada. |
hora_llegada | Sí | Hora elegida, HH:MM. |
zona | Sí | id y nombre (ej. backstage / Backstage). |
personas | Sí | Total del grupo, con los covers de los paquetes. |
items[] | Sí | Una línea por cover o paquete: tipo (cover o paquete), titulo, cantidad, valor_unitario, subtotal; en paquetes, id, covers_incluidos e incluye[] (lo que la puerta o la barra entregan). |
total | Sí | Debe ser la suma de los subtotales. |
pago | Sí | proveedor (mercadopago o manual), estado (aprobado o pendiente), y si los tienes id, valor y fecha. |
estado | Sí | Siempre confirmada al crear. |
origen | Sí | web o panel (reserva manual del equipo). |
ocasion, notas, mesa, creada | No | Cumpleaños, notas del cliente (hasta 500), la mesa si ya está asignada, cuándo se hizo el pedido. |
curl -X POST https://app.twionx.com/api/v1/reservas \
-H "Authorization: Bearer $TWIONX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"codigo": "CL-7KQ2MZ",
"cliente": { "tipo_documento": "CC", "documento": "1144000000", "nombre": "Laura Gómez",
"telefono": "3001234567", "correo": "laura@correo.com" },
"noche": { "titulo": "Noche de Salsa", "es_evento": true },
"fecha": "2026-10-17",
"hora_llegada": "21:00",
"zona": { "id": "backstage", "nombre": "Backstage" },
"personas": 6,
"items": [
{ "tipo": "cover", "titulo": "Cover Backstage", "cantidad": 2, "valor_unitario": 50000, "subtotal": 100000 },
{ "tipo": "paquete", "id": "paquete-pedro-navaja", "titulo": "Paquete Pedro Navaja", "cantidad": 1,
"valor_unitario": 900000, "subtotal": 900000, "covers_incluidos": 4,
"incluye": ["4 covers zona Backstage", "1 botella Don Julio 70", "1 pasante"] }
],
"total": 1000000,
"pago": { "proveedor": "mercadopago", "id": "129384756", "estado": "aprobado",
"valor": 1000000, "fecha": "2026-10-12T20:41:00-05:00" },
"estado": "confirmada",
"ocasion": "Cumpleaños",
"origen": "web",
"creada": "2026-10-12T20:38:00-05:00"
}'
{ "ok": true, "resultado": "creada", "reserva_id": 1841, "cliente_id": 22622 }
creada: es nueva.ya_existia: el mismo código con los mismos datos. No se crea otra: reintentar es seguro.actualizada: el mismo código con datos distintos; se aplican los cambios. Si la reserva ya entró, responde409 CONFLICTO.
Si el documento todavía no tiene ficha de cliente, la reserva se guarda igual con los datos de contacto, cliente_id viene en null y la respuesta trae un aviso. TwionX no crea la ficha por su cuenta: solo nace con la autorización de datos de POST /clientes. Al reenviar la reserva después del registro, queda unida.
PATCH /reservas/{codigo}
Cuando la reserva cambia en tu panel. Cambia solo los campos que envías: estado, fecha, hora_llegada, zona, personas, mesa, pago y notas_internas. Responde {"ok": true, "reserva": {…}}.
curl -X PATCH https://app.twionx.com/api/v1/reservas/CL-7KQ2MZ \
-H "Authorization: Bearer $TWIONX_API_KEY" -H "Content-Type: application/json" \
-d '{ "estado": "cancelada" }'
La llegada no se marca por la API: se marca en la puerta, en TwionX. Una reserva que ya entró solo admite cambios de mesa, pago y notas_internas.
GET /reservas
GET /reservas/{codigo} devuelve la reserva como está en TwionX: los mismos campos del POST, más reserva_id, cliente_id, notas_internas y llegada cuando ya se marcó.
"llegada": { "hora": "2026-10-18T02:12:00.000Z", "personas_ingresadas": 6, "marcada_por": "puerta" }
GET /reservas?fecha=2026-10-17 devuelve la lista de esa noche, para conciliar si alguna no llegó.
Estados y la puerta
| Estado | Lo pone | Significa |
|---|---|---|
confirmada | Tu página | Pagada o confirmada por el equipo: puede entrar esa noche. |
reprogramada | Tu página | Cambió de noche (envía la nueva fecha): puede entrar en la nueva. |
asistio | TwionX (puerta) | Ya entró; el código no vuelve a servir. |
no_asistio | Tu página o TwionX | La noche pasó sin llegar. |
cancelada | Tu página | Anulada o reembolsada: no puede entrar. |
En la puerta, la reserva sale en verde «Puede entrar» solo si está confirmada (o reprogramada) y la fecha es la de esa noche. La noche cruza la medianoche: hasta las 10 a. m. sigue siendo la del día anterior. En cualquier otro caso sale en rojo con el motivo. El QR de tu ticket puede llevar el código solo o la dirección del ticket terminada en el código.
Aviso de llegada (opcional)
Si quieres que tu panel se entere solo cuando alguien llega, configura en la llave la dirección del aviso (solo https://). TwionX muestra una vez un secreto whsec_…; guárdalo en tu servidor. Si no lo configuras, consulta GET /reservas/{codigo}.
POST https://tusitio.com/api/twionx/webhook
Content-Type: application/json
X-TwionX-Evento: reserva.llegada
X-TwionX-Firma: 9f2c… (HMAC-SHA256 del cuerpo exacto con tu secreto, en hexadecimal)
{ "evento": "reserva.llegada", "codigo": "CL-7KQ2MZ", "estado": "asistio", "fecha": "2026-10-17",
"llegada": { "hora": "2026-10-18T02:12:00.000Z", "personas_ingresadas": 6, "marcada_por": "puerta" },
"enviado": "2026-10-18T02:12:03.000Z" }
Eventos: reserva.llegada y reserva.actualizada (por ejemplo, cuando TwionX la marca como no asistió). Responde 200; si no, TwionX reintenta a los 1, 5 y 30 minutos. Rechaza cualquier aviso cuya firma no coincida:
// Express: guarda el cuerpo crudo para verificar la firma.
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
app.post('/api/twionx/webhook', (req, res) => {
const esperada = crypto.createHmac('sha256', process.env.TWIONX_AVISO_SECRETO)
.update(req.rawBody).digest('hex');
const firma = String(req.headers['x-twionx-firma'] || '');
if (firma.length !== 64 || !crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(firma))) {
return res.status(401).end();
}
marcarLlegada(req.body.codigo, req.body.llegada); // tu lógica
res.json({ ok: true });
});
POST /facturas
Factura una venta pagada en tu página: TwionX la emite a la DIAN como factura electrónica (a nombre del comprador si tiene ficha, si no a consumidor final), hace su asiento y la deja en los reportes. Requiere el permiso facturas. Llámala cuando el pago real se aprueba; nunca con pagos de prueba de la pasarela: la factura es real.
| Campo | Obligatorio | Qué es |
|---|---|---|
referencia | Sí | El código del pedido o de la reserva (ej. CL-7KQ2MZ). Una factura por referencia. |
cliente | No | tipo_documento, documento, nombre, correo. Con el documento se busca la ficha creada con POST /clientes. |
lineas[] | Sí | De 1 a 50 líneas. tipo: producto (con producto: { "sku": "…" }, o id o codigo_barras) o cover. Más cantidad y precio_unitario: el valor final cobrado, con impuestos. |
pago | Sí | medio (mercadopago, transferencia o tarjeta), valor (la suma de las líneas), y si los tienes id y fecha. |
entrega | No | ahora (por defecto) o al_llegar: el inventario sale cuando la puerta marca la llegada de la reserva con el mismo código (envíala antes con POST /reservas). |
notas | No | Hasta 200 caracteres. |
curl -X POST https://app.twionx.com/api/v1/facturas \
-H "Authorization: Bearer $TWIONX_API_KEY" -H "Content-Type: application/json" \
-d '{
"referencia": "CL-7KQ2MZ",
"cliente": { "tipo_documento": "CC", "documento": "1144000000", "nombre": "Laura Gómez", "correo": "laura@correo.com" },
"lineas": [
{ "tipo": "cover", "cantidad": 2, "precio_unitario": 50000 },
{ "tipo": "producto", "producto": { "sku": "paquete-celebracion-pedro-navaja" }, "cantidad": 1, "precio_unitario": 900000 }
],
"pago": { "medio": "mercadopago", "id": "129384756", "valor": 1000000, "fecha": "2026-10-12T20:41:00-05:00" },
"entrega": "al_llegar"
}'
{ "ok": true, "resultado": "creada",
"factura": { "referencia": "CL-7KQ2MZ", "venta_id": 32139, "numero": "CFY001842", "total": 1000000, "estado": "vigente",
"dian": { "estado": "enviando", "cufe": null }, "entrega": "al_llegar", "por_entregar": true } }
- Productos: cada uno se busca por su código en TwionX. Un paquete o combo debe existir en TwionX como combo con su composición (lo que se entrega) y con ese código en su SKU; si no existe, la respuesta dice cuál crear.
- Combo que incluye covers (un paquete de celebración): envía los covers que trae como líneas
coveral valor del cover de la zona a la que va el paquete, y el combo por el resto. Ej.: un paquete de $900.000 para Backstage con 4 covers de $50.000 son{ "tipo": "cover", "cantidad": 4, "precio_unitario": 50000 }más el combo a $700.000. - Cover: se factura como lo configure el negocio en TwionX (Ajustes › Integraciones › Tu página web (API) › Facturas de tu página): una parte por persona como ingreso propio, con impoconsumo, y el resto como ingreso para terceros. Por defecto, todo para terceros. Requiere el cover y el ingreso para terceros configurados en Taquilla.
- Caja: la venta no entra a ningún turno de caja (el dinero no pasó por el cajón). Mercado Pago y transferencias entran a Bancos; tarjeta, al datáfono por abonar.
- Sin duplicados: la misma referencia con los mismos datos responde
ya_existiacon la misma factura. Con datos distintos responde409 CONFLICTO: anula y factura con otra referencia. - Si el comprador no tiene ficha, la factura sale a consumidor final y la respuesta trae un
aviso.
GET /facturas/{referencia}
Devuelve la factura con su estado ante la DIAN y todos sus datos legales, para mostrarla en tu página. dian.estado pasa de enviando a en_validacion y validada (o rechazada, con mensaje). Si TwionX no logró enviarla, la reintenta sola cada 10 minutos.
Cuándo mostrarla: espera a "completa": true (la DIAN la validó y el PDF oficial existe). Mientras tanto, muestra «Tu factura se está procesando». Lo más simple es enlazar documento.enlaces.pdf: es el PDF oficial, el mismo que valida la DIAN. Si prefieres dibujarla en tu página, todos los datos vienen en documento.
{ "ok": true, "factura": {
"referencia": "CL-7KQ2MZ", "numero": "CFY001842", "total": 1000000, "estado": "vigente",
"dian": { "estado": "validada", "cufe": "112d1bd6…", "mensaje": null },
"nota_credito": null, "entrega": "al_llegar", "por_entregar": false,
"completa": true,
"documento": {
"tipo": "documento_equivalente_pos",
"tipo_nombre": "Documento equivalente electrónico tiquete de máquina registradora con sistema POS",
"numero": "CFY1842",
"emisor": { "razon_social": "IWANT SAS", "nombre_comercial": "Clandestino", "nit": "901424727", "dv": "4",
"direccion": "AV 6 14 33", "ciudad": "Cali", "codigo_dane_municipio": "76001",
"telefono": "…", "correo": "…", "actividad_economica": "4741", "responsable_iva": true,
"responsabilidades": [ { "codigo": "R-99-PN", "nombre": "No aplica – otros" } ] },
"resolucion": { "numero": "18764111182185", "fecha": "2026-06-13", "prefijo": "CFY", "desde": 1001, "hasta": 10000,
"vigencia_desde": "2026-06-13", "vigencia_hasta": "2028-06-13",
"texto": "Autorización de numeración DIAN No. 18764111182185 del 2026-06-13, prefijo CFY, del 1001 al 10000, vigente hasta el 2028-06-13." },
"adquirente": { "nombre": "Laura Gómez", "tipo_documento": "CC", "documento": "1144000000", "correo": "laura@correo.com" },
"lineas": [
{ "descripcion": "Ingreso a tercero", "codigo": null, "cantidad": 2, "precio_unitario": 50000, "base": 100000,
"impuesto": { "tipo": "ninguno", "tarifa": 0, "valor": 0 }, "total": 100000, "ingreso_para_terceros": true },
{ "descripcion": "Paquete Pedro Navaja", "codigo": "paquete-celebracion-pedro-navaja", "cantidad": 1,
"precio_unitario": 900000, "base": 833333.33,
"impuesto": { "tipo": "INC", "nombre": "Impuesto nacional al consumo", "tarifa": 8, "valor": 66666.67 },
"total": 900000, "ingreso_para_terceros": false }
],
"totales": { "base": 933333.33, "iva": 0, "impoconsumo": 66666.67, "descuento": 0, "redondeo": 0, "total": 1000000 },
"medio_pago": "transferencia",
"cufe": "112d1bd6…",
"qr": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=112d1bd6…",
"fechas": { "generada": "2026-10-12T20:41:05.000Z", "enviada_dian": "2026-10-12T20:41:06.000Z", "validada_dian": "2026-10-12T20:52:40.000Z" },
"enlaces": { "ver": "https://app.twionx.com/api/f/…", "pdf": "https://app.twionx.com/api/f/…/pdf" }
} } }
qr: el contenido del código QR del documento (la consulta en la DIAN); dibújalo como QR.responsabilidades: códigos del RUT del emisor (O-13 gran contribuyente, O-15 autorretenedor, O-23 agente de retención de IVA, O-47 régimen simple; R-99-PN si ninguna aplica).resolucion: la autorización de numeración de la DIAN con que salió el documento;nullsi el negocio no la tiene registrada en TwionX.- Las líneas de
ingreso_para_tercerosno llevan impuesto: son dinero recibido a nombre de un tercero. - Sin ficha de cliente, el adquirente es consumidor final (
222222222222). enlaces.veryenlaces.pdfson públicos pero imposibles de adivinar (llevan una firma): compártelos solo con el comprador.
POST /facturas/{referencia}/anular
Cuando la venta se cancela o se reembolsa. TwionX anula la venta, devuelve al inventario lo que ya se había entregado y emite la nota crédito a la DIAN. Lleva el motivo (va en la nota crédito).
curl -X POST https://app.twionx.com/api/v1/facturas/CL-7KQ2MZ/anular \
-H "Authorization: Bearer $TWIONX_API_KEY" -H "Content-Type: application/json" \
-d '{ "motivo": "Reserva cancelada y reembolsada" }'
{ "ok": true, "resultado": "anulada", "factura": { "estado": "anulada", "nota_credito": { "estado": "enviando", "cufe": null }, … } }
Anular otra vez responde ya_anulada: nunca salen dos notas crédito.
Errores
Todas las respuestas con error tienen la misma forma. Cuando el problema es un dato, campo dice cuál: muéstrale el mensaje a la persona sobre ese campo de tu formulario.
{ "ok": false, "codigo": "DATO_INVALIDO", "campo": "fecha_nacimiento",
"error": "El registro es solo para mayores de 18 años." }
| HTTP | codigo | Qué hacer |
|---|---|---|
| 400 | DATO_INVALIDO | Corregir el dato de campo. No sirve reintentar igual. |
| 401 | LLAVE_INVALIDA | Falta la llave, está mal copiada o fue revocada. |
| 403 | SIN_PERMISO | La llave no tiene ese permiso. Crea una con el permiso que necesitas. |
| 404 | NO_EXISTE | Revisa la ruta o el código de la reserva. |
| 409 | CONFLICTO | La reserva ya entró y lo que envías la cambiaría, o la referencia ya se facturó con otros datos. No reintentes igual. |
| 409 | SIN_CONFIGURAR | Al negocio le falta configurar algo en TwionX (por ejemplo, el cover en Taquilla). El mensaje dice qué. |
| 429 | LIMITE | Espera un minuto. |
| 5xx | ERROR_INTERNO | Reintenta más tarde. |
Si TwionX no responde o devuelve 5xx, no frenes la venta: guarda los datos y reintenta luego (por ejemplo, cada 10 minutos, hasta 30 veces). Un 400 o un 409 no se reintenta: muéstralo en tu panel para corregirlo.
Datos personales (Ley 1581 de 2012)
El negocio es el Responsable del tratamiento y TwionX es el Encargado. Para que el registro sea válido, tu formulario debe:
- Tener una casilla de autorización sin marcar por defecto, con el enlace a los términos que da
GET /clientes/terminos. - Tener la casilla de promociones aparte y opcional.
- Enviar la IP, el navegador y la página de la persona, para la prueba de la autorización.
- Registrar solo a quien llenó el formulario. Cada acompañante da su propia autorización: no registres a terceros con los datos del comprador.
- No registrar menores de edad.
Las consultas y solicitudes de borrado de datos las atiende privacidad@twionx.com.
Ejemplo completo (Node.js)
En el servidor de tu sitio, al recibir la compra:
const API = 'https://app.twionx.com/api/v1';
const cabeceras = {
Authorization: `Bearer ${process.env.TWIONX_API_KEY}`,
'Content-Type': 'application/json',
};
// ¿Ya está registrado? Entonces no le pidas la fecha ni la autorización otra vez.
async function yaRegistrado(documento) {
const r = await fetch(`${API}/clientes/${encodeURIComponent(documento)}`, { headers: cabeceras });
return r.ok && (await r.json()).registrado === true;
}
async function registrarComprador(compra, req) {
const r = await fetch(`${API}/clientes`, {
method: 'POST',
headers: cabeceras,
signal: AbortSignal.timeout(8000),
body: JSON.stringify({
nombre: compra.nombre,
tipo_documento: compra.tipoDocumento,
documento: compra.documento,
fecha_nacimiento: compra.fechaNacimiento,
telefono: compra.celular,
correo: compra.correo,
acepta_tratamiento: compra.aceptaDatos === true,
acepta_publicidad: compra.aceptaPromociones === true,
ip_cliente: req.ip,
navegador_cliente: req.headers['user-agent'],
origen: req.headers.referer,
referencia: compra.codigo,
}),
});
const j = await r.json();
if (r.status === 400) throw Object.assign(new Error(j.error), { campo: j.campo }); // mostrar en el formulario
if (!r.ok) return { pendiente: true }; // reintentar más tarde, sin frenar la venta
return j; // { resultado, cliente_id }
}
¿Necesitas otra integración?
Escríbenos a soporte@twionx.com con lo que quieres conectar.