Onboarding de sub-comercios

MODO PRUEBA

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.

1

Alta mínima

POST /v1/onboarding/applications con lo mínimo para identificar al sub-comercio.

draft
2

Completa datos y documentos

PUT .../{id} las veces que haga falta — domicilio, CLABE, representante, los 4 documentos.

draft / needs_info
3

Envía a revisión

POST .../{id}/submit dispara la verificación — en fase 0, síncrona.

approved / rejected / needs_info

1. Alta mínima

Solo legal_name, person_type (fisica o moral) y contact_email son obligatorios para crear la solicitud.

bash
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"
  }'
201 · respuesta real
{
  "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.

CampoNota
rfc13 caracteres (persona física) o 12 (moral); formato validado, no contra el padrón real del SAT (eso es el verificador gated).
regimen_fiscalClave del régimen (catálogo SAT), como texto libre en fase 0.
domicilio_calle / _numero / _colonia / _cp / _ciudad / _estadoDomicilio fiscal completo, los 6 campos son obligatorios para enviar a revisión.
clabe_liquidacion18 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_nameNombre del representante legal.
contact_phoneOpcional para enviar a revisión (no está en la lista de campos obligatorios).
giroGiro comercial, texto libre.
documentsArreglo de los 4 documentos requeridos (ver tabla abajo). PUT reemplaza el documento de ese document_type si ya existía.

Cada documento en documents[]:

CampoDescripción
document_typeUno de: ine, comprobante_domicilio, constancia_fiscal, caratula_estado_cuenta — los 4 son obligatorios, todos, para poder enviar a revisión.
referenceReferencia opaca al archivo real (fase 0 no almacena binarios — ver el aviso de honestidad abajo).
reference_hashHash (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.
bash
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…" }
    ]
  }'
200 · respuesta real
{
  "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)" ]
}
Honestidad: fase 0 no almacena los archivos
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:

bash
curl -s -X POST https://api.winal.com.mx/v1/onboarding/applications/75deacfb-.../submit \
  -H "Authorization: Bearer $SK"
200 · respuesta real
{
  "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

DesenlaceCuándostatus_reason real observado
approvedRFC 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_infoRFC 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)."
rejectedCoincidencia 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:

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

EstadoEditableDescripción
draftRecién creada o corregida; falta enviar a revisión.
submittedNoTransitorio: acaba de entrar a revisión (en fase 0 dura microsegundos, la verificación es síncrona).
under_reviewNoEn verificación — si el proceso automático truena aquí, queda resoluble manualmente.
needs_infoFormato de RFC/CLABE inválido, o un operador pidió más información — corrige y reenvía.
approvedNoTerminal. Lista para Winal Connect.
rejectedNoTerminal. Coincidencia en listas, o rechazo manual.

Endpoints

MétodoRutaNotas
POST/v1/onboarding/applicationsAlta 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}/submitDispara 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

HTTPcodeCausa
400onboarding_application.invalid_legal_nameFalta legal_name al crear.
400onboarding_application.invalid_person_typeperson_type ausente o distinto de fisica/moral.
400onboarding_application.invalid_contact_emailFalta contact_email al crear.
400onboarding_application.invalid_documentUn documento trae document_type inválido, o falta reference/reference_hash.
404onboarding_application.not_foundEl id no existe.
409onboarding_application.transition_conflictPUT/submit sobre una solicitud que ya no es editable, o carrera entre dos requests.
400onboarding_application.missing_fieldssubmit sin todos los campos obligatorios — el mensaje lista exactamente cuáles faltan.
400onboarding_application.missing_documentssubmit sin los 4 documentos requeridos.