The Penny Validation API enables account ownership verification by sending a minimal payment (penny) to a beneficiary account. This process generates a CEP (Comprobante Electrónico de Pago) that provides verified account holder information and creates an official payment receipt through BANXICO.
- Account Verification: Confirm the actual account holder matches your intended recipient
 - Official Documentation Get a easy to use URL to obtain a XML or PDF receipts for compliance and record-keeping directly from Banxico's website.
 - Fraud Prevention: Automate payments, reduce errors and fraudulent transactions
 
- Processing Time: CEP generation depends on the beneficiary's bank and may vary significantly
 - Supported Accounts: Currently supports CLABE accounts only (debit card support planned)
 - Future Enhancements: Automatic webhook notification when CEP is ready
 
- Prerequisites You need to have a CENTRALIZING_ACCOUNT and an INSTRUMENT previously created. And you need to create a new webhook for the penny validation message.
 
Endpoint POST /v1/transactions/penny_validation
Request Path parameters: none
Query Parameters: none
Request Body:
{
    "client_id": "{{client_id}}",
    "source_instrument_id": "", //use your own centralizing account where funds are located
    "destination_instrument_id": "" //add the id of the previously generated instrument
}
Response
Status Code: 200 OK
Response Body:
{
    "id": "1eb4b5ac-71f6-4203-aded-4fcb7fd21637",
    "bankId": "14b402f6-5dd1-4cd8-ac54-d12c5647d137",
    "clientId": "09b1c156-73c7-62e1-9a3e-bf705f8f3cbe",
    "externalReference": "123456",
    "trackingId": "20250820FINCHXXXXQ6RPX4",
    "description": "Validacion de cuenta FINCO_PAY",
    "amount": "0.01",
    "currency": "MXN",
    "category": "DEBIT_TRANS",
    "subCategory": "SPEI_DEBIT",
    "transactionStatus": "INITIALIZED",
    "audit": {
        "createdAt": "2025-08-19 13:03:36.194761-06:00",
        "updatedAt": "2025-08-19 13:03:36.194761-06:00",
        "deletedAt": "None",
        "blockedAt": "None"
    },
    "metadata": {
        "dataCep": {
            "cepUrl": "https://www.banxico.org.mx/cep/go?i=90734&s=20250401&d=uBq%2BmmaxaJx72v%2FafY3P2XgtF6PkNIxovKrCElr14xnUBOcXkfe9Vllu8xJ%2FHNPE6YqJ9U%2F2BEcsd3jbcB8OdTqrcqUZYkYQYxzjg4WIVSg%3D",
            "validationId": "f4ebe9af-6c93-4218-8dcb-74cb7456f3cd",
            "status": "INITIALIZED",
            "createdAt": "2025-08-19 13:03:36.732586-06:00"
        }
    }
}As you can see, the response is mostly the same as a money out transaction. But, after around 90 seconds (average time for most banks) the CEP Webhook will return the information parsed in the response. You could also manually fetch the transaction data, together with the updated metadata through the transactions endpoint.
GET /v1/clients/:client_id/transactions/:id{
  "client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "payload": {
    "id_msg": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "msg_name": "CEP",
    "msg_date": "2025-08-15",
    "body": {
      "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "tracking_key": "20250815XXXXXX123456789",
      "beneficiary_account": "000000000000000000",
      "beneficiary_name": "Daniela Paola Santelices Chavez",
      "beneficiary_rfc": "XXXX000000XXX",
      "status": "COMPLETED",
      "processed_at": "2025-08-15 16:42:39.327313-06:00"
    }
  },
  "webhook_type": "CEP"
}{
    "id": "UUID_1",
    "bankId": "UUID_2",
    "clientId": "UUID_3",
    "externalReference": "REF_001",
    "trackingId": "TRACKING_ID_001",
    "description": "Validacion de cuenta Finco",
    "amount": "0.01",
    "currency": "MXN",
    "category": "DEBIT_TRANS",
    "subCategory": "SPEI_DEBIT",
    "transactionStatus": "LIQUIDATED",
    "audit": {
        "createdAt": "2025-01-01 00:00:00.000000-00:00",
        "updatedAt": "2025-01-01 00:00:00.000000-00:00",
        "deletedAt": "None",
        "blockedAt": "None"
    },
    "jsonReference": "",
    "sourceInstrument": {
        "id": "UUID_4",
        "bankId": "UUID_2",
        "clientId": "UUID_3",
        "ownerId": "UUID_5",
        "instrumentAlias": "InternalAccount",
        "instrumentStatus": "ACTIVE",
        "instrumentType": "SENDER_RECEIVER",
        "instrumentDetail": {
            "accountNumber": "ACCOUNT_001",
            "clabeNumber": "CLABE_001",
            "holderName": "ANCV"
        },
        "rfc": "ND"
    },
    "destinationInstrument": {
        "id": "UUID_6",
        "bankId": "UUID_7",
        "clientId": "UUID_3",
        "ownerId": "UUID_3",
        "instrumentAlias": "CLABE",
        "instrumentStatus": "ACTIVE",
        "instrumentType": "SENDER_RECEIVER",
        "instrumentDetail": {
            "accountNumber": "ACCOUNT_002",
            "clabeNumber": "CLABE_002",
            "holderName": "BENEFICIARY_NAME"
        },
        "rfc": "RFC_001"
    },
    "metadata": {
        "dataCep": {
            "cepUrl": "https://example.com/cep/SPECIAL_URL_FOR_CEP",
            "validationId": "UUID_8",
            "beneficiaryName": "BENEFICIARY_NAME",
            "beneficiaryRfc": "RFC_001",
            "status": "COMPLETED",
            "createdAt": "2025-01-01 00:00:00.000000-00:00",
            "processedAt": "2025-01-01 00:00:00.000000-00:00"
        }
    }
}Total attempts: 17 (≈ 3:03 hrs total)
Phases:
- Attempts 1–3: every 90s → 
t0,+1:30,+3:00 - Attempts 4–6: every 5 min → 
+8:00,+13:00,+18:00 - Attempts 7–17: every 15 min → from 
+33:00… up to+3:03:00(final attempt) 
| Attempt | Delay from previous | Elapsed time | Webhook status (typical) | 
|---|---|---|---|
| 1 | 0:00:00 | 0:00:00 | PENDING_CEP | 
| 2 | 0:01:30 | 0:01:30 | PENDING_CEP | 
| 3 | 0:01:30 | 0:03:00 | PENDING_CEP | 
| 4 | 0:05:00 | 0:08:00 | DELAYED | 
| 5 | 0:05:00 | 0:13:00 | DELAYED | 
| 6 | 0:05:00 | 0:18:00 | DELAYED | 
| 7 | 0:15:00 | 0:33:00 | DELAYED | 
| 8 | 0:15:00 | 0:48:00 | DELAYED | 
| 9 | 0:15:00 | 1:03:00 | DELAYED | 
| 10 | 0:15:00 | 1:18:00 | DELAYED | 
| 11 | 0:15:00 | 1:33:00 | DELAYED | 
| 12 | 0:15:00 | 1:48:00 | DELAYED | 
| 13 | 0:15:00 | 2:03:00 | DELAYED | 
| 14 | 0:15:00 | 2:18:00 | DELAYED | 
| 15 | 0:15:00 | 2:33:00 | DELAYED | 
| 16 | 0:15:00 | 2:48:00 | DELAYED | 
| 17 | 0:15:00 | 3:03:00 | FAILED (only if no CEP confirmation) | 
PENDING_CEP→ fromt0through attempt 3DELAYED→ from attempt 4 through attempt 16FAILED→ only if no confirmation by attempt 17
When the CEP is issued, the webhook will send
COMPLETED(see “CEP Webhook Response” example below).
{
  "client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "payload": {
    "id_msg": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "msg_name": "CEP",
    "msg_date": "2025-08-15",
    "body": {
      "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "tracking_key": "20250815XXXXXX123456789",
      "beneficiary_account": "000000000000000000",
      "beneficiary_name": "Daniela Chavez Juarez",
      "beneficiary_rfc": "XXXX000000XXX",
      "status": "DELAYED", // possible: PENDING_CEP | DELAYED | COMPLETED | FAILED
      "processed_at": null
    }
  },
  "webhook_type": "CEP"
}