Validate a SPEI transfer from a receipt image or PDF
Goal
Let Veriko extract the transfer fields from a SPEI receipt image or PDF and validate the transfer against the Banxico CEP — without you manually parsing the comprobante. The resulting validation record is identical to one created with POST /v1/validate; you can download the CEP certificate and enable retries the same way.
Prerequisites
- An active API key (
veriko_…) with thevalidations_ocr:createpermission. - A receipt in JPEG, PNG, WebP, or a PDF of 1 to 3 pages, up to 12 MB.
- The receipt must be a legible SPEI comprobante (electronic receipt). Masked account numbers are resolved against your registered beneficiaries list when the masked form matches an account on file.
Steps
1. Submit the receipt
Send the receipt (image or PDF) as a base64-encoded string in the image field, or pass a publicly accessible URL in image_url. You can optionally provide cuenta_beneficiaria as a disambiguation hint when the receipt shows a masked account.
Option A — remote URL:
curl -X POST 'https://api.veriko.mx/v1/validate-ocr' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{
"image_url": "https://storage.example.com/receipts/spei-receipt-001.png"
}'Option B — base64 receipt:
curl -X POST 'https://api.veriko.mx/v1/validate-ocr' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{
"image": "<base64-encoded-receipt-bytes>"
}'Synchronous 200 response — OCR extracted the fields and Banxico replied in-band:
{
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"type": "validation",
"attributes": {
"validation_type": "ocr",
"status": "valid",
"ocr_confidence": 0.96,
"ocr_result": {
"fecha": "2025-04-10",
"monto": 12500.00,
"clave_rastreo": "MXBA20250410003456"
},
"image_path": "comprobantes/c3d4e5f6-a7b8-9012-cdef-123456789012.png",
"processing_time_ms": 2840,
"created_at": "2025-04-10T11:30:00Z",
"completed_at": "2025-04-10T11:30:02Z"
},
"links": {
"self": "/v1/validations/c3d4e5f6-a7b8-9012-cdef-123456789012",
"cep_xml": "/v1/validations/c3d4e5f6-a7b8-9012-cdef-123456789012/cep?format=xml",
"cep_pdf": "/v1/validations/c3d4e5f6-a7b8-9012-cdef-123456789012/cep?format=pdf"
}
}
}If the result remains not_found, error_code may identify what needs checking:
not_found_identity: date, tracking key or reference, and banks.not_found_account: beneficiary account; the amount does match.not_found_amount: amount; the account does match.not_found_account_and_amount: account and amount.not_found_account_or_amount: the payment exists, but the field could not be isolated.
A captcha, ambiguous response or failed control does not produce a specific diagnosis. Absence of a diagnosis does not confirm the details either.
If you sent cuenta_beneficiaria and the receipt shows another account, either complete or masked with at least three visible trailing digits that are not the end of the one you sent, the response includes account_conflict with sent_last4 and read_last4: the last digits of each (for a masked account, the ones the receipt shows). The account sent prevails and the verdict does not change; the field, which is also recorded in normalization_warnings, explains an unexpected not_found.
If the receipt shows neither the tracking key nor the numeric reference (many apps keep them behind "View more details"), the validation ends with preflight_failed, and its detail (field_errors[].detail and error_message) says to send the screenshot of the full details. The code does not change.
Receipt as a PDF
The image field also accepts a PDF of 1 to 3 pages, with the same 12 MB limit. The PDF travels as is, without being converted into an image, and it is rejected when it is encrypted, when it was modified after being issued, or when it carries active or hidden content: each case has its own code (pdf_encrypted, pdf_modified_after_issue, pdf_active_content, pdf_hidden_content…).
When the PDF is the CEP issued by Banxico, its data is read directly from its text, without OCR. Once Banxico confirms the payment, the seal and original string of the file are also checked against those of the official CEP, and the result is in banxico_result._cep_upload:
"_cep_upload": { "authenticated": true }With "authenticated": false, differing_fields lists the fields that do not match and normalization_warnings reports it. The verdict does not change: it is still Banxico's.
Several possible accounts
When the receipt does not show the account and you do not know which of several the payment went to, cuentas_candidatas replaces cuenta_beneficiaria:
{
"image_url": "https://storage.example.com/comprobantes/spei-003.png",
"cuentas_candidatas": ["012180004412345678", "002010077777777771"]
}The list carries 2 to 3 valid, distinct accounts, and it cannot be combined with cuenta_beneficiaria: sending both answers 422 cuenta_y_candidatas_excluyentes, and a list that does not comply, 422 cuentas_candidatas_invalidas, both before any quota is consumed. It takes priority over the account read from the image, it also applies with ?async=1, and it consumes a single quota unit. The winning account comes back in full in normalized_data.cuenta_beneficiaria and its position in candidate_match. If none matches, the response is a 422 and the unit is consumed all the same, because the receipt was already read. The detail of the result is in the direct validation guide.
Not keeping the receipt
By default the receipt file is kept. If you prefer that it does not stay on the platform, send retain_image as false:
{
"image": "<receipt-bytes-in-base64>",
"retain_image": false
}The file is deleted as soon as the validation reaches a terminal status in which it is no longer needed, also with ?async=1. The verdict and the extracted data are kept. The validation says so with image_retained as false, and GET /v1/validations/{id}/image responds 410 image_not_retained instead of a generic 404. A retain_image that is not a boolean responds 422 invalid_retain_image before any quota is consumed.
To delete a validation later, with its CEP and everything derived, see Permanently delete a validation.
2. Handle the async path (?async=1)
For high-volume pipelines, add ?async=1 to receive an immediate 202 and let the OCR + CEP query run in the background. The receipt is sanitized and persisted before the job is enqueued, so you won't lose it if the worker is briefly down.
curl -X POST 'https://api.veriko.mx/v1/validate-ocr?async=1' \
-H 'Authorization: Bearer veriko_••••' \
-H 'Content-Type: application/json' \
-d '{ "image_url": "https://storage.example.com/receipts/spei-receipt-002.png" }'Response 202:
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "validation",
"attributes": { "status": "queued" }
}
}Poll GET /v1/validations/{id} every 2–5 seconds until status reaches a terminal value. Honor the Retry-After header when present.
3. Download the CEP certificate
Once status is valid and the response carries links.cep_pdf and links.cep_xml, download the certificate just as you would for a manual validation:
curl 'https://api.veriko.mx/v1/validations/c3d4e5f6-.../cep?format=pdf' \
-H 'Authorization: Bearer veriko_••••' \
--output cep.pdf4. Retrieve the stored receipt
The receipt is persisted on the platform and accessible via GET /v1/validations/{id}/image, unless the validation was sent with retain_image=false (it responds 410 image_not_retained). Use this to display the comprobante in your UI or to archive it alongside the CEP. A PDF always arrives as an attachment (Content-Disposition: attachment) with Content-Type: application/pdf; save the file with the extension that header indicates.
curl 'https://api.veriko.mx/v1/validations/c3d4e5f6-.../image' \
-H 'Authorization: Bearer veriko_••••' \
--output comprobante.pngWhat's next
- Validate using explicit transfer fields instead of a receipt: see Validate a SPEI transfer (direct).
- Browse all your validation results and download exports: see Browse and manage validations.
- Configure automatic retries for
not_foundorcep_unavailableoutcomes: see Configure retry policy. - Permanently delete a validation and its receipt: see Permanently delete a validation.
- What the platform keeps and for how long: see Retention of receipts and data.