- Estructura nueva: README maestro, bitacora/ (REGISTRO, PENDIENTES, plantillas), propuesta/, fuentes/; material superado a _archivado/ - Propuesta v1.0: MVP BIND-first con emisión asistida MXN/USD (dry-run + confirmación, timbra PAC de BIND), 112-136 h / $67,200-$81,600 + IVA, stack .NET 10 + EF Core + Angular 21 + PostgreSQL 17 sobre Azure - Bitácora: historial de correos + 2 llamadas (incl. revisión 4-jun) y pendientes - Prototipo y diagrama actualizados a v1.0; precios de Azure verificados - Archivo ajeno (proyecto EOS) retirado del repo Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.4 KiB
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:
- Entender la forma real del API de BIND ERP antes de tener el API key.
- Probar el cliente tipado contra un mock que replica los headers, el formato OData y el rate-limit observable de BIND.
- Cuando Pedro entregue las credenciales reales, cambiar dos variables de entorno y apuntar el mismo código a
https://api.bind.com.mxsin 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 <API_KEY> + Ocp-Apim-Subscription-Key: <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 (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
POSTreal 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+.
# 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:
$env:BIND_BASE_URL = "https://api.bind.com.mx"
$env:BIND_API_KEY = "<key del perfil de usuario de Balam>"
$env:BIND_SUBSCRIPTION_KEY = "<si aplica>"
$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): bloqueaPOST/PUT/PATCH/DELETEen 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
addActivityestá aislado para que cuando se autorice escritura, sea fácil envolverlo conidempotency_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:
- API key generado desde Perfil → Integraciones en la cuenta de Balam, idealmente con permisos solo lectura primero.
- Subscription Key si el plan de Balam la requiere (algunos planes en Azure API Management la piden además del Bearer).
- Confirmación del plan contratado — para saber si 20K req/día aplica o si está reducido.
- Schema exacto del recurso
Invoices(especialmente nombres de camposUUID,Folio,Status,Balance). Una llamada de ejemplo con una factura real anonimizada serviría:GET /api/Invoices?$top=1.- ¿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.mxy 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 primerGETreal 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 soportacontains,startswith, funciones, lambdas. Suficiente para el MVP. - No reproduce la lógica de
$expand— si BIND lo soporta para traerLinesoCustomerembebidos, hay que extender.
Cuando alguno de estos límites se vuelva una piedra en el zapato, se extiende. Hoy es deliberadamente mínimo.