Skip to content

Operator rejection policy — curated reasons & internal notes

Purpose: the domain truth for what an operator may say to a merchant when a queue item is rejected — a curated, merchant-facing reason, never the operator’s free text. Independent of how any single queue is built. Audience: AI agents (and the platform operators the runbook trains). Status: as of 2026-09-20. Approved by the PO 2026-09-19 as part of Operator Queues (sub-project 2 — multi-platform-roles-admin-operator, § “Sub-project 2 — Operator queues”). Companion daily flow: operator-queues.

Every reject action across the operator queues used to have the same trap: the operator typed a free-text reason and — on the liquidaciones side — that free text was shown to the merchant verbatim (the finding that motivated this policy). On the bank-account side the opposite failure applied: the operator’s reason was audit-only, so a rejected merchant saw only the generic “tu cuenta fue rechazada, vuelve a capturar” (web #911) and re-entered the same wrong account because nobody told them what was actually wrong. Both failures are fixed the same way: split what the operator records into two fields with two different audiences.

  • Every reject is reasonCode + optional internalNote. The operator picks a curated reasonCode from the queue’s catalogue (a <select>, never free text). The merchant sees only that code’s es-MX copy. The operator may additionally write an internalNote — free text for platform staff.
  • The curated reason is the ONLY thing a merchant ever sees. No merchant-facing surface (web card, mobile screen, email/push notification, status projection, API read model) renders the operator’s internalNote. The note lives in audit_log alongside the actor + before/after, and stops there. This is an invariant every queue’s tests must assert (cf. the Web draft’s AC3).
  • internalNote is audit-only and platform-only. It exists so staff can record why — the specific document that was blurry, the exact bank that bounced the transfer, the ticket number — without leaking vendor names, internal process, or timeframes to the merchant.
  • “Otro” (other) requires an internalNote. Whenever the operator falls back to the catch-all reason, the note is mandatory (there is no curated copy to explain the rejection, so there must be an internal record). The merchant sees the generic line “Contacta a soporte para más información.” — never the note.
  • Curated copy is es-MX, actionable, and neutral. Each reason’s merchant copy says what to fix in plain Spanish. It carries no vendor/PSP names, no bank names beyond “admitido / no admitido”, no timeframes (“en 24 h”, “en 2 días hábiles”), and no internal process detail. Mechanism only.
  • The catalogue and its copy are product-owned, not operator-editable. This document is the source of truth for the curated sets. An operator can only select a code and write a note — they cannot add, rename, or reword a reason. Changing the set or its copy is a product decision (PO) landed as a docs PR against this file, mirrored into the enum the API and Web read. Operators propose changes through the PO; they do not ship copy.
  • Codes are stable identifiers; copy is a translation of the code. The reasonCode (e.g. clabe_invalid) is what is stored and audited; the es-MX string is a presentation-layer lookup keyed by the code, so copy can be reworded in one place without a data migration.

Each queue that rejects has its own curated set. other is present in all three, always requires a note, and always renders the same generic merchant line. The codes below are the canonical identifiers; the API enum and the Web <select> mirror them.

Cuentas bancarias — bank-account verification

Section titled “Cuentas bancarias — bank-account verification”
reasonCode Merchant-facing es-MX copy
holder_mismatch El titular de la cuenta no coincide con el comercio. Sube una cuenta a nombre del comercio.
clabe_invalid La CLABE no es válida. Verifica los 18 dígitos y vuelve a capturarla.
bank_not_supported El banco de la cuenta no está admitido. Registra una cuenta de un banco admitido.
illegible_document El documento no es legible o está incompleto. Sube una carátula clara y completa.
statement_mismatch La carátula no corresponde a la cuenta capturada. Sube la carátula de esa misma cuenta.
other Contacta a soporte para más información. (nota interna obligatoria)
reasonCode Merchant-facing es-MX copy
bank_rejected El banco rechazó la cuenta destino. Revisa los datos de tu cuenta y vuelve a solicitar.
account_outdated Los datos de la cuenta están desactualizados. Actualiza tu cuenta bancaria y vuelve a solicitar.
under_review El movimiento está en revisión. No necesitas hacer nada por ahora.
other Contacta a soporte para más información. (nota interna obligatoria)

Datos fiscales — business / tax verification

Section titled “Datos fiscales — business / tax verification”
reasonCode Merchant-facing es-MX copy
rfc_name_mismatch El RFC no coincide con la razón social. Corrige tus datos fiscales.
rfc_inactive El RFC no está vigente. Verifica tu situación fiscal y actualiza tus datos.
fiscal_incomplete Tus datos fiscales están incompletos. Completa la información faltante.
other Contacta a soporte para más información. (nota interna obligatoria)
  • Operator submits other with no note → the queue rejects the submission (validation error). “Otro” without a note is never persisted.
  • Operator writes an internalNote but picks a real curated code → allowed and encouraged; the note is stored, the merchant still sees only the curated copy, never the note.
  • A rejection reason exists in the model but not in this catalogue → treat it as unmapped and render the generic other copy to the merchant rather than leaking a raw slug (same defensive posture as the verification _fallback mapping, decisions 2026-09-02). Then file a docs change to add the code.
  • Identity checks are locked to Admin. Operators reject business/fiscal checks (this Datos fiscales catalogue); identity revoke/unlock/attest is an Admin action and out of this policy’s operator scope — see the runbook’s escalation section.
  • Final wording of each es-MX string is PO-confirmable; the codes are fixed. Any change is a docs PR against this file. Mirror unresolved items in ../progress/open-questions-and-todos.md.