Un beneficiario es una cuenta destino guardada bajo el user_id del API key (lista blanca por usuario, nunca compartida entre cuentas). Persistir un beneficiario tiene tres efectos concretos: 1) auto-completa banco y tipo en validaciones futuras, 2) sirve de fuente prioritaria para resolver PAN enmascarados que el OCR no puede completar, y 3) habilita los alias bilingües que usan las exportaciones y el estado de cuenta de Finanzas.

Los tres tipos de cuenta

MexCep detecta el account_type por el número de dígitos (src/Support/CardBins.php::detectAccountType):

  • clabe — 18 dígitos. El banco se deriva del prefijo de 3 dígitos contra BanxicoBanks::bankFromClabe. No requiere bank_code en el body.
  • card — 13 a 19 dígitos, excluyendo 18 (reservado para CLABE) y 10 (reservado para teléfono). El banco se infiere por BIN/dictionary. No requiere bank_code.
  • phone — 10 dígitos exactos. Es una clave DiMo del SPEI. bank_code es obligatorio en el body porque no existe un mapeo público de teléfono → banco; el endpoint responde 422 bank_code_required_for_phone cuando falta. El catálogo de códigos vive en GET /v1/public/banks.

Para validar la estructura sin persistir (CLABE checksum o Luhn) usa GET /v1/beneficiaries/validate-account.

PAN enmascarado y OCR

Cuando POST /v1/validate-ocr extrae una cuenta de un comprobante con la forma ••••5678 o 4111 •••• •••• 1111, el pipeline llama a NormalizationService::resolveMaskedAccount. La búsqueda usa los últimos dígitos visibles y se ejecuta en este orden:

  1. Lista blanca del usuarioBeneficiaryRepository::findByPartialAccount filtra los beneficiarios activos del usuario por sufijo + length_hint + visible_first. Coincidencia única → resuelto.
  2. Catálogo global por sufijo — solo si la lista blanca no resuelve. Devuelve masked_catalog_unique cuando hay match único, masked_probe_pending con candidatos cuando hay 2..N (N = clabe.probe_max_candidates, default 3), masked_ambiguous_in_catalog cuando hay más, o masked_unresolvable cuando no aparece nadie con ese sufijo.

Sin beneficiarios registrados un comprobante enmascarado cae al global por sufijo y puede quedar ambiguo. Registrar las cuentas que validás frecuentemente convierte estos casos en resoluciones determinísticas.

Crear, archivar, importar

  • POST /v1/beneficiaries crea uno. Acepta un alias opcional que el sistema usa en exportaciones.
  • GET /v1/beneficiaries lista con filtro tri-estado ?with_archived= (0/null = activos, 1 = archivados, omitido = ambos).
  • DELETE /v1/beneficiaries/{id} archiva — es un soft-delete (UPDATE user_beneficiaries SET status='inactive'). El API público no expone hard-delete; los registros archivados pueden recuperarse desde el panel de admin si el operador del deploy lo necesita.
  • PATCH /v1/beneficiaries/{id} edita alias o bank_code (obligatorio si el tipo es phone y el campo se incluye en la edición).

Para alta masiva el endpoint es POST /v1/beneficiaries/imports — ver Imports masivos para el flujo preview → commit y el parser híbrido manual+Gemini.