heruAPI
Point of sale

Create a charge

LiveServed by the v1 API in development with the same checks as production; production opens on the same contract. The capabilities from before v1 keep running in production at the path your account team gives you.

POST
/charges

Call it when a card payment is approved, with what was sold and its VAT. If you only know the amount, leave items out and the invoice uses the merchant's default_item. Returns a single-use link and its QR that you show on the terminal or print on the receipt. The customer opens the link, enters their tax data, we validate it with the SAT and stamp the invoice immediately, with no action from the merchant.

Needs the charges:write scope.

Authorization

oauth2 charges:write
headerAuthorizationBearer <token>

OAuth 2.0 client credentials. Exchange your client_id and client_secret at /oauth/token of the environment for a bearer token valid for one hour. The playground of every operation asks for your own client and keeps it in your browser tab only.

Scope: charges:write

Header parameters

Idempotency-Key*string

Unique per operation. Retrying with the same key returns the original result for 24 hours.

Lengthlength <= 255

Request body

application/json
  1. body
merchant_id*string

The merchant that made the sale, as returned by POST /merchants.

Match^mer_[0-7][0-9a-hjkmnp-tv-z]{25}$
external_id*string

Your transaction id.

occurred_at*string
Formatdate-time
amount*string

Total charged to the card, VAT included. When you send items, it must equal the sum of their amount; otherwise the charge is rejected with 422.

payment_form*string

04 credit card, 28 debit card. Only the card amount is invoiced; cash never goes through a charge.

Value in"04""28"
location?string

external_id of one of the merchant's locations. Its postal code becomes the place of issue.

items?array<>

What was sold. In a charge, each item's amount is the line total paid by the customer, VAT included; heru calculates the base and the VAT for the CFDI. Without items, the invoice uses the merchant's default_item for the whole amount, with its VAT rate. Send items when a sale mixes VAT rates.

expires_in_days?integer
Default30

Response body

The charge is open. Show qr on the terminal or print it on the receipt; it holds invoice_url. Keep id to get or void the charge later.

application/json
  1. response
id*string

The charge in this API: use it to get or void it.

Match^chg_[0-7][0-9a-hjkmnp-tv-z]{25}$
external_id*string

Your transaction id, as you sent it. Webhooks carry it so you can match them to your records.

status*string

open while the customer can invoice it; invoiced once they did; expired when expires_at passed unused; void after POST /charges/{id}/void; included_in_global when an expired charge went into the merchant's global invoice.

Value in"open""invoiced""expired""void""included_in_global"
merchant_id*string

The merchant that made the sale.

Match^mer_[0-7][0-9a-hjkmnp-tv-z]{25}$
amount*string

Total charged to the card, VAT included.

Match^[0-9]+\.[0-9]{2}$
payment_form*string

SAT c_FormaPago: 04 credit card, 28 debit card.

Value in"04""28"
occurred_at*string

When the card payment was approved.

Formatdate-time
location?|

external_id of the merchant's location, if you sent it.

items*array<>

What the invoice will say. If you sent no items, it holds the merchant's default_item for the whole amount.

invoice_token*|

Code inside invoice_url. Use it only if you build the link yourself, for example on your own domain. It is not the charge id. Null once the charge is no longer open.

invoice_url*|

Link the customer opens to invoice this charge: https://invoice.heru.app/{merchant slug}/c/{invoice_token}. It works once and until expires_at. Null once the charge is no longer open.

qr*|

QR with invoice_url. Null once the charge is no longer open.

expires_at*string

Last moment the customer can invoice the charge.

Formatdate-time
invoice*|

The CFDI the customer issued. Null until status is invoiced. Download its files with GET /invoices/{id}/download.

created_at*string
Formatdate-time
updated_at*string
Formatdate-time
curl -X POST 'https://api.dev.b2b.heru.app/v1/charges' \  -H 'Idempotency-Key: try-it-0001' \  -H 'Content-Type: application/json' \  -d '{  "merchant_id": "mer_01m4ra8wsyfh3s0tr4nrq3y96j",  "external_id": "TXN-0001845122",  "occurred_at": "2026-10-11T13:42:10-06:00",  "amount": "250.00",  "payment_form": "04",  "location": "SUC-01",  "items": [    {      "product_code": "90101500",      "description": "Chilaquiles verdes con pollo",      "amount": "165.00",      "quantity": 1,      "unit_code": "E48",      "vat_rate": "0.16"    },    {      "product_code": "90101500",      "description": "Café americano",      "amount": "85.00",      "quantity": 2,      "unit_code": "E48",      "vat_rate": "0.16"    }  ]}'
{  "id": "chg_01m4p7sbvpev1s5086cty36rvs",  "external_id": "TXN-0001845122",  "status": "open",  "merchant_id": "mer_01m4ra8wsyfh3s0tr4nrq3y96j",  "amount": "250.00",  "payment_form": "04",  "occurred_at": "2026-10-11T13:42:10-06:00",  "location": "SUC-01",  "items": [    {      "product_code": "90101500",      "description": "Chilaquiles verdes con pollo",      "amount": "165.00",      "quantity": 1,      "unit_code": "E48",      "vat_rate": "0.16"    },    {      "product_code": "90101500",      "description": "Café americano",      "amount": "85.00",      "quantity": 2,      "unit_code": "E48",      "vat_rate": "0.16"    }  ],  "invoice_token": "9fK2wQ7xLm4TbR8vZp3N",  "invoice_url": "https://invoice.heru.app/cafeteria-la-esquina/c/9fK2wQ7xLm4TbR8vZp3N",  "qr": {    "format": "png_base64",    "data": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAABlBMVEX///8AAABVwtN+…"  },  "expires_at": "2026-11-10T23:59:59-06:00",  "invoice": null,  "created_at": "2026-10-11T19:42:11.318Z",  "updated_at": "2026-10-11T19:42:11.318Z"}
© 2026 Kuarto Inc. (heru)heru.appQuestions about your integration? Contact your heru account team.