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.
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.
oauth2 retention_certificates:writeAuthorizationBearer <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
Idempotency-Key*stringUnique per operation. Retrying with the same key returns the original result for 24 hours.
length <= 255application/json- 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*stringYour 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.
length <= 128receiver*||||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*stringSAT c_CveRetenc: why the taxes were withheld. 26 services through digital platforms; 25 other (send description). Platforms use 26.
^(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.
1 <= itemsissuer?|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?stringWhat 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?stringPostal code where the certificate is issued (LugarExpRetenc, required by the SAT). Defaults to the issuer's tax address.
^[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.
The retention certificate, stamped by the SAT.
application/json- response
id*string^rtc_[0-7][0-9a-hjkmnp-tv-z]{25}$external_id*stringYour own id, as you sent it.
status*stringstamped once the SAT accepted it; cancellation_requested while the SAT processes a cancellation; cancelled when the SAT confirmed it.
"stamped""cancellation_requested""cancelled"uuid*stringFolio fiscal the SAT assigned.
uuidverification_url*stringSAT page that verifies the certificate; tt is the total of the operation.
uristamp*The SAT stamp (TimbreFiscalDigital). Print it on your own representation of the CFDI.
issuer*receiver*place_of_issue*stringPostal code where it was issued.
period*Months the withholdings cover.
retention_code*stringdescription?string|nullamounts*Amounts of the operation the withholdings come from.
withheld_taxes*array<>total_withheld*stringSum of withheld_taxes.
^[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*stringdate-timestamped_at*stringdate-timeupdated_at*stringdate-timecurl -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"}Get a download linkGET
Returns a signed link to the stamped invoice on `download.heru.app`, valid for 15 minutes. `format=xml` is the CFDI XML as the SAT stored it, the document with fiscal validity; `format=pdf` is a printable PDF with the SAT seal and the QR to verify it. Needs the `invoices:download` scope.
Cancel a retention certificateDEL
Asks the SAT to cancel the certificate. Retention certificates need no acceptance from the receiver: the answer is `202` with status `cancellation_requested`, the SAT confirms within seconds and `retention_certificate.cancelled` fires with status `cancelled`. To correct a certificate, issue the new one first with the old one in `related_certificates` (relationship 04), then cancel the old one with reason `01` and the new folio fiscal in `replacement_uuid`. heru checks that the replacement is stamped and relates this one. The cancelled certificate is not deleted: it keeps its files and its `cancellation`. Needs the `retention_certificates:cancel` scope.