---
title: API
slug: cp-sdconn-api
docTags: 
createdAt: 2026-03-11T11:46:44.400Z
---

API utilizada por la aplicación de PC para operar con el dispositivo POS a través de SDConn.

Base URL: http\://localhost:8484

Todas las respuestas son JSON con Content-Type: application/json.

## Operaciones

### POST /api/v1/read

Operación completa de lectura de tarjeta. Este es el endpoint principal que la aplicación de PC usa para procesar un pago.

**Flujo estándar** (sin intentToken):

1. Obtiene información del dispositivo (serial number, modelo, etc.)
2. Obtiene la ubicación GPS del dispositivo
3. Crea una operación en la API de Mobbex (/p/pos/operation)
4. Envía el token de lectura al dispositivo para que lea la tarjeta
5. Devuelve los datos de la tarjeta leída junto con el intentToken generado

**Flujo con token externo** (con intentToken):

Si se envía un intentToken en el cuerpo, se omite la llamada a la API de Mobbex y se usa ese token directamente para la lectura. En este caso la respuesta incluye además la geolocation del dispositivo.

**Cuerpo:**

:::BlockQuote
\{
&#x20; "total": 15.00,
&#x20; "otherAmount": 0,
&#x20; "intent": "token\_de\_intento",
&#x20; "currency": "ARS",
&#x20; "hideAmount": false,
&#x20; "test": false,
&#x20; "intentToken": "",
&#x20; "source": "",
&#x20; "description": "",
&#x20; "reference": "",
&#x20; "async": false
}
:::

| Campo       | Tipo    | Requerido | Descripción                                                                                                                                           |
| ----------- | ------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| total       | number  | Sí        | Monto total como float (ej: 15.00)                                                                                                                    |
| otherAmount | number  | No        | Monto adicional (propina, etc.)                                                                                                                       |
| intent      | string  | No        | Tipo de intento (por defecto: "payment.v2")                                                                                                           |
| currency    | string  | No        | Código de moneda (por defecto: "ARS")                                                                                                                 |
| hideAmount  | boolean | No        | Ocultar monto en la pantalla del dispositivo                                                                                                          |
| test        | boolean | No        | Enviar operación en modo test a Mobbex                                                                                                                |
| intentToken | string  | No        | Token externo. Si se envía, omite la llamada a la API de Mobbex                                                                                       |
| source      | string  | No        | Fuente de pago (ej: "arg.qr"). Si se envía, activa el flujo QR en lugar de lectura de tarjeta                                                         |
| description | string  | No        | Descripción de la operación (se envía a la API de Mobbex)                                                                                             |
| reference   | string  | No        | Referencia externa de la operación                                                                                                                    |
| async       | boolean | No        | Si es true, la lectura se ejecuta en segundo plano y se devuelve inmediatamente el intentToken. El resultado se consulta con GET /api/v1/read/\:token |

**Respuesta exitosa — flujo tarjeta (estándar):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "intentToken": "token\_generado\_por\_mobbex",
&#x20; "sourceData": \{
&#x20;   "pin": "...",
&#x20;   "emvData": "...",
&#x20;   "cryptogram": "...",
&#x20;   "ksn": "...",
&#x20;   "track2": "..."
&#x20; }
}
:::

**Respuesta exitosa — flujo tarjeta (con token externo):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "intentToken": "token\_externo\_enviado",
&#x20; "geolocation": \[-31.4201, -64.1888],
&#x20; "sourceData": \{
&#x20;   "pin": "...",
&#x20;   "emvData": "...",
&#x20;   "cryptogram": "...",
&#x20;   "ksn": "...",
&#x20;   "track2": "..."
&#x20; }
}
:::

**Respuesta exitosa — flujo QR (con source):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "intentToken": "token\_generado\_por\_mobbex",
&#x20; "operationId": "kS6D18unD",
&#x20; "source": "arg.qr",
&#x20; "status": \{
&#x20;   "code": "100",
&#x20;   "text": "Pendiente"
&#x20; },
&#x20; "geolocation": \[-31.4201, -64.1888]
}
:::

Cuando se envía source, SDConn:

1. Crea el intent token (o usa el intentToken externo).
2. Llama al endpoint de operación (/p/operations/&#x7B;token}) con la fuente.
3. Descarga la imagen QR del resultado y la codifica en base64.
4. Envía la imagen al dispositivo POS para que la muestre en pantalla.
5. Retorna los datos de la operación (el pago queda pendiente hasta que el usuario escanee el QR).

:::BlockQuote
**Nota:** En el flujo QR, la respuesta no contiene sourceData ya que no se lee una tarjeta. El campo operationId identifica la operación para consultar su estado. El campo status indica el estado actual de la operación (generalmente pendiente hasta que se escanee el QR).
:::

**Respuesta — modo asíncrono (async: true):**

Cuando se envía async: true, SDConn ejecuta los pasos 1-3 (obtener info del dispositivo, ubicación y token) de forma síncrona y luego lanza la lectura de tarjeta o flujo QR en segundo plano. Responde inmediatamente con el intentToken para que el cliente pueda consultar el resultado cuando esté listo.

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "intentToken": "token\_generado\_o\_externo",
&#x20; "async": true,
&#x20; "status": "pending"
}
:::

El resultado se consulta con GET /api/v1/read/\:token (ver sección siguiente).

**Error:**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "intentToken": "token\_si\_fue\_generado",
&#x20; "error": "descripción del error"
}
:::

**Posibles errores:**

| Error                                         | Descripción                                                                                                                           |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| total is required and must be > 0             | Falta el monto o es inválido                                                                                                          |
| credentials not configured                    | No se configuraron credenciales (solo si no se envía intentToken). Cuando se usa un token externo, las credenciales no son necesarias |
| credentials required for source operations    | Se envió source pero no hay credenciales (el flujo QR siempre requiere credenciales para llamar al endpoint de operación)             |
| no device connected                           | No hay dispositivo conectado                                                                                                          |
| device info not available                     | No se pudo obtener info del dispositivo                                                                                               |
| Card read failed                              | El dispositivo no pudo leer la tarjeta                                                                                                |
| TIMEOUT                                       | Se agotó el tiempo de lectura (120s)                                                                                                  |
| CANCELLED                                     | La lectura fue cancelada                                                                                                              |
| NOT\_IN\_PINPAD\_MODE                         | El dispositivo no está en modo pinpad                                                                                                 |
| Operate response did not contain a QR barcode | La respuesta del endpoint de operación no contiene el código QR esperado                                                              |

### GET /api/v1/read/\:token

Consulta el resultado de una operación de lectura asíncrona iniciada con async: true.

**Parámetro de ruta:**

| Parámetro | Tipo   | Descripción                                   |
| --------- | ------ | --------------------------------------------- |
| token     | string | El intentToken devuelto por POST /api/v1/read |

**Respuesta — pendiente (la lectura aún está en progreso):**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "intentToken": "token",
&#x20; "status": "pending"
}
:::

**Respuesta — completada (la lectura finalizó exitosamente):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "intentToken": "token",
&#x20; "status": "completed",
&#x20; "sourceData": \{
&#x20;   "pin": "...",
&#x20;   "emvData": "...",
&#x20;   "cryptogram": "...",
&#x20;   "ksn": "...",
&#x20;   "track2": "..."
&#x20; }
}
:::

La respuesta contiene todos los campos que devolvería una lectura síncrona (sourceData, geolocation, etc.). El resultado se elimina del almacenamiento interno una vez consumido.

**Respuesta — error (la lectura falló):**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "intentToken": "token",
&#x20; "status": "error",
&#x20; "error": "TIMEOUT"
}
:::

El resultado de error también se elimina una vez consumido.

**Token no encontrado:**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "no async operation found for this token"
}
:::

### DELETE /api/v1/read

Cancela una lectura de tarjeta en progreso (tanto síncrona como asíncrona). Envía la señal de cancelación al dispositivo para que deje de esperar la tarjeta.

**Respuesta:**

:::BlockQuote
\{
&#x20; "result": true
}
:::

## Pantalla del dispositivo

### Mostrar resultado — POST /api/v1/display

Muestra una pantalla de resultado con animación en el dispositivo POS. Es fire-and-forget: SDConn responde inmediatamente tras enviar el comando.

**Cuerpo:**

:::BlockQuote
\{
&#x20; "status": "success",
&#x20; "title": "Pago Aprobado",
&#x20; "subtitle": "Visa \*\*\*\* 1234",
&#x20; "total": 15.00,
&#x20; "currency": "ARS",
&#x20; "text": "ID: 123456",
&#x20; "timeout": 10
}
:::

| Campo    | Tipo            | Requerido | Descripción                                                                          |
| -------- | --------------- | --------- | ------------------------------------------------------------------------------------ |
| status   | string o number | No        | Estado del resultado (ver tablas abajo)                                              |
| title    | string          | No        | Título que se muestra en pantalla                                                    |
| subtitle | string          | No        | Subtítulo debajo del título                                                          |
| total    | number          | No        | Monto como float (ej: 15.00)                                                         |
| currency | string          | No        | Código de moneda (ej: "ARS")                                                         |
| text     | string          | No        | Texto adicional debajo del monto                                                     |
| timeout  | number          | No        | Segundos antes de volver a pantalla de espera (defecto: 10). Usar 0 para desactivar. |

**Valores de status por nombre:**

| Valor     | Animación                   |
| --------- | --------------------------- |
| "success" | Círculo verde con check     |
| "error"   | Círculo rojo con X          |
| "neutral" | Círculo naranja con reloj   |
| "none"    | Sin animación, fondo blanco |

**Valores de status por código numérico:**

Se pueden enviar códigos numéricos directamente (por ejemplo, el status.code del procesador) y el dispositivo resuelve la animación automáticamente:

| Códigos                      | Se resuelve a |
| ---------------------------- | ------------- |
| 3, 4, 200, 300-302, 605, 800 | success       |
| 400-417, 500, 603, 604       | error         |
| 1, 2, 100, 210, 600-602, 610 | neutral       |
| Otros                        | none          |

**Respuesta:**

:::BlockQuote
\{
&#x20; "result": true
}
:::

### Mostrar carga — POST /api/v1/display

Muestra una pantalla de carga con spinner en el dispositivo POS. Es fire-and-forget. Se usa para indicar que se está procesando una operación.

**Cuerpo:**

:::BlockQuote
\{
&#x20; "view": "loading",
&#x20; "title": "Procesando pago...",
&#x20; "subtitle": "No retire la tarjeta",
&#x20; "timeout": 30
}
:::

| Campo    | Tipo   | Requerido | Descripción                                                                          |
| -------- | ------ | --------- | ------------------------------------------------------------------------------------ |
| view     | string | Sí        | Debe ser "loading"                                                                   |
| title    | string | No        | Título que se muestra en pantalla                                                    |
| subtitle | string | No        | Subtítulo debajo del título                                                          |
| timeout  | number | No        | Segundos antes de volver a pantalla de espera (defecto: 10). Usar 0 para desactivar. |

**Respuesta:**

:::BlockQuote
\{
&#x20; "result": true
}
:::

Para quitar la pantalla de carga, enviar un DELETE /api/v1/display o mostrar otra vista.

### Mostrar selección — POST /api/v1/display

Muestra una pantalla con opciones seleccionables en el dispositivo POS. Es **bloqueante**: la petición HTTP queda en espera hasta que el usuario selecciona una opción o se agota el timeout.

**Cuerpo:**

:::BlockQuote
\{
&#x20; "view": "choose",
&#x20; "title": "Seleccione cuotas",
&#x20; "subtitle": "Visa \*\*\*\* 1234",
&#x20; "values": \[
&#x20;   \{ "label": "1 Pago - $15.00", "value": "1" },
&#x20;   \{ "label": "3 Cuotas - $5.75 c/u", "value": "3" },
&#x20;   \{ "label": "6 Cuotas - $3.08 c/u", "value": "6" }
&#x20; ],
&#x20; "timeout": 60
}
:::

| Campo    | Tipo   | Requerido | Descripción                                                                           |
| -------- | ------ | --------- | ------------------------------------------------------------------------------------- |
| view     | string | Sí        | Debe ser "choose"                                                                     |
| title    | string | No        | Título de la pantalla                                                                 |
| subtitle | string | No        | Subtítulo                                                                             |
| values   | array  | Sí        | Lista de opciones. Cada opción tiene label (texto visible) y value (valor de retorno) |
| timeout  | number | No        | Segundos de espera antes de timeout (defecto: 60)                                     |

**Respuesta exitosa (usuario seleccionó una opción):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "selected": \{
&#x20;   "label": "3 Cuotas - $5.75 c/u",
&#x20;   "value": "3"
&#x20; }
}
:::

**Respuesta — timeout:**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "TIMEOUT"
}
:::

### Ocultar pantalla — DELETE /api/v1/display

Oculta la pantalla del dispositivo antes de que expire el timeout. El dispositivo vuelve a la pantalla de espera de pinpad.

**Respuesta:**

:::BlockQuote
\{
&#x20; "result": true
}
:::

## Información de tarjeta

### POST /api/v1/card/info

Analiza los datos de track de una tarjeta y extrae el código de servicio para determinar si la tarjeta tiene chip EMV o capacidad contactless (NFC). No requiere conexión con un dispositivo — es un utilitario de parsing local.

**Cuerpo:**

:::BlockQuote
\{
&#x20; "track1": "%B4111111111111111^DOE/JOHN^2512101000000000?",
&#x20; "track2": ";4111111111111111=2512101000000000?"
}
:::

| Campo  | Tipo   | Requerido | Descripción                                                            |
| ------ | ------ | --------- | ---------------------------------------------------------------------- |
| track1 | string | No        | Datos de track 1 (actualmente no se parsea, reservado para uso futuro) |
| track2 | string | Sí        | Datos de track 2 (formato ISO 7813)                                    |

**Formato de Track 2:**

:::BlockQuote
;PAN=YYMMSSSDDDDDDDDD?
&#x20;│    │  │ └─ Datos discrecionales
&#x20;│    │  └─── Código de servicio (3 dígitos)
&#x20;│    └────── Vencimiento (AAMM)
&#x20;└─────────── Número de tarjeta (PAN)
:::

El separador puede ser = (texto) o D (hex).

**Respuesta exitosa:**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "pan": "41111111\*\*\*\*1111",
&#x20; "expiry": "2512",
&#x20; "serviceCode": \{
&#x20;   "code": "101",
&#x20;   "hasChip": true,
&#x20;   "chipRequired": true,
&#x20;   "contactless": false,
&#x20;   "international": true,
&#x20;   "pinRequired": false,
&#x20;   "cashAllowed": true
&#x20; }
}
:::

| Campo                     | Tipo    | Descripción                                                       |
| ------------------------- | ------- | ----------------------------------------------------------------- |
| pan                       | string  | PAN enmascarado (primeros 8 dígitos del BIN + últimos 4 dígitos)  |
| expiry                    | string  | Vencimiento en formato AAMM                                       |
| serviceCode.code          | string  | Código de servicio de 3 dígitos                                   |
| serviceCode.hasChip       | boolean | La tarjeta tiene chip EMV (IC)                                    |
| serviceCode.chipRequired  | boolean | El chip es obligatorio (no se permite fallback a banda magnética) |
| serviceCode.contactless   | boolean | La tarjeta soporta contactless (NFC/tap)                          |
| serviceCode.international | boolean | La tarjeta es de intercambio internacional                        |
| serviceCode.pinRequired   | boolean | Se requiere PIN para la transacción                               |
| serviceCode.cashAllowed   | boolean | Se permite retiro de efectivo                                     |

**Referencia del primer dígito del código de servicio:**

| Dígito | Significado                       | hasChip | chipRequired | contactless |
| ------ | --------------------------------- | ------- | ------------ | ----------- |
| 1      | Internacional, chip obligatorio   | ✓       | ✓            |             |
| 2      | Internacional, chip + contactless | ✓       |              | ✓           |
| 5      | Nacional, chip obligatorio        | ✓       | ✓            |             |
| 6      | Nacional, chip + contactless      | ✓       |              | ✓           |
| 7      | Privado, chip obligatorio         | ✓       | ✓            |             |
| 0      | Solo banda magnética              |         |              |             |

**Error:**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "invalid track2: no separator found (expected '=' or 'D')"
}
:::

## Proxy de procesamiento

### POST /api/v1/p/operations/\:token

Proxy directo a la API de Mobbex (POST &#x7B;mobbexAPIBase}/p/operations/&#x7B;token}). Reenvía el cuerpo tal cual, sin parsing ni validación. Útil cuando el intent token se genera externamente y se quiere procesar la operación sin pasar por el flujo de lectura.

Las credenciales de Mobbex (si están configuradas) se agregan automáticamente como headers x-api-key y x-access-token.

**Parámetro de ruta:**

| Parámetro | Tipo   | Descripción           |
| --------- | ------ | --------------------- |
| token     | string | Intent token a operar |

**Cuerpo:** Se envía directamente a la API de Mobbex sin modificaciones.

**Respuesta:** Se reenvía directamente la respuesta de la API de Mobbex sin modificaciones.

## Conexión al dispositivo por API

### POST /api/v1/connect

Inicia una conexión al dispositivo de forma programática (alternativa al panel de administración).

**Conexión serial:**

:::BlockQuote
\{
&#x20; "mode": "serial",
&#x20; "port": "COM3"
}
:::

| Campo | Tipo   | Requerido | Descripción                                                   |
| ----- | ------ | --------- | ------------------------------------------------------------- |
| mode  | string | Sí        | Debe ser "serial"                                             |
| port  | string | Sí        | Nombre del puerto serial (ej: "COM3", "/dev/cu.usbmodem1421") |

**Respuesta exitosa (serial):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "mode": "serial",
&#x20; "port": "COM3",
&#x20; "connected": true
}
:::

La conexión serial se establece inmediatamente (handshake + modo pinpad). No requiere verificación adicional.

**Conexión HTTP:**

:::BlockQuote
\{
&#x20; "mode": "http",
&#x20; "ip": "192.168.1.50",
&#x20; "port": "5000"
}
:::

| Campo | Tipo   | Requerido | Descripción                            |
| ----- | ------ | --------- | -------------------------------------- |
| mode  | string | Sí        | Debe ser "http"                        |
| ip    | string | Sí        | IP del dispositivo                     |
| port  | string | No        | Puerto del dispositivo (defecto: 5000) |

**Respuesta exitosa (HTTP):**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "mode": "http",
&#x20; "ip": "192.168.1.50",
&#x20; "port": 5000,
&#x20; "verificationNeeded": true,
&#x20; "message": "Enter the PIN displayed on the device using POST /api/v1/connect/verify"
}
:::

El dispositivo mostrará un PIN en pantalla. Se debe verificar con POST /api/v1/connect/verify.

### POST /api/v1/connect/verify

Verifica el PIN mostrado en el dispositivo para completar la conexión HTTP.

**Cuerpo:**

:::BlockQuote
\{
&#x20; "ip": "192.168.1.50",
&#x20; "port": 5000,
&#x20; "pin": "123456"
}
:::

| Campo | Tipo   | Requerido | Descripción                            |
| ----- | ------ | --------- | -------------------------------------- |
| ip    | string | Sí        | IP del dispositivo                     |
| port  | number | No        | Puerto del dispositivo (defecto: 5000) |
| pin   | string | Sí        | PIN mostrado en el dispositivo         |

**Respuesta exitosa:**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "mode": "http",
&#x20; "device": \{
&#x20;   "ip": "192.168.1.50",
&#x20;   "port": 5000,
&#x20;   "name": "POS N62"
&#x20; },
&#x20; "connected": true
}
:::

**Error (PIN inválido):**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "invalid PIN"
}
:::

## Reconexión

### POST /api/v1/reconnect

Intenta reconectarse al último dispositivo guardado usando el token de confianza. No requiere PIN.

**Respuesta exitosa:**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "device": \{
&#x20;   "ip": "192.168.1.50",
&#x20;   "port": 5000,
&#x20;   "name": "POS N62"
&#x20; }
}
:::

**Error:**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "no saved device or device not reachable"
}
:::

## Dispositivo

### GET /api/v1/device/info

Obtiene información del hardware y software del dispositivo conectado.

**Respuesta:**

:::BlockQuote
\{
&#x20; "type": "POS",
&#x20; "serialNumber": "SN12345",
&#x20; "deviceExtras": \{
&#x20;   "os": "Android",
&#x20;   "osVersion": "11",
&#x20;   "manufacturer": "nexgo",
&#x20;   "model": "N62",
&#x20;   "serialNumber": "SN12345",
&#x20;   "bundleId": "com.sugaway.pos",
&#x20;   "buildNumber": 42,
&#x20;   "appVersion": "1.2.3",
&#x20;   "romVersion": "N62-20230101",
&#x20;   "kernelVersion": "4.14.116"
&#x20; }
}
:::

La información se cachea en memoria por **24 horas** y se limpia al cambiar de modo de conexión o dispositivo.

### GET /api/v1/device/location

Obtiene la ubicación GPS del dispositivo. Se cachea por **5 minutos**. La consulta al dispositivo tiene un timeout de **1 segundo** — si no responde a tiempo, se omite la ubicación (no se envía en la operación). Si no hay ubicación disponible (ni en caché ni del dispositivo), la operación continúa sin geolocalización.

**Respuesta — ubicación disponible:**

:::BlockQuote
\{
&#x20; "latitude": -31.4201,
&#x20; "longitude": -64.1888,
&#x20; "accuracy": 12.5,
&#x20; "provider": "gps",
&#x20; "timestamp": 1741564800000
}
:::

**Respuesta — ubicación no disponible:**

:::BlockQuote
\{
&#x20; "error": "Location not available"
}
:::

### GET /api/v1/devices

Lista los dispositivos conectados actualmente.

**Respuesta:**

:::BlockQuote
\{
&#x20; "result": true,
&#x20; "devices": \[
&#x20;   \{ "ip": "192.168.1.50", "port": 5000, "name": "POS N62" }
&#x20; ]
}
:::

Si no hay dispositivo conectado, devices es un array vacío.

### DELETE /api/v1/devices/\:id

Elimina un dispositivo de la lista de dispositivos activos.

**Respuesta:**

:::BlockQuote
\{ "result": true }
:::

### PATCH /api/v1/devices/\:id/mode

Cambia el modo de operación del dispositivo.

**Cuerpo:**

:::BlockQuote
\{ "mode": "pinpad" }
:::

| Modo    | Descripción                                             |
| ------- | ------------------------------------------------------- |
| normal  | Modo POS estándar con pantalla de ingreso de monto      |
| slave   | Modo cola. Recibe trabajos de pago desde una cola       |
| transit | Lectura continua de tarjetas para peajes/transporte     |
| pinpad  | Modo pinpad. Espera comandos de lectura del controlador |

### POST /api/v1/pinpad-config

Personaliza la pantalla de espera del modo pinpad. Permite configurar título, subtítulo e imagen de fondo.

Todos los campos son opcionales — solo se envían al dispositivo los campos proporcionados. Los campos omitidos no se modifican.

Si background\_image es una URL (http\:// o https\://), SDConn descarga la imagen, la redimensiona y optimiza automáticamente antes de enviarla al dispositivo:

- **Imágenes estáticas** (PNG, JPEG): se redimensionan a máximo 480px de ancho (manteniendo proporción) y se re-codifican como JPEG calidad 80.
- **GIF animados**: se redimensionan a máximo 480px de ancho, se limitan a 90 frames (eliminando frames de forma uniforme y acumulando los delays para mantener la velocidad de animación), y se re-codifican como GIF con dithering Floyd-Steinberg y paleta de 256 colores.

:::BlockQuote
**Nota:** background\_image solo está disponible en modo HTTP. Si se envía en modo serial, se retorna un error.
:::

**Cuerpo:**

:::BlockQuote
\{
&#x20; "title": "Mi Negocio",
&#x20; "subtitle": "Acerque o inserte su tarjeta",
&#x20; "background\_image": "https\://example.com/background.png"
}
:::

| Campo             | Tipo   | Requerido | Descripción                                                                                                                                                           |
| ----------------- | ------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| title             | string | No        | Título mostrado en la pantalla de espera                                                                                                                              |
| subtitle          | string | No        | Subtítulo mostrado debajo del título                                                                                                                                  |
| background\_image | string | No        | URL de la imagen de fondo (PNG, JPEG o GIF) o string base64. Si es URL, se descarga, optimiza y convierte a base64 automáticamente. **Solo disponible en modo HTTP.** |

**Comportamiento según modo de conexión:**

- **Serial:** Solo title y subtitle. Se envía como campo waiting dentro del mensaje SET\_MODE (\{ mode: "pinpad", waiting: \{ title, subtitle } }). Si se envía background\_image, se retorna error.
- **HTTP:** Se envía via POST /device/pinpad-config al dispositivo. Soporta los 3 campos.

**Respuesta exitosa:**

:::BlockQuote
\{
&#x20; "result": true
}
:::

**Errores:**

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "no device connected"
}
:::

:::BlockQuote
\{
&#x20; "result": false,
&#x20; "error": "background\_image is only available in HTTP mode"
}
:::

## Flujo típico de uso

### Sin token externo (flujo estándar)

:::BlockQuote
App PC                          SDConn                         Dispositivo POS
&#x20; │                               │                                │
&#x20; │  POST /api/v1/read            │                                │
&#x20; │  \{ total: 15.00 }             │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │                               │  (obtiene info del dispositivo)│
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │\<──────────────────────────────│
&#x20; │                               │                                │
&#x20; │                               │  (crea operación en Mobbex)    │
&#x20; │                               │ ──────────> Mobbex API         │
&#x20; │                               │\<────────── \{ token }           │
&#x20; │                               │                                │
&#x20; │                               │  (lectura de tarjeta)          │
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │          (usuario pasa tarjeta)│
&#x20; │                               │\<────────── \{ sourceData }      │
&#x20; │                               │                                │
&#x20; │  \{ result: true,              │                                │
&#x20; │    intentToken: "...",        │                                │
&#x20; │    sourceData: &#x7B;...} }        │                                │
&#x20; │\<─────────────────────────────│                                │
&#x20; │                               │                                │
&#x20; │  POST /api/v1/display         │                                │
&#x20; │  \{ status: "success" }        │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │         (pantalla de resultado)│
:::

### Con token externo

:::BlockQuote
App PC                          SDConn                         Dispositivo POS
&#x20; │                               │                                │
&#x20; │  POST /api/v1/read            │                                │
&#x20; │  \{ total: 15.00,              │                                │
&#x20; │    intentToken: "ext\_token" } │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │                               │  (obtiene info del dispositivo)│
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │\<──────────────────────────────│
&#x20; │                               │                                │
&#x20; │                               │  (omite llamada a Mobbex API)  │
&#x20; │                               │                                │
&#x20; │                               │  (lectura de tarjeta)          │
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │          (usuario pasa tarjeta)│
&#x20; │                               │\<────────── \{ sourceData }      │
&#x20; │                               │                                │
&#x20; │  \{ result: true,              │                                │
&#x20; │    intentToken: "ext\_token",  │                                │
&#x20; │    geolocation: \[lat, lng],   │                                │
&#x20; │    sourceData: &#x7B;...} }        │                                │
&#x20; │\<─────────────────────────────│                                │
:::

### Flujo QR (con source)

:::BlockQuote
App PC                          SDConn                         Dispositivo POS
&#x20; │                               │                                │
&#x20; │  POST /api/v1/read            │                                │
&#x20; │  \{ total: 15.00,              │                                │
&#x20; │    source: "arg.qr" }        │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │                               │  (obtiene info del dispositivo)│
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │\<──────────────────────────────│
&#x20; │                               │                                │
&#x20; │                               │  (crea operación en Mobbex)    │
&#x20; │                               │ ──────────> Mobbex API         │
&#x20; │                               │\<────────── \{ token }           │
&#x20; │                               │                                │
&#x20; │                               │  (obtiene QR de Mobbex)        │
&#x20; │                               │ ──────────> Mobbex API         │
&#x20; │                               │\<────────── \{ barcode }         │
&#x20; │                               │                                │
&#x20; │                               │  (muestra QR en dispositivo)   │
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │         (QR visible en pantalla)│
&#x20; │                               │                                │
&#x20; │  \{ result: true,              │                                │
&#x20; │    intentToken: "...",        │                                │
&#x20; │    operationId: "...",        │                                │
&#x20; │    source: "arg.qr",         │                                │
&#x20; │    status: \{ code, text },    │                                │
&#x20; │    geolocation: \[lat, lng] }  │                                │
&#x20; │\<─────────────────────────────│                                │
&#x20; │                               │                                │
&#x20; │  (espera a que el usuario     │                                │
&#x20; │   escanee el QR — consultar   │                                │
&#x20; │   estado externamente)        │                                │
&#x20; │                               │                                │
&#x20; │  DELETE /api/v1/read          │                                │
&#x20; │  (cuando quiera cancelar)     │                                │
&#x20; │ ─────────────────────────────>│ ──────────────────────────────>│
&#x20; │                               │                (quita QR)      │
:::

### Flujo asíncrono

:::BlockQuote
App PC                          SDConn                         Dispositivo POS
&#x20; │                               │                                │
&#x20; │  POST /api/v1/read            │                                │
&#x20; │  \{ total: 15.00,              │                                │
&#x20; │    async: true }              │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │                               │  (obtiene info del dispositivo)│
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │\<──────────────────────────────│
&#x20; │                               │                                │
&#x20; │                               │  (crea operación en Mobbex)    │
&#x20; │                               │ ──────────> Mobbex API         │
&#x20; │                               │\<────────── \{ token }           │
&#x20; │                               │                                │
&#x20; │  \{ result: true,              │  (lanza lectura en background) │
&#x20; │    intentToken: "...",        │ ──────────────────────────────>│
&#x20; │    async: true,               │                                │
&#x20; │    status: "pending" }        │                                │
&#x20; │\<─────────────────────────────│                                │
&#x20; │                               │                                │
&#x20; │  (polling)                    │                                │
&#x20; │  GET /api/v1/read/\:token      │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │  \{ status: "pending" }        │                                │
&#x20; │\<─────────────────────────────│                                │
&#x20; │                               │          (usuario pasa tarjeta)│
&#x20; │  ...                          │\<────────── \{ sourceData }      │
&#x20; │                               │                                │
&#x20; │  GET /api/v1/read/\:token      │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │  \{ result: true,              │                                │
&#x20; │    status: "completed",       │                                │
&#x20; │    intentToken: "...",        │                                │
&#x20; │    sourceData: &#x7B;...} }        │                                │
&#x20; │\<─────────────────────────────│                                │
:::

### Flujo con vista choose (cuotas)

:::BlockQuote
App PC                          SDConn                         Dispositivo POS
&#x20; │                               │                                │
&#x20; │  POST /api/v1/display         │                                │
&#x20; │  \{ view: "choose",            │                                │
&#x20; │    title: "Cuotas",           │                                │
&#x20; │    values: \[...] }            │                                │
&#x20; │ ─────────────────────────────>│                                │
&#x20; │                               │ ──────────────────────────────>│
&#x20; │                               │        (usuario toca una opción)│
&#x20; │                               │\<────────────────────────────── │
&#x20; │                               │                                │
&#x20; │  \{ result: true,              │                                │
&#x20; │    selected: \{                │                                │
&#x20; │      label: "3 Cuotas",      │                                │
&#x20; │      value: "3" } }          │                                │
&#x20; │\<─────────────────────────────│                                │
:::

