Skip to content
Last updated

Penny Validation API

Overview

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.

Key Benefits

  • 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

Important Considerations

  • 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
  • Transient initial state Immediately after creation, metadata.dataCep.status may briefly be INITIALIZED. Treat it as equivalent to PENDING for client logic/UI.

CEP status values

Client-facing values for metadata.dataCep.status are:

  • INITIALIZEDtransient initial state right after creation; typically flips to PENDING within seconds. Do not build distinct flows based on this state.
  • PENDING — waiting for CEP retrieval/confirmation.
  • DELAYED — CEP is taking longer than expected; background retries continue.
  • COMPLETED — CEP available and validated.
  • FAILED — CEP unavailable or permanently failed.

Note: Treat INITIALIZED the same as PENDING in client logic and UI.

Penny Validation endpoint

  • 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": "PENDING",
            "createdAt": "2025-08-19 13:03:36.732586-06:00"
        }
    }
}

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.

Note: Immediately after creation, the very first read may briefly return INITIALIZED before transitioning to PENDING. Clients should treat INITIALIZED as equivalent to PENDING for logic and UI.

GET /v1/clients/:client_id/transactions/:id

CEP Webhook Response

{
  "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", // possible: INITIALIZED (transient) | PENDING | DELAYED | COMPLETED | FAILED
      "processed_at": "2025-08-15 16:42:39.327313-06:00"
    }
  },
  "webhook_type": "CEP"
}

Transaction response

{
    "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", // possible: INITIALIZED (transient) | PENDING | DELAYED | COMPLETED | FAILED
            "createdAt": "2025-01-01 00:00:00.000000-00:00",
            "processedAt": "2025-01-01 00:00:00.000000-00:00"
        }
    }
}

Delivery & Retry Policy (CEP Webhook)

Total attempts: 17 (≈ 3:03 hrs total)
Phases:

  • Attempts 1–3: every 90st0, +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)

Detailed schedule (with webhook status)

AttemptDelay from previousElapsed timeWebhook status (typical)
10:00:000:00:00PENDING
20:01:300:01:30PENDING
30:01:300:03:00PENDING
40:05:000:08:00DELAYED
50:05:000:13:00DELAYED
60:05:000:18:00DELAYED
70:15:000:33:00DELAYED
80:15:000:48:00DELAYED
90:15:001:03:00DELAYED
100:15:001:18:00DELAYED
110:15:001:33:00DELAYED
120:15:001:48:00DELAYED
130:15:002:03:00DELAYED
140:15:002:18:00DELAYED
150:15:002:33:00DELAYED
160:15:002:48:00DELAYED
170:15:003:03:00FAILED (only if no CEP confirmation)

Webhook-reported states

  • PENDING → from t0 through attempt 3
  • DELAYED → from attempt 4 through attempt 16
  • FAILEDonly if no confirmation by attempt 17

When the CEP is issued, the webhook will send COMPLETED (see “CEP Webhook Response” example below).

Example payload with intermediate states

{
  "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: INITIALIZED (transient) | PENDING | DELAYED | COMPLETED | FAILED
      "processed_at": null
    }
  },
  "webhook_type": "CEP"
}