Antes de ligar un sub-comercio a Winal Connect, necesita una solicitud de
alta aprobada: nombre legal, RFC, domicilio, representante legal, CLABE de liquidación y
cuatro documentos. Tu plataforma administra estas solicitudes con su propia API key (análogo a
POST /v1/accounts de Stripe Connect); la resolución (aprobar, rechazar, pedir más
información) la hace Winal — tu plataforma no puede autoaprobar su propio KYB.
Alta mínima
POST /v1/onboarding/applications con lo mínimo para identificar al sub-comercio.
Completa datos y documentos
PUT .../{id} las veces que haga falta — domicilio, CLABE, representante, los 4 documentos.
Envía a revisión
POST .../{id}/submit dispara la verificación — en fase 0, síncrona.
1. Alta mínima
Solo legal_name, person_type (fisica o moral) y contact_email son obligatorios para crear la solicitud.
curl -s https://api.winal.com.mx/v1/onboarding/applications \
-H "Authorization: Bearer $SK" \
-H "Content-Type: application/json" \
-d '{
"legal_name": "Tienda Docs SA de CV",
"person_type": "moral",
"contact_email": "docs@example.mx"
}'
{
"id": "75deacfb-ff0d-476e-8292-f9471941694a",
"object": "onboarding_application",
"status": "draft",
"legal_name": "Tienda Docs SA de CV",
"person_type": "moral",
"contact_email": "docs@example.mx",
"created_at": "2026-07-08T06:48:56.880311+00:00",
"updated_at": "2026-07-08T06:48:56.880311+00:00",
"documents": []
}
No exige Idempotency-Key (no mueve dinero). person_type: "fisica" también es válido para un sub-comercio persona física.
2. Completa datos y documentos
PUT /v1/onboarding/applications/{id} acepta un patch parcial (los campos que omitas no
se tocan) mientras la solicitud siga editable (draft o needs_info). Solo
submit exige que todo esté completo.
| Campo | Nota |
|---|---|
rfc | 13 caracteres (persona física) o 12 (moral); formato validado, no contra el padrón real del SAT (eso es el verificador gated). |
regimen_fiscal | Clave del régimen (catálogo SAT), como texto libre en fase 0. |
domicilio_calle / _numero / _colonia / _cp / _ciudad / _estado | Domicilio fiscal completo, los 6 campos son obligatorios para enviar a revisión. |
clabe_liquidacion | 18 dígitos con dígito de control válido (algoritmo Banxico 3-7-1) — la CLABE donde el sub-comercio recibirá sus dispersiones de Connect. Se expone enmascarada (clabe_masked) en cualquier respuesta HTTP; nunca completa. |
legal_representative_name | Nombre del representante legal. |
contact_phone | Opcional para enviar a revisión (no está en la lista de campos obligatorios). |
giro | Giro comercial, texto libre. |
documents | Arreglo de los 4 documentos requeridos (ver tabla abajo). PUT reemplaza el documento de ese document_type si ya existía. |
Cada documento en documents[]:
| Campo | Descripción |
|---|---|
document_type | Uno de: ine, comprobante_domicilio, constancia_fiscal, caratula_estado_cuenta — los 4 son obligatorios, todos, para poder enviar a revisión. |
reference | Referencia opaca al archivo real (fase 0 no almacena binarios — ver el aviso de honestidad abajo). |
reference_hash | Hash (recomendado SHA-256 hex) del contenido del documento, para poder auditar después que no se alteró — Winal no valida el algoritmo, solo que venga no vacío. |
curl -s -X PUT https://api.winal.com.mx/v1/onboarding/applications/75deacfb-... \
-H "Authorization: Bearer $SK" \
-H "Content-Type: application/json" \
-d '{
"rfc": "TDS900101AB1",
"regimen_fiscal": "601",
"domicilio_calle": "Av. Reforma", "domicilio_numero": "123",
"domicilio_colonia": "Juárez", "domicilio_cp": "06600",
"domicilio_ciudad": "Ciudad de México", "domicilio_estado": "CDMX",
"clabe_liquidacion": "646180157000000004",
"legal_representative_name": "Juan Pérez López",
"contact_phone": "5512345678",
"giro": "Comercio al por menor",
"documents": [
{ "document_type": "ine", "reference": "INE-DOC-001", "reference_hash": "a1b2c3…" },
{ "document_type": "comprobante_domicilio", "reference": "CFE-REF-002", "reference_hash": "b2c3d4…" },
{ "document_type": "constancia_fiscal", "reference": "SAT-CSF-003", "reference_hash": "c3d4e5…" },
{ "document_type": "caratula_estado_cuenta", "reference": "EDOCTA-004", "reference_hash": "d4e5f6…" }
]
}'
{
"id": "75deacfb-ff0d-476e-8292-f9471941694a",
"object": "onboarding_application",
"status": "draft",
"legal_name": "Tienda Docs SA de CV",
"rfc": "TDS900101AB1",
"clabe_masked": "**** **** **** 0004",
"legal_representative_name": "Juan Pérez López",
"documents": [ { "document_type": "ine", "reference": "INE-DOC-001", "reference_hash": "a1b2c3…", "created_at": "..." }, "... (4 en total)" ]
}
reference/reference_hash son metadatos de auditoría, no un vault de
documentos: tu integración decide dónde vive el archivo real (tu propio storage, o el que uses hoy
para KYC). Un vault de documentos con antivirus y retención legal propia es una pieza gated, no
construida en fase 0.
3. Envía a revisión
POST /v1/onboarding/applications/{id}/submit hace, en la MISMA llamada: valida
completitud (los campos de arriba + los 4 documentos), valida el formato de RFC y CLABE, y
corre el check de listas (PEP/OFAC/SAT 69-B). En fase 0 el check de listas está simulado — aprueba
cualquier RFC salvo el centinela de pruebas — así que una solicitud completa y bien formada queda
approved al instante:
curl -s -X POST https://api.winal.com.mx/v1/onboarding/applications/75deacfb-.../submit \
-H "Authorization: Bearer $SK"
{
"id": "75deacfb-ff0d-476e-8292-f9471941694a",
"object": "onboarding_application",
"status": "approved",
"status_reason": "Verificación automática aprobada: RFC y CLABE con formato válido, sin coincidencias en listas.",
"submitted_at": "2026-07-08T06:49:17.364553+00:00",
"resolved_at": "2026-07-08T06:49:17.364553+00:00",
"resolved_by": "system:auto_verification",
"...": "resto de los campos igual que arriba"
}
Ya approved, la solicitud está lista para ligarse a una cuenta Connect.
Los tres desenlaces de submit
| Desenlace | Cuándo | status_reason real observado |
|---|---|---|
approved | RFC y CLABE con formato válido, sin coincidencia en listas. | "Verificación automática aprobada: RFC y CLABE con formato válido, sin coincidencias en listas." |
needs_info | RFC o CLABE con formato inválido (typo, dígito de control incorrecto). | "El RFC no tiene un formato válido para el tipo de persona declarado." / "La CLABE de liquidación no tiene un dígito de control válido (18 dígitos exigidos)." |
rejected | Coincidencia en el check de listas (terminal — no hay reintento). | "Coincidencia simulada en lista de bloqueo (PEP/OFAC/SAT 69-B) — RFC centinela de pruebas." |
needs_info vuelve la solicitud editable: corrige con PUT y manda
submit otra vez. Si el proceso de verificación truena entre pasos, la solicitud queda
resoluble manualmente por un operador (nunca en limbo).
Verificación KYB: qué es Sim y qué está gated
Hoy el check de listas corre contra un verificador simulado (aprueba todo, sin llamadas de red) — pensado para que puedas probar el flujo completo sin depender de un proveedor real. Un KYB de producción de verdad necesita:
- Screening real contra listas PEP/OFAC/ONU y la Lista de Personas Bloqueadas de la UIF.
- Consulta contra el padrón real del SAT (el 69-B de contribuyentes con operaciones simuladas) y buró de crédito.
- Almacenamiento con retención legal de los documentos (INE, comprobante de domicilio, constancia fiscal, carátula de estado de cuenta).
El puerto IIdentityVerifier ya separa esto del resto del flujo: cambiar el verificador
simulado por uno real (probablemente asíncrono — webhook o polling contra el proveedor de listas) no
toca el resto del ciclo de vida de la solicitud. Es trabajo pendiente del dueño (certificación con un
proveedor de KYB), no una limitación del código.
Resolución manual (portal)
Un operador de Winal puede aprobar, rechazar o pedir más información manualmente desde el
portal → Onboarding — por ejemplo, si el proceso automático se detuvo a medio camino, o para
revisar un caso needs_info a mano. Esa resolución vive bajo /admin (no es
un endpoint público de /v1): tu plataforma, con su propia API key, no puede
autoaprobarse su propio KYB — la misma razón por la que en Stripe Connect la plataforma tampoco
aprueba sus propias cuentas Custom.
Estados de una solicitud
| Estado | Editable | Descripción |
|---|---|---|
draft | Sí | Recién creada o corregida; falta enviar a revisión. |
submitted | No | Transitorio: acaba de entrar a revisión (en fase 0 dura microsegundos, la verificación es síncrona). |
under_review | No | En verificación — si el proceso automático truena aquí, queda resoluble manualmente. |
needs_info | Sí | Formato de RFC/CLABE inválido, o un operador pidió más información — corrige y reenvía. |
approved | No | Terminal. Lista para Winal Connect. |
rejected | No | Terminal. Coincidencia en listas, o rechazo manual. |
Endpoints
| Método | Ruta | Notas |
|---|---|---|
| POST | /v1/onboarding/applications | Alta mínima. |
| GET | /v1/onboarding/applications | ?status= y ?limit= opcionales. |
| GET | /v1/onboarding/applications/{id} | Incluye documents[] (las listas no). |
| PUT | /v1/onboarding/applications/{id} | Patch parcial; solo mientras editable. |
| POST | /v1/onboarding/applications/{id}/submit | Dispara la verificación síncrona. |
La resolución manual (/admin/tenants/{tenantId}/onboarding/applications/{id}/approve
y equivalentes de reject/request-info) es una operación de portal para
operadores de Winal, no un endpoint que documentemos como parte de tu integración pública.
Errores de onboarding_application
| HTTP | code | Causa |
|---|---|---|
400 | onboarding_application.invalid_legal_name | Falta legal_name al crear. |
400 | onboarding_application.invalid_person_type | person_type ausente o distinto de fisica/moral. |
400 | onboarding_application.invalid_contact_email | Falta contact_email al crear. |
400 | onboarding_application.invalid_document | Un documento trae document_type inválido, o falta reference/reference_hash. |
404 | onboarding_application.not_found | El id no existe. |
409 | onboarding_application.transition_conflict | PUT/submit sobre una solicitud que ya no es editable, o carrera entre dos requests. |
400 | onboarding_application.missing_fields | submit sin todos los campos obligatorios — el mensaje lista exactamente cuáles faltan. |
400 | onboarding_application.missing_documents | submit sin los 4 documentos requeridos. |