heruAPI
Retention certificates

Issue a retention certificate

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
/retention-certificates

Issues the CFDI de retenciones e información de pagos: the certificate you give every month to the merchants and workers you withheld ISR and VAT from.

Who issues it. Whoever withheld: your company, sealed with the CSD heru holds for it (see Companies), or issuer.merchant_id when a merchant withheld. heru never issues it in its own name.

Who receives it. A worker or merchant you registered (receiver.worker_id, receiver.merchant_id), one of your companies, or any taxpayer by RFC, name, regime and postal code exactly as the SAT has them.

Platforms (retention_code 26). Send one month in period and the services in platform.services. heru builds the complemento de servicios de plataformas tecnológicas and computes amounts and withheld_taxes with the SAT rules: the ISR rate of the service type and year, the VAT withheld on the VAT transferred, and cash services left out of both bases. Send them too if you want heru to check your own numbers. The retention certificates guide walks through a merchant and a courier.

Checked before stamping. Every SAT rule of the CFDI and the complement runs before the PAC sees it. A mistake answers 422 with the field and the value the SAT expects; nothing is stamped.

Synchronous. heru stamps with the PAC before answering, usually in 2 to 5 seconds, and returns the certificate with its folio fiscal; retention_certificate.stamped follows. If the PAC or the SAT do not answer in time you get 503 and nothing is created: retry after Retry-After with the same Idempotency-Key. For a month of certificates, use a batch.

Files. GET /retention-certificates/{id}/download returns a signed link to the XML or the PDF, valid for 15 minutes.

Needs the retention_certificates:write scope.

Authorization

oauth2 retention_certificates: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: retention_certificates: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

A CFDI de retenciones e información de pagos: the certificate the company that withheld gives to whoever it withheld from. With retention_code 26 send platform and heru computes the amounts and the withholdings; for any other code send amounts and withheld_taxes.

external_id*string

Your own id for this certificate, unique per issuer. A second certificate with the same external_id answers 409 EXTERNAL_ID_ALREADY_USED with the id of the first, whatever the Idempotency-Key. Echoed back in every response and webhook.

Lengthlength <= 128
receiver*||||

Who the taxes were withheld from: one of your companies, a worker, a merchant, a customer, or any taxpayer by their data.

period*

Months the withholdings cover, from 2019 and never later than the current month. With retention_code 26 the SAT takes one month only: start_month equals end_month.

retention_code*string

SAT c_CveRetenc: why the taxes were withheld. 26 services through digital platforms; 25 other (send description). Platforms use 26.

Match^(0[1-9]|1[0-9]|2[0-8])$
amounts?

Required unless retention_code is 26. With 26 heru computes them: total and taxable are the sum of the services' price_without_vat and exempt is 0.00. If you send them anyway they must match.

withheld_taxes?array<>

Required unless retention_code is 26. With 26 heru computes them from the services, the receiver and the year (see the rates in the retention certificates guide); if you send them they must match, and a mismatch answers 422 with the value the SAT expects. One entry per tax.

Items1 <= items
issuer?|

Who withheld and issues the certificate, sealed with the CSD heru holds for them (see Companies). Leave it out to issue as the company of your credential.

description?string

What the withholding is for. Required with retention_code 25.

platform?

Required with retention_code 26, and only with it. heru builds the complemento de servicios de plataformas tecnológicas from it and computes amounts and withheld_taxes as the SAT does.

place_of_issue?string

Postal code where the certificate is issued (LugarExpRetenc, required by the SAT). Defaults to the issuer's tax address.

Match^[0-9]{5}$
related_certificates?array<|>

To replace a certificate issued with errors: list it here with relationship 04 (it must be stamped and have the same receiver), then cancel it with DELETE /retention-certificates/{id}?reason=01&replacement_uuid=<uuid of this one>.

metadata?

Your own key-value data. Returned as sent.

Response body

The retention certificate, stamped by the SAT.

application/json
  1. response
id*string
Match^rtc_[0-7][0-9a-hjkmnp-tv-z]{25}$
external_id*string

Your own id, as you sent it.

status*string

stamped once the SAT accepted it; cancellation_requested while the SAT processes a cancellation; cancelled when the SAT confirmed it.

Value in"stamped""cancellation_requested""cancelled"
uuid*string

Folio fiscal the SAT assigned.

Formatuuid
verification_url*string

SAT page that verifies the certificate; tt is the total of the operation.

Formaturi
stamp*

The SAT stamp (TimbreFiscalDigital). Print it on your own representation of the CFDI.

issuer*
receiver*
place_of_issue*string

Postal code where it was issued.

period*

Months the withholdings cover.

retention_code*string
description?string|null
amounts*

Amounts of the operation the withholdings come from.

withheld_taxes*array<>
total_withheld*string

Sum of withheld_taxes.

Match^[0-9]+(\.[0-9]{1,6})?$
related_certificates*array<>

Certificates this one replaces. Empty when there are none.

platform?|

The complement, with retention_code 26. Null otherwise.

cancellation?|

Null until you request a cancellation.

metadata?
created_at*string
Formatdate-time
stamped_at*string
Formatdate-time
updated_at*string
Formatdate-time
curl -X POST 'https://api.dev.b2b.heru.app/v1/retention-certificates' \  -H 'Idempotency-Key: try-it-0001' \  -H 'Content-Type: application/json' \  -d '{  "external_id": "RET-2026-000113",  "receiver": {    "worker_id": "wrk_01m4ra8w0he5esnnsew0v9ffkv"  },  "period": {    "start_month": "09",    "end_month": "09",    "year": "2026"  },  "retention_code": "26",  "platform": {    "service_type": "02",    "periodicity": "02",    "tips": "640.00",    "period": {      "from": "2026-09-01",      "to": "2026-09-30"    },    "services": [      {        "date": "2026-09-06",        "payment_form": "03",        "price_without_vat": "2850.00",        "vat_rate": "0.16",        "commission": {          "rate": "0.10",          "amount": "285.00"        }      },      {        "date": "2026-09-13",        "payment_form": "03",        "price_without_vat": "3120.00",        "vat_rate": "0.16",        "commission": {          "rate": "0.10",          "amount": "312.00"        }      },      {        "date": "2026-09-20",        "payment_form": "03",        "price_without_vat": "2980.00",        "vat_rate": "0.16",        "commission": {          "rate": "0.10",          "amount": "298.00"        }      },      {        "date": "2026-09-27",        "payment_form": "09",        "price_without_vat": "500.00"      }    ]  }}'
{  "id": "rtc_01m4rdmjy8fr1tn1re492c0z4j",  "external_id": "RET-2026-000113",  "status": "stamped",  "uuid": "59C6F5BE-075D-4079-AD15-0B23A967A6C0",  "verification_url": "https://prodretencionverificacion.clouda.sat.gob.mx/?id=59C6F5BE-075D-4079-AD15-0B23A967A6C0&re=PDE180514R21&rr=HEGL900101K44&tt=9450.00&fe=bHhBKA==",  "stamp": {    "sat_certificate_number": "00001000000509465028",    "issuer_certificate_number": "00001000005687795825",    "pac_rfc": "SPR190613I52",    "issuer_seal": "qRXkuhx3RbYWauYlV3UAv8YYRvjAV0OeqDp8SEn2X5tYPZCbrQjOF9GvnTOUnTBr53h8COzlm4WlGNXHZnKMrSMP2KRbn3und/dzobPgK1mE27mgvpG9+prdYtgZ1vbnVecKX7PB8FiGUOJVbnGadEeyHT5GL/f+UO9IGz0Y2kho3p7UZDJS0XEp0HHfvQ3+YNCX/ysZq/KMyXBfJH7FFnTkPJ0YuzUZKEfu/oYXkCWGImuUJ8GiNS01jL4efi6KMhx6IV3FmIBGUxfm6minHYGuZjOxVh4ueD3rCvehLd2WEfYXaZjfR2HPG3XBvqzZUC1rMeNKAqMRBLcHbHhBKA==",    "sat_seal": "id2m4j4hIMaLas7/UwcOfOaio62Zqf3HcmaE3p+GKIGYsX9ufiHiWyE6skSMwahtHqoccsgNyRswrrEBThzy9y041zLKaVl0cWNKdxbDTANj/Kh6MFrWrGityN2hUf0USvdD+MJvZKvVliajw7v/Az5W7PrgQSCC6Iz68oMU6/mbcFwdXVT82IJwNxM8E/944wi3mUG1Idcov035HY+sGIpMbLPZrfWa5LF5cH57hglwXcOXajvGLe433OKJSody7tZ81lXdzXoLYb4FEHha/JbqRyJuDTcTOb3MlqUMpHOYJwranp7KQn3kq7qncDEKiQrYOT6I0ts6JET4s93W7A==",    "original_chain": "||1.1|59C6F5BE-075D-4079-AD15-0B23A967A6C0|2026-10-16T19:03:07|SPR190613I52|qRXkuhx3RbYWauYlV3UAv8YYRvjAV0OeqDp8SEn2X5tYPZCbrQjOF9GvnTOUnTBr53h8COzlm4WlGNXHZnKMrSMP2KRbn3und/dzobPgK1mE27mgvpG9+prdYtgZ1vbnVecKX7PB8FiGUOJVbnGadEeyHT5GL/f+UO9IGz0Y2kho3p7UZDJS0XEp0HHfvQ3+YNCX/ysZq/KMyXBfJH7FFnTkPJ0YuzUZKEfu/oYXkCWGImuUJ8GiNS01jL4efi6KMhx6IV3FmIBGUxfm6minHYGuZjOxVh4ueD3rCvehLd2WEfYXaZjfR2HPG3XBvqzZUC1rMeNKAqMRBLcHbHhBKA==|00001000000509465028||"  },  "issuer": {    "rfc": "PDE180514R21",    "name": "PLATAFORMA DEMO",    "tax_regime": "601",    "postal_code": "11000"  },  "receiver": {    "rfc": "HEGL900101K44",    "name": "JOSE LUIS HERNANDEZ GARCIA",    "tax_regime": "625",    "postal_code": "06600",    "email": null,    "cc": null  },  "place_of_issue": "11000",  "period": {    "start_month": "09",    "end_month": "09",    "year": "2026"  },  "retention_code": "26",  "description": null,  "amounts": {    "total": "9450.00",    "taxable": "9450.00",    "exempt": "0.00"  },  "withheld_taxes": [    {      "tax": "ISR",      "base": "9450.00",      "rate": "0.021",      "amount": "198.45",      "payment_type": "04"    },    {      "tax": "VAT",      "base": "1432.00",      "rate": "0.5",      "amount": "716.00",      "payment_type": "01"    }  ],  "total_withheld": "914.45",  "related_certificates": [],  "platform": {    "service_type": "02",    "periodicity": "02",    "isr_payment_type": "04",    "tips": "640.00",    "period": {      "from": "2026-09-01",      "to": "2026-09-30"    },    "services": [      {        "date": "2026-09-06",        "payment_form": "03",        "price_without_vat": "2850.00",        "vat_rate": "0.16",        "vat": "456.00",        "commission": {          "base": "2850.00",          "rate": "0.10",          "amount": "285.00"        },        "government_contribution": null      },      {        "date": "2026-09-13",        "payment_form": "03",        "price_without_vat": "3120.00",        "vat_rate": "0.16",        "vat": "499.20",        "commission": {          "base": "3120.00",          "rate": "0.10",          "amount": "312.00"        },        "government_contribution": null      },      {        "date": "2026-09-20",        "payment_form": "03",        "price_without_vat": "2980.00",        "vat_rate": "0.16",        "vat": "476.80",        "commission": {          "base": "2980.00",          "rate": "0.10",          "amount": "298.00"        },        "government_contribution": null      },      {        "date": "2026-09-27",        "payment_form": "09",        "price_without_vat": "500.00",        "vat_rate": null,        "vat": null,        "commission": null,        "government_contribution": null      }    ],    "totals": {      "services": 4,      "price_without_vat": "9450.00",      "vat_transferred": "1432.00",      "vat_withheld": "716.00",      "isr_withheld": "198.45",      "vat_delivered": "716.00",      "platform_fees": "895.00",      "government_contributions": "0.00"    }  },  "cancellation": null,  "metadata": {},  "created_at": "2026-10-16T19:03:07Z",  "stamped_at": "2026-10-16T19:03:07Z",  "updated_at": "2026-10-16T19:03:07Z"}
© 2026 Kuarto Inc. (heru)heru.appQuestions about your integration? Contact your heru account team.