terminalSessionCommands

Crear un comando de terminal

Envía un comando de cobro a una terminal POS. Recurrente crea un checkout y lo despacha a la terminal indicada. Si ya existe un comando activo con el mismo external_id, retorna el comando existente en vez de crear uno nuevo (idempotencia).

Cuando la terminal recibe el comando, muestra automáticamente la pantalla de cobro para que el cliente pague con tarjeta.

Usa una llave LIVE para una terminal física. Una llave TEST heredada que todavía apunta a la cuenta LIVE se rechaza con 403 terminal_test_key_requires_sandbox, antes de crear el checkout o mover dinero. Las llaves TEST solo se aceptan cuando la solicitud ya está aislada dentro de un Sandbox; ese flujo no contacta hardware ni procesadores reales.

La terminal debe estar en Modo espera y reportando disponibilidad. Si no lo está, la API responde 409 con code: terminal_not_in_standby sin crear un checkout nuevo. Los reintentos con un external_id existente conservan la idempotencia y retornan el comando original. Una respuesta exitosa incluye terminal_availability.

Flujo

  1. Tu sistema envía POST /api/terminal_session_commands con el monto, moneda y terminal.
  2. Recurrente crea un checkout y un comando en estado pending.
  3. La terminal levanta el comando y lo pasa a dispatched.
  4. El cliente paga en la terminal.
  5. Recibes un webhook payment_intent.succeeded con el resultado.

También puedes consultar el comando con GET /api/terminal_session_commands/{random_id}. Los estados definitivos son canceled, superseded, consumed y failed; pending, dispatched y cancel_requested todavía pueden cambiar.

Idempotencia

Si envías dos requests con el mismo external_id dentro de la misma cuenta, el segundo retorna el comando original sin crear uno duplicado. Dos cuentas distintas pueden usar el mismo external_id. Esto te permite reintentar de forma segura.

Superseding

Si envías un nuevo comando a la misma terminal (con un external_id diferente), los comandos anteriores pendientes se marcan como superseded y la terminal solo procesa el más reciente.

Meses sin intereses (installments)

Si quieres que el cobro se procese en cuotas, envía installments con el número de meses. Solo aplica a cobros en GTQ y los valores permitidos son 3, 6, 12 o 18 (algunas cuentas tienen configuraciones distintas). Si la tarjeta del cliente no soporta la opción elegida, el cobro se rechaza con unsupported_installments.

Pantallas post-pago

Por defecto, después de un pago exitoso la terminal muestra las pantallas para solicitar NIT, correo y teléfono. Envía show_post_payment_screens: false para omitirlas y volver automáticamente a Modo espera. Recurrente emite la factura como C/F cuando corresponde y adelanta el webhook y los correos que normalmente esperan a que el comprador termine esas pantallas.

Si el monto y la configuración de facturación hacen obligatorio un NIT válido, Recurrente conserva las pantallas aunque envíes false.

Impresión automática del comprobante

Envía print_receipt: true para mandar el comprobante de pago a la impresora una vez que el cobro sea exitoso. La instrucción es best-effort: el resultado de impresión no se expone por API y una impresora sin papel, ocupada o con error no revierte el pago ni retrasa payment_intent.succeeded.

Cuentas conectadas

Para originar el cobro desde una plataforma y registrarlo en una cuenta hija:

  1. Autentica el request con la llave LIVE de la plataforma en X-SECRET-KEY y envía el ID ac_... de la cuenta hija en X-ACCOUNT-ID.
  2. Obtén el terminal_id público (trm_...) en el panel de la cuenta hija, en POS → detalle de la terminal, y guárdalo en tu configuración. Actualmente no existe un endpoint público para listar terminales.
  3. Confirma que el dispositivo inició sesión en esa misma cuenta hija y está en Modo espera. Un pinpad emparejado con la plataforma es invisible para la hija (y viceversa).
  4. Envía el comando con terminal_id, monto, moneda y un external_id único de tu sistema. La respuesta incluye el id del comando (tsc_...), checkout_id, status y terminal_availability.

El checkout, el pago y la factura se crean bajo la cuenta hija. La plataforma recibe payment_intent.succeeded con connected: true y el account_id de la hija; si la hija también tiene un webhook endpoint, Recurrente entrega el evento a ambos. Usa checkout.metadata.external_id para conciliar la orden original, checkout.metadata.terminal_id para identificar el dispositivo y tax_invoice_url para recuperar la factura cuando exista.

Cuando algo no calza, el 404 incluye un code que identifica cuál de las tres cosas falta:

codeQué revisar
connected_account_not_foundLa cuenta que enviaste no está conectada a la tuya (o es nieta, no hija directa).
terminal_not_foundLa terminal no está asociada a la cuenta que va a cobrar.
connected_account_mismatchEnviaste account_id de una hermana distinta a la del header X-ACCOUNT-ID.
recipient_not_foundEl recipient_id de un transfer_setup no es tu cuenta ni una hija conectada.
post/terminal_session_commands

Headers

X-SECRET-KEYstring required

Tu clave secreta de API.

Una llave de cuenta (sk_live_..., sk_test_...) opera sobre su propia cuenta y, con X-ACCOUNT-ID, sobre sus cuentas conectadas.

Una llave de organización (sk_org_live_..., sk_org_test_...) alcanza todas las cuentas de una organización y solo sirve para leer: saldos, movimientos y reportes. Para operar sobre una cuenta debe nombrarla con X-ACCOUNT-ID; omitirlo en una lectura devuelve todas las cuentas de la organización. Cualquier otro endpoint responde 403 con code: organization_key_unsupported.

Request body

terminal_idstring required

ID público trm_... de la terminal POS donde se enviará el cobro. Cópialo desde POS → detalle de la terminal en la cuenta que va a cobrar; actualmente no existe un endpoint público para listar terminales.

amount_in_centsinteger

Monto a cobrar en centavos. Envía amount_in_cents, amount o items — solo uno.

amountnumber double

Monto a cobrar en unidades (ej. 50.00). Alternativa a amount_in_cents.

currency'GTQ' | 'USD' required

Moneda del cobro

external_idstring required

ID único de tu sistema para este cobro dentro de la cuenta autenticada. Se usa para idempotencia — si envías el mismo external_id dos veces en esa cuenta, no se crea un duplicado.

descriptionstring

(Opcional) Concepto del cobro, por ejemplo el número de orden de tu sistema. Aparece en el recibo del cliente y en la actividad del comercio (ej. "Pago POS por Orden #1234"). Si se omite, el cobro se muestra sin concepto, como un pago rápido. No se puede combinar con items.

installments'3' | '6' | '12' | '18'

Número de meses sin intereses. Solo válido con currency: GTQ. Valores permitidos por defecto [3, 6, 12, 18] (puede variar por cuenta).

show_post_payment_screensboolean

Muestra las pantallas post-pago de NIT, correo y teléfono. Envía false para omitirlas y volver a Modo espera, salvo cuando un NIT válido sea obligatorio.

print_receiptboolean

Imprime automáticamente el comprobante de pago después de un cobro exitoso. El resultado de impresión no cambia el estado financiero del pago.

application_fee_amountinteger

(Opcional) Comisión de plataforma en centavos. Requiere que el cobro sea a nombre de una cuenta conectada (modelo directo): cuando el cobro se completa, Recurrente transfiere este monto del balance de la cuenta conectada al de tu plataforma. La comisión se puede revertir al reembolsar con refund_application_fee: true, y se incluye en la facturación diaria de comisiones (DTE) si la conexión la tiene habilitada. No se puede combinar con un transfer_setups de purpose: platform_commission.

Response

Comando creado exitosamente

idstring

ID público aleatorio del comando

external_idstring

ID único de tu sistema dentro de la cuenta autenticada

status'pending' | 'dispatched' | 'cancel_requested' | 'canceled' | 'superseded' | 'consumed' | 'failed'

Estado del comando

finalboolean

true cuando el comando alcanzó un resultado definitivo y ya no puede cambiar

terminal_idstring

ID de la terminal POS

amount_in_centsinteger

Monto en centavos. Para un cobro con items, la suma de los items.

currency'GTQ' | 'USD'

Moneda del cobro

installmentsinteger nullable

Meses sin intereses solicitados, o null si el cobro va sin cuotas

descriptionstring nullable

Concepto del cobro enviado al crear el comando, o null si no se envió uno

show_post_payment_screensboolean

Indica si el comando solicita mostrar las pantallas post-pago de NIT, correo y teléfono

print_receiptboolean

Indica si el comando solicita imprimir automáticamente el comprobante de pago

terminal_availability'available' | 'unavailable'

Disponibilidad observada de la terminal para recibir comandos

checkout_idstring

ID del checkout generado

checkout_urlstring uri

URL del checkout (la terminal usa esta URL internamente)

checkout_status'unpaid' | 'paid' | 'failed' | 'payment_in_progress' | 'needs_verification' | 'no_payment_required' | 'verification_failed' | 'needs_payer_authentication'

Estado actual del checkout asociado

cancellation_requested_atstring date-time nullable

Momento en que se solicitó detener un comando ya despachado

canceled_atstring date-time nullable

Momento en que la cancelación se volvió definitiva

Changes