> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mattildapayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SPEI bank transfers

> Accept payments by SPEI bank transfer in Mexico.

SPEI (Sistema de Pagos Electrónicos Interbancarios) lets your buyers pay with a bank transfer from
any bank in Mexico. When you create a SPEI transaction, mattilda Pay generates a CLABE (an 18-digit
bank account number) for that charge. The buyer transfers the amount to that CLABE from their banking
app, and the transaction is confirmed automatically once the deposit arrives.

| Property | Value |
| :- | :- |
| `method` | `spei` |
| Country | `MX` |
| Currency | `MXN` |
| Payment flow | Asynchronous (push payment) |

## How it works

1. You create a transaction with `method` set to `spei`.
2. mattilda Pay generates a CLABE for the charge and returns an `approval_url` with the payment
   instructions.
3. You redirect the buyer to the `approval_url`, or show the payment instructions in your own
   checkout.
4. The buyer transfers the **exact amount** to the CLABE from their bank.
5. When the deposit is received, the transaction moves to `capture_succeeded` and you receive a
   `transaction.captured` [webhook](/guides/features/webhooks/overview).

## Create a SPEI transaction

Create a transaction using the [`POST /transactions`](/reference/transactions/new-transaction) endpoint
with a redirect payment method.

```bash theme={"dark"}
curl -X POST https://api.mattildapayments.com/transactions \
  -H "Authorization: Bearer [JWT]" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 129900,
    "currency": "MXN",
    "country": "MX",
    "external_identifier": "order-1234",
    "buyer_external_identifier": "user-1234",
    "payment_method": {
      "method": "spei",
      "redirect_url": "https://example.com/callback"
    }
  }'
```

* `amount` - The amount to charge, in the smallest currency unit (cents). `129900` is \$1,299.00 MXN.
* `external_identifier` - Your own reference for the order. We recommend always sending it, as it is
  returned in every webhook and helps you reconcile the payment.
* `payment_method.redirect_url` - Where to send the buyer back to after they see the payment
  instructions.

The response is a transaction that is pending payment. It includes:

* `approval_url` - A page hosted by mattilda Pay with the payment instructions for the buyer.
* `payment_service_transaction_id` - The CLABE generated for this charge.

> [!WARNING]
>
> Don't send `store: true` and don't reuse a stored payment method for SPEI transactions. Every
> charge must get its own CLABE so that the deposit can be matched to it automatically.

## Show the payment instructions

Redirect the buyer to the `approval_url`. The page shows the information the buyer needs to make the
transfer from their banking app:

* **Total a pagar**: the amount to transfer.
* **Banco**: the receiving bank.
* **CLABE**: the account number to transfer to.
* **Beneficiario**: your business name.

Once the payment is confirmed, the same page shows a success message.

If you prefer to show the instructions in your own checkout, use the CLABE in
`payment_service_transaction_id` and the transaction amount.

## Payment confirmation

SPEI transfers are usually confirmed within **30 minutes**, depending on the buyer's bank.

When the deposit is received, the transaction status changes to `capture_succeeded` and a
`transaction.captured` webhook is sent to your endpoint. Use the webhook to fulfill the order, and
[verify its signature](/guides/features/webhooks/signatures) before trusting its content.

You can also check the status at any time with the
[`GET /transactions/{transaction_id}`](/reference/transactions/get-transaction) endpoint.

## Amount rules

The buyer must transfer the **exact amount** of the transaction. Deposits that differ from the
transaction amount by more than \$1.00 MXN can't be matched to the charge and are **returned to the
buyer's account** automatically.

Partial payments are not supported: a charge can't be paid with several transfers.

> [!NOTE]
>
> Ask your buyers to copy the CLABE and the amount exactly as shown in the payment instructions.
> Most failed SPEI payments are caused by a typo in the CLABE or an amount that doesn't match.

## Best practices

* Create a new transaction for every charge. Don't ask buyers to reuse a CLABE from a previous
  payment.
* Always send an `external_identifier` so you can reconcile the payment with your order.
* Rely on webhooks to confirm the payment instead of the buyer returning to your `redirect_url`: the
  buyer may close the page before the transfer is confirmed.
* Process webhooks idempotently: you may receive the same event more than once.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.