Invoices and certificates
Why an invoice or a retention certificate was not stamped, and the worker, merchant and company checks.
link: https://developers.heru.app/errors/invoice
POST /invoices and POST /retention-certificates stamp while you wait, so every reason a document was not stamped is an error of the request itself. Nothing was created in any of them: fix what the error says and send the request again with the same external_id.
| Status | Code | When |
|---|---|---|
| 422 | The checks before stamping | heru rejected it before sending it to the SAT. |
| 422 | SAT_REJECTED | The SAT or the PAC rejected it. |
| 503 | STAMP_UNAVAILABLE | The SAT or the PAC did not answer in time. |
Before stamping
Status 422. heru checks the worker, the merchant and the company before anything reaches the SAT. The worker and merchant checks also apply to the operations that act on them: POST /workers/{id}/csd, POST /signatures and POST /charges.
{
"code": "WORKER_NOT_ELIGIBLE",
"status": 422,
"message": "The worker's RFC is suspended, so heru cannot invoice on their behalf. Check third_party_invoicing in their fiscal validation.",
"link": "https://developers.heru.app/errors/invoice",
"fields": [
{ "field": "third_party.worker_id", "reason": "not eligible for third-party invoicing" }
],
"request_id": "req_01m4ra8x59ehp870crwjcypejt"
}| Code | What happened | What to do |
|---|---|---|
COMPANY_REQUIRED | Your credential covers several companies and the request does not say which one issues. Batch operations (POST /batches, GET /batches/{id}, POST /batches/{id}/process) answer it the same way. | Send issuer.company_id, or use a credential for a single company. See Companies. |
COMPANY_CSD_NOT_ACTIVE | The CSD heru keeps for the issuing company expired or was revoked. | Send the new CSD to your account team. See Companies. In development, the companies registered for you carry a CSD of the SAT test environment. |
COMPANY_NOT_ALLOWED | issuer.company_id is not a company your credential can use. | Use one of your companies, or a credential of that company. See Companies. |
INVOICE_TYPE_NOT_AVAILABLE | The type is in the SAT catalog but heru does not issue it yet. Today only I (ingreso) is available. | Issue it as I, or wait for the type in the changelog. |
MERCHANT_NOT_ACTIVE | The merchant cannot invoice: its CSD is missing or expired. | Send the merchant a new onboarding link. |
WORKER_NO_CONSENT | The worker has not given consent on their enrollment page. | Show them enrollment.url in your app, or create a new link with POST /workers/{id}/enrollment. |
WORKER_NO_EFIRMA | Requesting a CSD or signing needs the worker's active e.firma, and they have none on file. | Ask them to upload it on their enrollment link. |
WORKER_NO_CSD | The worker is in issuer but has no valid CSD. | Generate it with POST /workers/{id}/csd, or put the worker in third_party. |
WORKER_NOT_ELIGIBLE | The worker is in third_party, but the SAT data does not allow invoicing on their behalf. | Check third_party_invoicing.reason in their fiscal validation. |
SAT rejections
Status 422, code SAT_REJECTED. The SAT or the PAC rejected the document. sat_code is the SAT validation rule that failed (CFDI40…), as the SAT publishes it, for your logs and for support; it is absent when the rejection has no SAT rule, such as an issue date older than 72 hours. fields points at the field to fix.
{
"code": "SAT_REJECTED",
"status": 422,
"message": "The receiver's name does not match the name registered with the SAT for that RFC.",
"sat_code": "CFDI40145",
"link": "https://developers.heru.app/errors/invoice",
"fields": [
{ "field": "receiver.name", "reason": "does not match the SAT records" }
],
"request_id": "req_01m4ra8x59ehp870crwjcypejt"
}| Cause | What to do |
|---|---|
| Receiver data does not match the SAT: name, postal code or tax regime. | Validate the receiver with a fiscal validation and use the values in result. |
| Wrong catalog value: product code, unit or payment form. | Fix the value with the SAT catalog. |
| Retention certificate: the receiver's RFC is not in the SAT list of active taxpayers, or its name or postal code differ. | Validate the receiver with a fiscal validation and use the values in result. |
Retention certificates: rules checked before stamping
Retention certificates go through the rules of the CFDI de retenciones 2.0 and of the complemento de servicios de plataformas tecnológicas before the PAC sees them. A broken rule answers 422 VALIDATION_FAILED; each entry of fields names the field, the value the SAT expects and the SAT rule.
{
"code": "VALIDATION_FAILED",
"status": 422,
"message": "Fix the fields listed in `fields` and retry.",
"link": "https://developers.heru.app/errors/validation",
"fields": [
{ "field": "withheld_taxes[1].base", "reason": "must be 1432.00: the VAT transferred on the services not paid in cash, not the price (SAT rule SPT116)" },
{ "field": "withheld_taxes[1].amount", "reason": "must be 716.00: base × 0.5 (SAT rule SPT117)" }
],
"request_id": "req_01m4ra8x59ehp870crwjcypejt"
}| Rule | What it checks |
|---|---|
| SPT105, SPT106 | With retention code 26, one month per certificate; the year from 2019 and not later than the year of issue. heru also rejects a month later than the current one. |
| SPT107 to SPT110 | The total is the sum of the services without VAT; taxable equals total; exempt is 0.00; the total withheld is the sum of the withholdings. |
| SPT111, SPT113 | An issuer with a 13-character RFC withholds no ISR; one entry per tax. |
| SPT115 to SPT120 | The ISR base is the price of the services not paid in cash and its rate is the one of the service type and year; the VAT base is the VAT transferred on those services and half of it is withheld; 20 % and 100 % for the generic RFC. |
| SPT126 to SPT147 | The complement: number of services, totals, service dates inside the month, VAT and commission on every service except bonuses (payment form 09), commission above zero, government contributions of MXN 1.00 or more. |
The retention certificates guide lists the rates.
Stamping unavailable
Status 503, code STAMP_UNAVAILABLE. The SAT or the PAC did not answer within the time heru waits. Nothing was stamped and no webhook follows.
{
"code": "STAMP_UNAVAILABLE",
"status": 503,
"message": "The PAC did not answer in time and the invoice was not stamped. Retry in 30 seconds with the same Idempotency-Key.",
"link": "https://developers.heru.app/errors/invoice",
"request_id": "req_01m4ra8x59ehp870crwjcypejt"
}Wait the seconds in the Retry-After header and send the same request again, with the same Idempotency-Key and external_id. If an earlier attempt did get stamped, the retry returns that CFDI instead of issuing a second one. See Idempotency and retries.