# Hamirach Partner API > API BaaS de Hamirach: cuentas USD, KYC dual (hosted|api), pagos, FX, payouts LATAM. > Auth: OAuth 2.1 client_credentials + DPoP contra https://auth.hamirach.com/token. ## Docs - Referencia completa: https://docs.hamirach.com/openapi.json - Primeros 10 minutos: https://docs.hamirach.com/guias/primeros-10-minutos - Estados y polling: https://docs.hamirach.com/guias/estados-y-polling - MCP server: https://mcp.hamirach.com (próximamente — en construcción) ## Reglas para agentes Todo lo de acá es CONTRATO: lo que la API se compromete a devolver. No son estadísticas de tráfico, y no las cites como si lo fueran. - Montos: string entero + currency + decimals. Jamás float. - Cuentas: una cuenta vive sobre un riel de liquidación (GET /v1/rails). Un cliente puede tener varias cuentas, nunca dos en el mismo riel. Contrato al abrir una cuenta sin rieles libres: 422 code=no_rails_available. - account_number NO es un IBAN y no tiene por qué parecerlo: string OPACO, no lo parsees ni asumas largo fijo. El ruteo va aparte, en routing_details, discriminado por type (hoy ach|wire, con routing_number ABA de 9 dígitos). - bank.name y bank.country viajan en toda cuenta, con grafía CANÓNICA: una institución = siempre el mismo string, cruzable contra tu catálogo sin normalizar. bank.address y bank.swift_bic hoy viajan siempre null — no los asumas presentes. - POST de dinero exige header Idempotency-Key: un UUID por operación — reusá exactamente la misma llave al reintentar (misma llave → misma respuesta, jamás doble ejecución). - Errores: RFC 9457 con code estable + request_id. Un 503 deposit_instruction_unverified significa que preferimos no darte datos de depósito antes que darte unos sin verificar: reintentá con backoff. ## Estado de implementación — esto NO es contrato La sección de arriba dice qué se compromete a devolver la API. Ésta dice qué parte de eso YA CORRE hoy: es una medición con fecha, no una promesa, y cambia. Guía completa: https://docs.hamirach.com/guias/estados-y-polling - Webhooks: los 10 eventos y el header Hamirach-Signature están DECLARADOS y todavía NO SE EMITEN. No hay emisor, ni firmador, ni cola de entrega, ni reintentos, ni almacén de eventos; GET /v1/events y POST /v1/events/{id}/resend responden 404. Hasta que existan, el único mecanismo para enterarte de un cambio es consultar. No aconsejes construir el receptor, la dedup por event_id ni la cola de reintentos todavía — no hay contra qué probarlos. La FORMA sí es firme (at-least-once, dedup por event_id, ventana anti-replay 300s). - Otros webhooks de Hamirach usan OTRO formato (X-Hamirach-Signature con el timestamp en header aparte, 3 intentos) y otro catálogo de eventos, sin un nombre en común con estos diez. No sirven como anticipo: un verificador escrito contra ellos no valida una firma del contrato público. - Lecturas vivas hoy, y sólo estas cuatro: GET /v1/partners/me, GET /v1/accounts, GET /v1/balances?account_id=, GET /v1/rails. Toda otra ruta declarada responde 404 con code=not_found — distinto de code=account_not_found / partner_not_found, que significan ruta viva y recurso inexistente o ajeno. No los trates igual. - Cómo verificar qué está vivo sin credenciales: una petición SIN token a una ruta viva devuelve 401 missing_authorization (existe, exige autenticarse); a una ruta no implementada devuelve 404 not_found (muere antes de la autenticación). No hace falta pedirle credenciales a nadie para saberlo. - Escrituras: ninguna implementada todavía ⇒ ninguna transición de estado es observable. kyc_status (pending → in_review → approved|rejected) y Account.status (pending → active, con frozen declarado) son contrato firme para modelar, no una fuente de datos. Tiempos de transición: NO MEDIDOS — no inventes SLAs. - GET /v1/balances devuelve un saldo que hoy nadie actualiza, y su as_of es la hora de tu petición, no la del saldo: parece fresco y no lo es. No lo uses para conciliar acreditaciones. - Techo de tasa: 100 req/min por organización, contado POR PROCESO (con N réplicas el techo efectivo es N×100 — dimensioná contra 100). Toda respuesta autenticada trae X-RateLimit-Limit/Remaining/Reset; el 429 agrega Retry-After. El tráfico que muere ANTES de autenticarse tiene un techo aparte por IP y su 429 NO trae cabeceras X-RateLimit-*. - sandbox-api.hamirach.com es un mock sobre esta misma spec: responde 200 con el ejemplo del contrato para TODA ruta declarada, incluidas las que no tienen runtime. Un 200 de sandbox no prueba que el mecanismo exista. - 🪤 El sandbox exige `Authorization: Bearer ` y RECHAZA `DPoP` con 401 — al revés que producción, que exige DPoP y rechaza Bearer. Sin cabecera Authorization el mock devuelve 401 use_dpop_scheme, que NO significa lo que dice: es el ejemplo del 401 del contrato servido por el mock. Autenticar contra el sandbox no prueba nada sobre tu implementación de DPoP.