# BIND ERP API · sandbox local Sandbox para validar la integración del MVP **BIND-first** de Balam **sin tocar producción**. Está pensado para que tú (Johann), Pedro (Balam) o un futuro dev puedan: 1. Entender la forma real del API de BIND ERP antes de tener el API key. 2. Probar el cliente tipado contra un mock que replica los headers, el formato OData y el rate-limit observable de BIND. 3. Cuando Pedro entregue las credenciales reales, **cambiar dos variables de entorno** y apuntar el mismo código a `https://api.bind.com.mx` sin reescribir nada. --- ## TL;DR del API de BIND (lo que descubrí del discovery) | Tema | Hallazgo | |---|---| | **Base URL** | `https://api.bind.com.mx` | | **Estilo** | REST con sintaxis **OData v3** (filtros `$filter`, `$top`, `$skip`, `$orderby`, `$count`, IDs como `guid'...'`) | | **Auth** | Dos headers: `Authorization: Bearer ` + `Ocp-Apim-Subscription-Key: ` (este último cuando aplica) | | **Origen del API key** | Cuenta BIND → Perfil de usuario → pestaña *Integraciones* | | **Rate limit** | **20,000 peticiones / día** (confirmado por Noe, 25-may-2026) | | **Sandbox oficial** | **No existe.** BIND recomienda Postman contra producción → razón #1 de este sandbox | | **Portal dev** | [developers.bind.com.mx](https://developers.bind.com.mx) (login requerido para ver schemas detallados) | | **PAC para CFDI** | Integrado dentro del propio BIND — la plataforma de Balam **no toca el SAT**, solo orquesta | | **Recursos confirmados** | `Activities`, `Customers`, `Products` (y según el discovery doc: `Invoices`, `Payments`, `Quotes` muy probables) | ### Por qué un sandbox propio y no Postman - BIND solo tiene producción → cualquier `POST` real toca facturas reales con consecuencias fiscales. - El contrato exacto está detrás de login → necesitamos un lugar donde ir **acumulando lo que aprendemos** del API real conforme Pedro nos dé acceso. - Tener el contrato en código (TypeScript + tipos) hace que **el motor de cobranza del MVP se pueda probar con CI** sin depender de la red. - Cuando llegue Belvo / BUK en Fase 2 esta misma estructura sirve como plantilla. --- ## Cómo correrlo Requiere Node 20+. ```powershell # 1) Instalar deps cd bind-api-sandbox npm install # 2) Levantar el mock en una terminal npm run mock # -> [bind-mock] escuchando en http://localhost:4010 # 3) Correr la demo end-to-end en otra terminal npm run demo ``` La demo ejecuta 5 escenarios alineados al MVP: | # | Escenario | Qué demuestra | |---|---|---| | 1 | Listar clientes activos | Cómo se construye la query OData base del dashboard | | 2 | Facturas vencidas (`Status eq 'overdue' and DueDate lt ...`) | El motor de cobranza | | 3 | CxC agregada por cliente (MXN) | KPI directivo del dashboard | | 4 | Factura USD a cliente extranjero | Regla sin-IVA + TC fijado al emitir | | 5 | Intento de `POST /Activities` en modo read-only | El guardrail que evita escribir a BIND prod por accidente | Output esperado (verificado): ``` ── 2. Facturas vencidas — motor de cobranza ┌─────────┬──────────┬────────────┬──────────────┬──────────┬─────────┐ │ Folio │ Cliente │ DueDate │ Currency │ Balance │ │ 'A-0003' │ '22222222' │ '2026-04-01' │ 'MXN' │ 20880 │ │ 'A-0005' │ '11111111' │ '2026-05-20' │ 'MXN' │ 62640 │ ── 5. Intento de escritura en read-only (debe BLOQUEARSE) ✅ Guardrail OK: Read-only mode bloqueó POST /api/Activities. Cambia BIND_MODE=write... Stats del cliente { requestsToday: 6, quota: 20000, remaining: 19994, mode: 'read-only' } ``` ### Apuntar a producción (cuando llegue el API key) Solo cambiar variables de entorno — el código no se modifica: ```powershell $env:BIND_BASE_URL = "https://api.bind.com.mx" $env:BIND_API_KEY = "" $env:BIND_SUBSCRIPTION_KEY = "" $env:BIND_MODE = "read-only" # mantenlo así hasta tener autorización para escribir npm run demo ``` --- ## Mapeo a la arquitectura de Balam Este sandbox es el prototipo de lo que en el repo principal vivirá en `packages/integrations/bind/`: ``` balam/ └── packages/ └── integrations/ └── bind/ ├── BindClient.ts ← este sandbox lo prototipa ├── types.ts ← este sandbox lo prototipa ├── odata.ts ← este sandbox lo prototipa └── README.md apps/ └── worker/ └── src/ └── modules/ └── bind-sync/ ← consume BindClient, escribe a Postgres, respeta tenant_id + outbox pattern ``` El cliente está pensado para encajar con los principios del proyecto (ver `01 - ARQUITECTURA-TECNICA.md`): - **Modo seguro por default** (`read-only`): bloquea `POST/PUT/PATCH/DELETE` en código. Cumple §1 "La plataforma no escribe a BIND en MVP". - **Dry-run** opcional: imprime el request sin enviarlo (cumple §1 punto 2 "Modo dry-run disponible en cualquier acción con efecto externo"). - **Idempotencia preparada**: el método `addActivity` está aislado para que cuando se autorice escritura, sea fácil envolverlo con `idempotency_key`. - **Quota awareness**: el cliente lleva un contador local de requests del día y avisa cuando se acerca al límite de 20K. - **Retries con backoff** en 429 y 5xx, respetando `Retry-After`. --- ## Decisiones de discovery que este sandbox **acelera** Estos son los puntos del `03_Anexo_Tecnico_Integraciones_Discovery_Balam.docx` y del `04_Checklist_Accesos_Datos_Dependencias_Balam.docx` que dejan de estar "pendientes de validar" en cuanto se ejecuta esta prueba con un API key real: | Item del discovery | Cómo lo cierra este sandbox | |---|---| | Tipo de autenticación, headers requeridos | Ya implementado en `BindClient`: dos headers, listos para producción | | Estructura de URLs por recurso | Confirmada (`/api/{Recurso}` + OData) y probada en mock | | Operaciones de lectura: clientes, facturas, pagos | Demo las ejerce todas. Una vez con API key, basta correr `BIND_BASE_URL=https://api.bind.com.mx npm run demo` | | Filtros y paginación (`$filter`, `$top`, `$skip`) | Validados contra mock con la misma sintaxis que documenta BIND | | Estrategia de pruebas sin sandbox | **Esta es la respuesta**: mock local + cliente tipado + modos read-only / dry-run / write | | Rate limits | El cliente cuenta requests; el mock simula `?simulate=throttle` para probar el backoff | --- ## Pendientes para cerrar con Pedro (sugerencia de mail) > Pedro, para destrabar la integración con BIND necesitamos: > > 1. **API key** generado desde *Perfil → Integraciones* en la cuenta de Balam, idealmente con permisos **solo lectura** primero. > 2. **Subscription Key** si el plan de Balam la requiere (algunos planes en Azure API Management la piden además del Bearer). > 3. Confirmación del **plan contratado** — para saber si 20K req/día aplica o si está reducido. > 4. **Schema exacto** del recurso `Invoices` (especialmente nombres de campos `UUID`, `Folio`, `Status`, `Balance`). Una llamada de ejemplo con una factura real anonimizada serviría: `GET /api/Invoices?$top=1`. > 5. ¿Existe endpoint para descargar **XML/PDF del CFDI**? Por la doc parece que sí, pero falta confirmar la ruta exacta. > > Con (1)–(2) podemos correr el sandbox apuntando a `https://api.bind.com.mx` y validar lo demás de un jalón sin necesidad de otra llamada. --- ## Limitaciones honestas de este sandbox - **El schema de `Invoice`, `Customer`, etc. es una aproximación** — está modelado a partir de la doc pública y del flujo que necesita Balam, no del SDK oficial. Cuando salga el primer `GET` real contra producción, hay que reconciliar nombres de campos (especialmente capitalización y campos opcionales). - **El mock acepta cualquier Bearer**, solo valida que exista. No es un servidor de auth real, es un placeholder. - **El parser de OData del mock solo cubre lo que el cliente genera** (`eq, ne, gt, lt, ge, le, and, or`, paréntesis). No soporta `contains`, `startswith`, funciones, lambdas. Suficiente para el MVP. - **No reproduce la lógica de `$expand`** — si BIND lo soporta para traer `Lines` o `Customer` embebidos, hay que extender. Cuando alguno de estos límites se vuelva una piedra en el zapato, se extiende. Hoy es deliberadamente mínimo.