Partner API V1

Contrato claro para conectar POS aprobados sin acceso a la base de datos.

WAGMI recibe catálogo y facturas por endpoints versionados. Cada request entra firmado, con idempotencia, bandeja de revisión y auditoría antes de afectar contabilidad o reportes del cliente.

V1 vive hoy bajo /api/integrations/v1/toi porque el primer conector aprobado usa ese namespace. El contrato es reutilizable para otros POS aprobados; las rutas genéricas se publicarán cuando exista un segundo conector real.

Implementación de sandbox

Esta es la ruta completa para que un POS externo conecte con WAGMI sin acceso a base de datos. V1 ya fue validado con un primer conector aprobado; cualquier otro POS requiere sandbox, mapeo y aprobación de conector equivalente antes de producción.

1
Solicitar sandbox
Completa /os/start?path=connect-pos con sistema actual, sucursales, IDs externos y contacto técnico.
2
Enviar muestras
Comparte muestras sanitizadas de categoría, producto, factura cerrada y anulación. Sin datos personales, RNC/teléfonos reales, API keys, secrets ni exportaciones completas.
3
Recibir credenciales sandbox
El equipo WAGMI provisiona API key y webhook secret dedicados por canal seguro sólo después de aprobar muestras, confirmar el branchExternalId exacto del sandbox inicial y pasar smoke con evidencia.
4
Health
Ejecuta GET /health con Bearer para validar modo, moneda, fiscal sources y payment methods soportados.
5
Firmar POST
Firma timestamp.idempotencyKey.rawBody con HMAC-SHA256 y manda la firma como sha256=<hex>.
6
Sincronizar catálogo
Envía categorías primero y productos después. Los externalId deben ser estables.
7
Enviar ventas
Envía facturas cerradas con totales, líneas, pagos y fiscalSource correcto.
8
Revisar bandeja
WAGMI revisa imports, mappings, duplicados fiscales e idempotencia antes de activar producción.

Primer paquete técnico

Para abrir sandbox no necesitamos base de datos, SQL, usuarios internos ni pantallas privadas. Necesitamos muestras sanitizadas representativas para validar el contrato.

IDs externos de sucursales y cajas/canales que enviará el POS.
Una categoría y un producto representativos y sanitizados, con externalId estable, precio, impuesto, estado y sucursal si aplica.
Dos o tres facturas cerradas sanitizadas con líneas, pagos, descuentos, ITBIS, propina/servicio, cliente ficticio y NCF/e-CF de sandbox si aplica.
Un ejemplo de anulación o nota de crédito, aunque sea de sandbox.
Volumen esperado por día y ventana preferida para pruebas.
No envíes contraseñas, llaves productivas ni exportaciones completas de base de datos por correo. Las credenciales sandbox se generan sólo después de aprobar estas muestras, confirmar el branchExternalId exacto del sandbox inicial y pasar smoke con evidencia.
Solicitar sandbox Partner API

Smoke sandbox

Este ejemplo valida conexión y muestra cómo firmar un envío sin usar credenciales productivas ni acceso a la base de datos.

export WAGMI_BASE_URL="https://wagmi.do"
export WAGMI_API_KEY="<sandbox_api_key>"
export WAGMI_WEBHOOK_SECRET="<sandbox_webhook_secret>"

curl -s "$WAGMI_BASE_URL/api/integrations/v1/toi/health" \
  -H "Authorization: Bearer $WAGMI_API_KEY"

body='{"categories":[{"externalId":"sandbox-bebidas","name":"Bebidas","active":true}]}'
timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
idempotency_key="sandbox-category-smoke-001"
signature="$(printf "%s.%s.%s" "$timestamp" "$idempotency_key" "$body" | openssl dgst -sha256 -hmac "$WAGMI_WEBHOOK_SECRET" -binary | xxd -p -c 256)"

curl -s "$WAGMI_BASE_URL/api/integrations/v1/toi/categories/batch" \
  -H "Authorization: Bearer $WAGMI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-WAGMI-Timestamp: $timestamp" \
  -H "X-WAGMI-Idempotency-Key: $idempotency_key" \
  -H "X-WAGMI-Signature: sha256=$signature" \
  --data "$body"

Reglas del contrato V1

Versión
V1 vive bajo /api/integrations/v1. Cambios breaking van a una ruta versionada nueva; campos opcionales pueden agregarse en V1.
Sucursal autorizada
La conexión define empresa y sucursal autorizadas. branchExternalId es opcional y debe resolver dentro de esa empresa.
Catálogo externo
Categorías y productos no reciben branchExternalId en V1. Para catálogos compartidos se usa una conexión general de empresa; las ventas se separan con branchExternalId en facturas.
Rate limits
Health: 240 req/min por conexión. Writes: 120 req/min por endpoint/conexión. Invalid keys también tienen límites pre-auth.
Reintentos
Reintenta 429/5xx con backoff. No reintentes 400/401/403 hasta corregir payload, firma, API key o estado de conexión.
Posteo contable
Partner API V1 deja las importaciones en revisión previa; postingMode=manual_review_only y los asientos requieren aprobación contable.

Errores esperados

Los partners deben corregir 400/401/403 antes de reintentar. Sólo 429/5xx se reintentan con backoff.

VALIDATION_ERROR
JSON inválido, schema incorrecto, falta idempotency key, moneda no soportada o body distinto con la misma key.
UNAUTHORIZED
API key, timestamp o firma ausente/inválida.
FORBIDDEN
Conexión inactiva, secreto no configurado o modo no permitido.
RATE_LIMITED
Límite por IP, key o conexión excedido.
INTERNAL_ERROR
Error inesperado; revisar status/soporte antes de reintentar.

Endpoints

Contrato público para partners POS.

GET
/api/integrations/v1/toi/health
Validar conexión, capacidades y moneda soportada.
POST
/api/integrations/v1/toi/categories/batch
Sincronizar categorías externas.
POST
/api/integrations/v1/toi/products/batch
Sincronizar productos externos.
POST
/api/integrations/v1/toi/invoices/batch
Recibir facturas cerradas desde un POS externo.
POST
/api/integrations/v1/toi/invoices/void
Anular una factura externa en revisión.

Referencia por endpoint

Resumen humano del contrato. El OpenAPI JSON es la fuente importable para Swagger/Postman.

GET /health
/api/integrations/v1/toi/health

Confirmar que la API key está activa y ver capacidades de la conexión.

Requerido
Authorization: Bearer <partner_api_key>
No requiere HMAC.
No requiere idempotency key.
Devuelve mode: sandbox|production, DOP, fiscal sources y payment methods.
POST /categories/batch
/api/integrations/v1/toi/categories/batch

Crear o actualizar categorías externas.

Requerido
categories[].externalIdcategories[].name
Máximo 500 categorías por request.
No acepta branchExternalId en V1; el alcance sale de la conexión autorizada.
parentExternalId se valida contra mappings existentes.
active=false conserva la categoría inactiva.
POST /products/batch
/api/integrations/v1/toi/products/batch

Crear o actualizar productos externos para reporting, margen e imports.

Requerido
products[].externalIdproducts[].nameproducts[].priceSell
Máximo 500 productos por request.
No acepta branchExternalId en V1; un SKU/nombre igual en otra sucursal queda en needsMapping.
categoryExternalId puede quedar en revisión si no hay mapping.
barcodes no está soportado en V1.
POST /invoices/batch
/api/integrations/v1/toi/invoices/batch

Recibir facturas cerradas del POS externo en revisión contable/fiscal.

Requerido
invoices[].externalIdissuedAtsubtotaltotallines[]
Máximo 250 facturas por request.
Pagos son opcionales pero recomendados.
Las facturas no postean contabilidad automáticamente en V1.
POST /invoices/void
/api/integrations/v1/toi/invoices/void

Anular una factura externa en revisión sin borrar evidencia.

Requerido
externalIdvoidedAtreason
No se elimina data fiscal.
creditNote puede incluir NCF/e-CF y monto si aplica.
Reutiliza idempotency key al reintentar la misma anulación.

Campos y mappings críticos

Estos campos son los que más rompen integraciones cuando no se definen temprano. Mantenerlos estables evita duplicados, mezcla de sucursales y errores contables.

externalId
ID estable del partner para categoría, producto, factura o línea. No debe cambiar entre syncs.
branchExternalId
ID de sucursal/caja/canal del POS externo. Debe mapear dentro de la empresa autorizada.
categoryExternalId
Referencia a una categoría externa ya enviada o a mapear manualmente.
catálogo
Categorías y productos son scope de la conexión. Si el cliente usa catálogo compartido, se recomienda conexión general de empresa y branchExternalId sólo en facturas.
fiscalSource
TOI o EXTERNAL cuando el POS externo emitió NCF/e-CF; NONE cuando no hay documento fiscal.
currency
V1 sólo acepta DOP. Otras monedas requieren contrato/versionado posterior.
payments[].method
Método de pago externo. Health devuelve la lista aceptada para la conexión.
subtotal / discount / tax / tip / total
Totales de factura cerrada. WAGMI valida consistencia y deja diferencias en revisión.
X-WAGMI-Idempotency-Key
Una llave por intento lógico. Reintentos usan la misma key y el mismo raw body.

Secuencia sandbox recomendada

Antes de entregar credenciales sandbox o productivas, validamos muestras sanitizadas, target aprobado y branchExternalId exacto en una conexión sandbox separada. El partner no recibe acceso a base de datos y sandbox y producción requieren aprobación humana.

1
Enviar muestras sanitizadas de categoría, producto, factura cerrada y anulación.
2
Confirmar company sandbox, IDs externos de sucursales, branchExternalId exacto del sandbox inicial y volumen esperado por día.
3
WAGMI ejecuta smoke con evidencia contra el target sandbox aprobado antes de entregar credenciales.
4
Provisionar conexión sandbox por equipo WAGMI solo con aprobación humana explícita, API key y webhook secret dedicados.
5
Usar el mismo host con credenciales sandbox; el modo lo define la conexión/API key aprobada.
6
Ejecutar GET /health con Bearer solamente para validar capacidades y moneda.
7
Sincronizar 5 categorías y 10 productos de prueba.
8
Enviar 10-20 facturas sandbox con pagos, ITBIS, descuentos y NCF/e-CF si aplica.
9
Revisar imports, mappings, idempotencia, branch y bandeja contable.
10
Pasar a activación productiva solo con payloads aprobados y acuerdo operativo.

Headers requeridos

Health

GET /health valida la llave y la conexión. Sólo requiere Bearer; no firma HMAC ni idempotency key.

Authorization
Bearer <partner_api_key>
POST writes

Categorías, productos, facturas y anulaciones requieren firma e idempotencia.

Authorization
Bearer <partner_api_key>
X-WAGMI-Timestamp
ISO-8601 con zona, ejemplo 2026-05-12T14:35:00-04:00
X-WAGMI-Idempotency-Key
Llave única por intento lógico
X-WAGMI-Signature
sha256 HMAC de timestamp.idempotencyKey.rawBody

Ejemplo factura

{
  "source": "TOI",
  "sentAt": "2026-05-12T10:10:00-04:00",
  "invoices": [
    {
      "externalId": "inv-1001",
      "number": "1001",
      "issuedAt": "2026-05-12T10:09:00-04:00",
      "branchExternalId": "SUCURSAL-SANDBOX-001",
      "channel": "DINE_IN",
      "customer": {
        "externalId": "cust-1",
        "name": "Cliente final",
        "rnc": null,
        "phone": null,
        "email": null
      },
      "fiscal": {
        "fiscalSource": "NONE",
        "ncf": null,
        "ecf": null,
        "ncfType": null
      },
      "currency": "DOP",
      "subtotal": 480,
      "discount": 0,
      "tax": 86.4,
      "tip": 48,
      "total": 614.4,
      "lines": [
        {
          "externalLineId": "line-1",
          "productExternalId": "prod-classic-smash",
          "name": "Classic Smash",
          "quantity": 1,
          "unitPrice": 480,
          "discount": 0,
          "tax": 86.4,
          "total": 566.4
        }
      ],
      "payments": [
        {
          "method": "CASH",
          "amount": 614.4,
          "receivedAt": "2026-05-12T10:09:30-04:00"
        }
      ]
    }
  ]
}

Payloads que necesitamos

Muestras canónicas para validar sandbox antes de credenciales productivas.

Categoría
Primer sync para armar el árbol de catálogo externo.
{
  "source": "TOI",
  "sentAt": "2026-05-12T10:00:00-04:00",
  "categories": [
    {
      "externalId": "cat-burgers",
      "name": "Burgers",
      "parentExternalId": null,
      "active": true,
      "sortOrder": 10
    }
  ]
}
Producto
Productos activos con precio, impuesto y categoría externa.
{
  "source": "TOI",
  "sentAt": "2026-05-12T10:05:00-04:00",
  "products": [
    {
      "externalId": "prod-classic-smash",
      "sku": "SMASH-001",
      "name": "Classic Smash",
      "categoryExternalId": "cat-burgers",
      "priceSell": 480,
      "taxRate": 0.18,
      "active": true,
      "trackStock": false,
      "imageUrl": null,
      "barcodes": []
    }
  ]
}
Factura cerrada
Venta finalizada en el POS externo, lista para revisión previa.
{
  "source": "TOI",
  "sentAt": "2026-05-12T10:10:00-04:00",
  "invoices": [
    {
      "externalId": "inv-1001",
      "number": "1001",
      "issuedAt": "2026-05-12T10:09:00-04:00",
      "branchExternalId": "SUCURSAL-SANDBOX-001",
      "channel": "DINE_IN",
      "customer": {
        "externalId": "cust-1",
        "name": "Cliente final",
        "rnc": null,
        "phone": null,
        "email": null
      },
      "fiscal": {
        "fiscalSource": "NONE",
        "ncf": null,
        "ecf": null,
        "ncfType": null
      },
      "currency": "DOP",
      "subtotal": 480,
      "discount": 0,
      "tax": 86.4,
      "tip": 48,
      "total": 614.4,
      "lines": [
        {
          "externalLineId": "line-1",
          "productExternalId": "prod-classic-smash",
          "name": "Classic Smash",
          "quantity": 1,
          "unitPrice": 480,
          "discount": 0,
          "tax": 86.4,
          "total": 566.4
        }
      ],
      "payments": [
        {
          "method": "CASH",
          "amount": 614.4,
          "receivedAt": "2026-05-12T10:09:30-04:00"
        }
      ]
    }
  ]
}
Anulación
Anulación de una factura externa en revisión.
{
  "externalId": "inv-1001",
  "voidedAt": "2026-05-12T10:30:00-04:00",
  "reason": "Anulada en POS externo",
  "creditNote": null
}

Idempotencia y reintentos

Si un partner reintenta el mismo evento, debe mantener la misma `X-WAGMI-Idempotency-Key` y el mismo body. Si el body cambia con la misma llave, WAGMI responde error controlado para evitar duplicados o corrupción de imports.

Respuesta de batches

duplicate
true cuando la misma idempotency key y el mismo body ya fueron procesados.
requestId
Eco de la idempotency key para rastreo con soporte.
received
Cantidad de registros recibidos en el batch.
created / updated
Conteo de categorías o productos creados/actualizados.
accepted
Facturas aceptadas para revisión.
needsMapping
Registros que requieren mapping manual antes de postear o reportar completo.
rejected
Facturas rechazadas por validación de negocio.
errors[]
Lista de errores por registro. No contiene secretos ni IDs internos sensibles.

Checklist para producción

La activación productiva se habilita sólo cuando estos puntos están verdes. Si algo falta, seguimos en sandbox.

Health sandbox responde mode=sandbox y supportedCurrencies=[DOP].
Categorías y productos sanitizados pasan sin errores críticos.
Facturas sandbox cubren efectivo, tarjeta, transferencia, crédito/cortesía si aplica.
NCF/e-CF externos no duplican documentos ya importados.
branchExternalId y canales quedan mapeados por empresa/sucursal.
Reintentos con misma idempotency key responden duplicate=true o resultado estable.
Payload distinto con misma idempotency key falla como VALIDATION_ERROR.
Volumen esperado no excede límites acordados o ya tiene batching/backoff.
WAGMI y partner aprueban handoff comercial y soporte antes de activación productiva.