Errors

This guide describes how API errors are classified, what each status code means, and how to read the most common error responses.

Error types

Errors are classified in two levels. If a level 1 validation fails, the API stops the request and returns 400 (Bad Request) with the details of the failures, without running the level 2 validations.

1. Level 1 errors

These are request validation failures, which the API checks before it processes the request. Each failure gives the property that failed (propertyName) and a message that depends on the rule applied. Use the property, not the message text, to identify the failure in your code (see ValidationFailure).

Messages are in Spanish; for example, 'transpInternacId' no debe estar vacío. ('transpInternacId' must not be empty.) Some rule-specific messages and the JSON parsing errors, such as malformed JSON or a value of the wrong type, are in English. For the rule-specific ones, see Stamping validation messages (stamping: the certification by a SAT-authorized provider that makes a CFDI valid).

2. Level 2 errors

These are the checks the SAT (Mexico's tax authority, Servicio de Administración Tributaria) requires for a CFDI (Mexico's electronic invoice) to comply with version 4.0. Their messages are in Spanish and explain the cause according to the SAT specifications. See the CFDI 4.0 error table for the full list.


Status codes

The status code category tells you whether the request succeeded. The response body follows the Fiscalapi response model.

  • Name
    2xx
    Description

    The request succeeded.

  • Name
    4xx
    Description

    Client error: there's a problem with the request.

  • Name
    5xx
    Description

    Server error. It's rare; see Server errors.


Resource not found

When an endpoint looks up a resource by its ID and the resource doesn't exist in your tenant (the space in Fiscalapi that holds your issuers, recipients, credits, and data), it returns 404. The ID can go in the path, as in Get an invoice by ID, or in the body, as invoiceId in Generate a PDF by ID. Endpoints that have their own message for this case say so in their section, such as tax files (the CSD and e.firma .cer and .key files you upload for a person).

In the messages, <id> is the ID you sent and <catalogName> is the catalog name in the path, which is case-sensitive: satpaymentforms isn't SatPaymentForms.

StatusWhen it happensmessagedetails
404The resource doesn't exist in your tenant.Sorry, it's not me, it's you.No such resource for the given Id: <id>
404The key doesn't exist in the catalog, in Get a record by ID.Sorry, it's not me, it's you.No such resource for the given Id: <id>
404The catalog name in the path doesn't exist.Sorry, it's not me, it's you.No existe el catálogo '<catalogName>'.

Fiscalapi checks the catalog name on every route that includes it: GET /api/v4/catalogs/<catalogName>, Get a record by ID, Search a catalog, GET /api/v4/catalogs/<catalogName>/load, and List a catalog for the bulk download (the SAT service that delivers the CFDIs a taxpayer issued and received) catalogs. The valid names are the ones that List catalogs and List bulk download catalogs return.


SAT catalog keys

When a field carries a key that isn't in its SAT catalog, the endpoint returns 400 with a validation failure on that field. The message, in Spanish, includes the value received and the catalog name, without the list of keys.

The rule applies to invoices (POST /api/v4/invoices) and to their cancellation, which rejects the cancellation reason (one of the SAT keys 01 to 04) without calling the PAC (Proveedor Autorizado de Certificación, a SAT-authorized provider that stamps CFDIs). It also applies to CFDI products, to people (person: any individual or legal entity registered in your tenant), both issuers and recipients, and to employee and employer data. Look up the valid keys with the SAT catalogs API.

FieldMessage
General form<El dato> '<valor>' no existe en el catálogo <c_Catálogo> del SAT. (<The field> '<value>' doesn't exist in the SAT catalog <c_Catalog>.)
PaymentFormCode (example)La forma de pago '98' no existe en el catálogo c_FormaPago del SAT. (The form of payment '98' doesn't exist in the SAT catalog c_FormaPago.)
CancellationReasonCodeEl motivo de cancelación '<valor>' no existe en el catálogo c_MotivoCancelacion del SAT. (The cancellation reason '<value>' doesn't exist in the SAT catalog c_MotivoCancelacion.)

Each field returns a single failure: in required fields with this rule (for example, versionCode, typeCode, taxes, or payroll keys), an empty or missing value returns only the empty field message. The following fields have their own message, different from the general form:

FieldMessage
versionCodeLa versión de CFDI '<valor>' no es válida: usa 4.0. (The CFDI version '<value>' isn't valid: use 4.0.)
taxFlagCode of the line item taxes, the paid invoice taxes, and local taxes (complement.localTaxes); taxFlagId in productsLa naturaleza del impuesto '<valor>' no es válida: usa T (traslado) o R (retención). (The tax flag '<value>' isn't valid: use T (transferred tax) or R (withholding).)
expeditionZipCode, recipient.zipCode, and the postal code of the third party on whose behalf the invoice is issuedEl código postal '<valor>' debe tener 5 caracteres (catálogo c_CodigoPostal del SAT). (The postal code '<value>' must have 5 characters (SAT catalog c_CodigoPostal).)
recipient.zipCodeEl código postal '<valor>' debe tener 5 dígitos (catálogo c_CodigoPostal del SAT). (The postal code '<value>' must have 5 digits (SAT catalog c_CodigoPostal).)
recipient.zipCode, if missingEl código postal es obligatorio. (The postal code is required.)
itemCode of the line item and its partsLa clave de producto o servicio '<valor>' debe tener 8 caracteres (catálogo c_ClaveProdServ del SAT). (The product or service key '<value>' must have 8 characters (SAT catalog c_ClaveProdServ).)
currencyCode of a payment CFDI (type P)La moneda de un comprobante de pago debe ser XXX. (A payment CFDI's currency must be XXX.)
Currency of the payment and of each paid invoice (complement.payment) set to XXXLa moneda de un pago o de un documento pagado no puede ser XXX. (The currency of a payment or of a paid invoice can't be XXX.)
Payroll 1.2 catalogs: payroll type, earnings (percepciones: salary, overtime, bonuses, and other pay), overtime, other payment, deduction, and work incapacity; in employee and employer data, contract type, hiring regime, pay period, Mexican state, occupational risk class, bank, workday type, and source of funds. Only current keys are accepted.La clave '<valor>' no existe en el catálogo <c_Catálogo> del SAT o ya no está vigente. (The key '<value>' doesn't exist in the SAT catalog <c_Catalog> or is no longer current.)
Unionized status (Sindicalizado, satUnionizedStatusId)El valor '<valor>' de Sindicalizado no es válido: usa Sí o No. (The value '<value>' of Sindicalizado isn't valid: use Sí or No.)

Values that don't fit in the invoice

When you stamp (POST /api/v4/invoices) and when you calculate (POST /api/v4/invoices/calculate), Fiscalapi builds the invoice it would save and checks that each value fits in it. Stamping here covers an invoice by value (you send every issuer, recipient, and item value in the request) or by reference (you send the IDs of people and products already saved in Fiscalapi), of any CFDI type (the TipoDeComprobante key: I income, the regular sales invoice; E credit note; T transfer, moves goods without a sale; N payroll; P payment), with any complement (complemento, an XML extension the SAT defines for specific operations). A text can't be longer than its maximum, and an amount or a quantity, rounded to its decimal places, can't have more integer digits than allowed. For example, the line item quantity and the payment amount allow 12: values less than 1000000000000.

If a value doesn't fit, the endpoint returns 400 with a validation failure for each value, without stamping and without using a stamp (one credit, used each time you stamp a CFDI). When you stamp, the check happens before Fiscalapi deducts the stamp and calls the PAC; calculating never stamps or uses a stamp.

ValueerrorMessageattemptedValue
TextAdmite como máximo <n> caracteres y se recibieron <m>. (Allows at most <n> characters, and <m> were received.)null (the text may be personal data)
Amount or quantityAdmite como máximo <d> dígitos enteros (valores menores que <10^d>); se recibió <valor>. (Allows at most <d> integer digits (values less than <10^d>); <value> was received.)The number received

In this failure, propertyName is the path of the value in the saved invoice, not the path of the request field:

Request fieldpropertyName
items[0].quantityInvoiceItems[0].Quantity
complement.payment.amountInvoicePayment.Amount
complement.payroll.deductions[0].codePayroll.Deductions[0].Code

Other possible paths include, for example:

ValuepropertyName
Paid invoiceInvoicePayment.PaidInvoices[0].Number
Payroll earnings itemPayroll.Earnings.Earnings[0].Concept
Employee dataInvoiceRecipient.EmployeeData.Curp
Employer dataInvoiceIssuer.EmployerData.EmployerRegistration
Carta Porte (the CFDI complement that documents the transport of goods in Mexico)CartaPorte.Mercancias[0].ClaveUnidad
Comercio Exterior (the CFDI foreign trade complement for exports)ComercioExterior.Emisor.Domicilio.Colonia

Rules specific to the request fields (for example, a recipient email of up to 256 characters or an itemSku of up to 100) are checked first, and their failure is on the request field.


Decimal places in amounts, quantities, and rates

When you stamp (POST /api/v4/invoices, by value or by reference) and when you calculate (POST /api/v4/invoices/calculate), the amounts, quantities, and rates that go into the XML as is allow at most the decimal places the SAT defines for their attribute (the XSD of CFDI 4.0 and its complements). A value with more decimal places returns 400 with a validation failure for each value, before Fiscalapi deducts the stamp and calls the PAC:

propertyNameerrorMessageattemptedValue
The path of the request field, in PascalCase (for example, Items[0].Discount)'<campo>' admite como máximo <n> decimales (se recibieron <m>): redondéalo antes de enviarlo; FiscalAPI no lo redondea. ('<field>' allows at most <n> decimal places (<m> were received): round it before you send it; Fiscalapi doesn't round it.)The number received
Example: a discount calculated in JavaScript as 0.1 + 0.2, in Items[0].Discount'discount' admite como máximo 6 decimales (se recibieron 17): redondéalo antes de enviarlo; FiscalAPI no lo redondea. ('discount' allows at most 6 decimal places (17 were received): round it before you send it; Fiscalapi doesn't round it.)0.30000000000000004

Fiscalapi doesn't round the value: round it in your system to the right number of decimal places before you send it. For example, to 6 decimal places:

LanguageRounding
JavaScriptNumber(x.toFixed(6))
PHPround($x, 6)
PythonDecimal.quantize
JavasetScale(6, RoundingMode.HALF_UP)
.NETMath.Round(x, 6)

Only significant decimal places count, not trailing zeros. For example, 0.16000000 in taxRate and an exchange rate of 1.0000000 with the MXN currency are accepted. The limits, by XML attribute, are:

FieldsDecimal placesSAT attribute
items[n].quantity and items[n].parts[n].quantity6Cantidad
items[n].unitPrice, items[n].discount, and items[n].parts[n].unitPrice6ValorUnitario and Descuento (t_Importe)
items[n].itemTaxes[n].taxRate6TasaOCuota
exchangeRate6TipoCambio
complement.payment.exchangeRate6TipoCambioP
complement.payment.amount6Monto
complement.payment.paidInvoices[n].previousBalance, paymentAmount, and remainingBalance6ImpSaldoAnt, ImpPagado, and ImpSaldoInsoluto
complement.payment.paidInvoices[n].subTotal and paidInvoiceTaxes[n].base6BaseDR
complement.payment.paidInvoices[n].paidInvoiceTaxes[n].taxRate6TasaOCuotaDR
complement.payment.paidInvoices[n].equivalence10EquivalenciaDR
complement.payroll.daysPaid3NumDiasPagados (see the note below)
complement.payroll.earnings.earnings[n].taxedAmount and exemptAmount2ImporteGravado and ImporteExento (t_ImporteMXN)
complement.payroll.earnings.earnings[n].stockOptions.marketPrice and grantPrice6ValorMercado and PrecioAlOtorgarse
complement.payroll.earnings.earnings[n].overtime[n].amountPaid2ImportePagado
complement.payroll.earnings.otherPayments[n].subsidyCaused, and balanceCompensation.favorableBalance and remainingFavorableBalance2SubsidioCausado, SaldoAFavor, and RemanenteSalFav
complement.payroll.earnings.retirement.totalOneTime, totalInstallments, and dailyAmount2JubilacionPensionRetiro attributes
complement.payroll.earnings.retirement.accumulableIncome and nonAccumulableIncome2JubilacionPensionRetiro attributes
complement.payroll.earnings.severance.totalPaid and lastMonthlySalary2SeparacionIndemnizacion attributes
complement.payroll.earnings.severance.accumulableIncome and nonAccumulableIncome2SeparacionIndemnizacion attributes
complement.payroll.deductions[n].amount2Importe of the deduction
complement.payroll.disabilities[n].monetaryAmount2ImporteMonetario
recipient.employeeData.baseSalaryForContributions and integratedDailySalary2SalarioBaseCotApor and SalarioDiarioIntegrado
recipient.employeeData.timePercentage3PorcentajeTiempo
issuer.employerData.ownResourceAmount2MontoRecursoPropio

The rule applies to the field even when, in a specific invoice, its value doesn't reach the XML (for example, totalInstallments and dailyAmount together with totalOneTime). It doesn't apply to the fields that Fiscalapi rounds when it builds the XML (the amount of other payroll payments, local taxes, and Comercio Exterior amounts), or to the Carta Porte and Comercio Exterior fields, which validate their decimal places with their own message.

The daysPaid field (the days paid in the payroll) accepts an integer or up to 3 decimal places. Fiscalapi writes it to the XML as the SAT requires: as an integer, or with exactly 3 decimal places.

Value sentResult
15.5It's stamped as 15.500.
15.0It's stamped as 15.
15.2083333Returns the decimal places failure: it has more than 3 significant decimal places.

Fiscalapi writes some numbers with its own scale, such as the rate or fixed amount and the exchange rate with MXN, so extra zeros in those fields don't reach the XML. The PAC rejects an amount that goes into the XML as is with extra zeros (for example, 200.0000000 in unitPrice) with CFDI40999: send at most the attribute's decimal places.


Stamping validation messages

When you stamp (POST /api/v4/invoices, by value or by reference) and when you calculate (POST /api/v4/invoices/calculate), validation messages are in Spanish and name the field as the API does: in camelCase, the same as in the JSON. The other endpoints name the field with the property name split into words.

The failure field (propertyName) is the path of the value in the request, in PascalCase (for example, Items[0].ItemTaxes[0].TaxCode). Use it, not the text, to identify the failure (see ValidationFailure).

CaseMessage (example)
Empty field, when stamping'taxCode' no debería estar vacío. ('taxCode' shouldn't be empty.)
Value out of range, when stamping'quantity' debe ser mayor que '0'. ('quantity' must be greater than '0'.)
Required field missing inside a complement'code' no debe estar vacío. ('code' must not be empty.)
Empty field, in the other endpoints'Cancellation Reason Code' no debería estar vacío. ('Cancellation Reason Code' shouldn't be empty.)

These stamping rules have their own message. In the messages, <campo> (<field>) is the field name as the API writes it; for example, 'date'. <régimen> (<regime>) and <uso> (<use>) are the recipient's keys, and the person type, legal entity (persona moral) or individual (persona física), is the one that matches the length of the recipient's RFC (Mexico's taxpayer ID, Registro Federal de Contribuyentes).

Field (propertyName)Message
Issuer.Tin, Recipient.Tin'tin' no tiene el formato de RFC del SAT: 3 letras (persona moral) o 4 (persona física), la fecha AAMMDD y 3 caracteres de homoclave. ('tin' doesn't have the SAT RFC format: 3 letters (legal entity) or 4 (individual), the date as YYMMDD, and 3 homoclave characters.)
Number'number' (folio) debe tener de 1 a 40 caracteres sin el carácter '|'; si se omite (o va null o vacío), se genera automáticamente. ('number' (folio number) must have 1 to 40 characters without the '|' character; if you omit it (or send it null or empty), it's generated automatically.)
Date, Complement.Payment.PaymentDate, and Complement.CartaPorte.Ubicaciones[n].FechaHoraSalidaLlegada'<campo>' es obligatorio y debe ser una fecha con el formato yyyy-MM-ddTHH:mm:ss. ('<field>' is required and must be a date in the yyyy-MM-ddTHH:mm:ss format.)
Complement.Payroll.PaymentDate, InitialPaymentDate, and FinalPaymentDate'<campo>' es obligatorio y debe ser una fecha con el formato yyyy-MM-dd. ('<field>' is required and must be a date in the yyyy-MM-dd format.)
Items (invoices of type I, E, and T)items debe tener al menos un concepto en los comprobantes de tipo I, E y T. (items must have at least one line item in CFDIs of type I, E, and T.)
Items[n].Description'description' debe tener de 1 a 1000 caracteres y no puede contener el carácter '|'. ('description' must have 1 to 1000 characters and can't contain the '|' character.)
TaxRate of the line item taxes and the paid invoice taxes'taxRate' (tasa o cuota) debe ser mayor o igual que 0. ('taxRate' (rate or fixed amount) must be greater than or equal to 0.)
TaxTypeCode of a withholding (retención)Una retención (taxFlagCode R) no puede tener 'taxTypeCode' Exento. (A withholding (taxFlagCode R) can't have 'taxTypeCode' Exento.)
ExchangeRate and Complement.Payment.ExchangeRate, with the MXN currencyCon la moneda MXN, 'exchangeRate' (tipo de cambio) debe ser 1. (With the MXN currency, 'exchangeRate' (exchange rate) must be 1.)
ExchangeRate and Complement.Payment.ExchangeRate, with another currencyCon una moneda distinta de MXN, 'exchangeRate' (tipo de cambio) debe ser el del día de la operación (mayor que 0). (With a currency other than MXN, 'exchangeRate' (exchange rate) must be the rate for the transaction date (greater than 0).)
ExchangeRate and Complement.Payment.ExchangeRate, with the XXX currencyCon la moneda XXX, 'exchangeRate' (tipo de cambio) no aplica: se omite (o se envía 1 o 0). (With the XXX currency, 'exchangeRate' (exchange rate) doesn't apply: omit it (or send 1 or 0).)
Complement (the message depends on the invoice type; for example, in a payment)Un comprobante de tipo P (pago) requiere complement.payment y no admite otro complemento. (A CFDI of type P (payment) requires complement.payment and doesn't accept any other complement.)
Complement (for example, in an income invoice)Un comprobante de tipo I (ingreso) no lleva complement o solo lleva complement.localTaxes, complement.cartaPorte o complement.comercioExterior. (A CFDI of type I (income) has no complement, or has only complement.localTaxes, complement.cartaPorte, or complement.comercioExterior.)
GlobalInformation.Year'year' debe ser el año de la fecha del comprobante (<año>) o uno de los 5 anteriores. ('year' must be the year of the CFDI date (<year>) or one of the 5 previous years.)
RelatedInvoicesTodos los 'relatedInvoices' deben tener el mismo relationshipTypeCode. (All 'relatedInvoices' must have the same relationshipTypeCode.)
Recipient.TaxRegimeCode with the generic RFCCon el RFC genérico (XAXX010101000 o XEXX010101000) el régimen fiscal del receptor (taxRegimeCode) debe ser 616 (CFDI40159). (With the generic RFC (XAXX010101000 or XEXX010101000), the recipient's tax regime (taxRegimeCode) must be 616 (CFDI40159).)
Recipient.ZipCode with the generic RFCCon el RFC genérico (XAXX010101000 o XEXX010101000) el código postal del receptor (zipCode) debe ser el lugar de expedición (expeditionZipCode) (CFDI40149). (With the generic RFC (XAXX010101000 or XEXX010101000), the recipient's postal code (zipCode) must be the place of issue (expeditionZipCode) (CFDI40149).)
Recipient.TaxRegimeCode by person typeEl régimen fiscal '<régimen>' del receptor (taxRegimeCode) no aplica a una persona moral (RFC de 12 caracteres) según el catálogo c_RegimenFiscal (CFDI40158). (The recipient's tax regime '<regime>' (taxRegimeCode) doesn't apply to a legal entity (12-character RFC) according to the c_RegimenFiscal catalog (CFDI40158).)
Recipient.CfdiUseCode by person typeEl uso de CFDI '<uso>' del receptor (cfdiUseCode) no aplica a una persona física (RFC de 13 caracteres) según el catálogo c_UsoCFDI (CFDI40161). (The recipient's CFDI use '<use>' (cfdiUseCode) doesn't apply to an individual (13-character RFC) according to the c_UsoCFDI catalog (CFDI40161).)
Recipient.CfdiUseCode by tax regimeEl uso de CFDI '<uso>' del receptor (cfdiUseCode) no admite el régimen fiscal '<régimen>' (taxRegimeCode) según el catálogo c_UsoCFDI (CFDI40161). (The recipient's CFDI use '<use>' (cfdiUseCode) doesn't allow the tax regime '<regime>' (taxRegimeCode) according to the c_UsoCFDI catalog (CFDI40161).)
GlobalInformation in an income invoice to XAXX010101000Un ingreso a XAXX010101000 'PUBLICO EN GENERAL' es una factura global: globalInformation es obligatorio (CFDI40130). (An income CFDI to XAXX010101000 'PUBLICO EN GENERAL' is a global invoice: globalInformation is required (CFDI40130).)
Recipient.ForeignTin'foreignTin' (NumRegIdTrib) debe tener de 1 a 40 caracteres. ('foreignTin' (NumRegIdTrib) must have 1 to 40 characters.)
Recipient.Email'email' no es un correo electrónico válido. ('email' isn't a valid email address.)
Issuer.TaxCredentials (and TaxCredentials in the cancellation by value)Debes proporcionar exactamente 2 credenciales fiscales (sellos SAT): una de fileType=0 (CertificateCsd) y otra de fileType=1 (PrivateKeyCsd). (You must provide exactly 2 tax credentials (the CSD files): one with fileType=0 (CertificateCsd) and another with fileType=1 (PrivateKeyCsd).)
Each credential in Issuer.TaxCredentials or TaxCredentials'fileType' '<valor>' no es un tipo de archivo válido. ('fileType' '<value>' isn't a valid file type.)
Each credential in Issuer.TaxCredentials or TaxCredentials'password' es obligatorio para la llave privada. ('password' is required for the private key.)
Paid invoice (Complement.Payment.PaidInvoices[n])'equivalence' debe ser 1 cuando la moneda del documento pagado es la del pago. ('equivalence' must be 1 when the paid invoice's currency is the payment's currency.)
Paid invoice (Complement.Payment.PaidInvoices[n])'subTotal' debe ser mayor que 0 cuando un impuesto del documento pagado tiene base 0 (base 0 toma el subTotal). ('subTotal' must be greater than 0 when a tax of the paid invoice has base 0 (base 0 takes the subTotal).)
Base of the paid invoice taxes'base' debe ser mayor o igual que 0 (0 toma el subTotal del documento pagado). ('base' must be greater than or equal to 0 (0 takes the paid invoice's subTotal).)
Base of the paid invoice taxes, with taxTypeCode CuotaUn impuesto con taxTypeCode Cuota requiere 'base' (unidades): el subTotal del documento pagado es un importe. (A tax with taxTypeCode Cuota requires 'base' (units): the paid invoice's subTotal is an amount.)
Local taxes (Complement.LocalTaxes)'taxes' requiere al menos un impuesto local. ('taxes' requires at least one local tax.)
Local taxes (Complement.LocalTaxes)'taxName' (nombre del impuesto local) es obligatorio; p. ej., ISN o CEDULAR. ('taxName' (local tax name) is required; for example, ISN or CEDULAR.)
Local taxes (Complement.LocalTaxes)'taxRate' debe ser mayor que 0; p. ej., 0.030000 para 3 %. ('taxRate' must be greater than 0; for example, 0.030000 for 3%.)
taxFlagCode of local taxesThe text in SAT catalog keys.
Carta Porte (Complement.CartaPorte)'ubicaciones' requiere al menos 2 ubicaciones. ('ubicaciones' requires at least 2 locations.)
Carta Porte (Complement.CartaPorte)Se permiten como máximo 2 remolques. (At most 2 trailers are allowed.)
Comercio Exterior (Complement.ComercioExterior)'certificadoOrigen' debe ser 0 (no funge) o 1 (funge como certificado de origen). ('certificadoOrigen' must be 0 (doesn't act as one) or 1 (acts as a certificate of origin).)
Comercio Exterior (Complement.ComercioExterior)'mercancias' requiere al menos una mercancía. ('mercancias' requires at least one item of goods.)
Comercio Exterior (Complement.ComercioExterior)'domicilios' del destinatario requiere al menos un domicilio. (The addressee's 'domicilios' requires at least one address.)

An invoice with more than 1,500 line items returns 400 without a validation failure, both when you stamp and when you calculate:

StatusWhen it happensdetails
400The invoice has more than 1,500 line items.Una factura admite como máximo 1500 conceptos (items).

The Carta Porte and Comercio Exterior catalog messages, the certificate file messages, and the JSON parser messages (malformed JSON or a value of the wrong type) are in English. For example:

SourceMessage
Carta Porte and Comercio Exterior catalogs<Field> must be a valid c_<Catalog> value.
Carta Porte and Comercio Exterior catalogs (example)NumEstacionId must be a valid c_Estaciones value.
Certificate files (example)The certificate (.cer) belongs to RFC <rfc>, but the RFC of <person> is <rfc>. Use the certificate issued to that RFC.

Null elements in lists

Each element of a list of objects in the body must be an object. A null element, as in "relatedInvoices": [null] or "productTaxes": [{ ... }, null], returns 400 with a validation failure for each null element:

Failure fieldValue
propertyNameThe path of the element, with its zero-based position; for example, Items[1] or Complement.Payroll.Earnings.Earnings[0].
errorMessage'<campo>' no admite elementos null: cada elemento debe ser un objeto. ('<field>' doesn't accept null elements: each element must be an object.)
attemptedValuenull

This check runs before the other validations and before Fiscalapi calls the PAC: the response includes only the null element failures, without stamping or using a stamp. If the body also can't be read (for example, a value of the wrong type), the response includes only the parsing failure.

When you stamp and when you calculate, the message names the field as the API does, in camelCase. The other endpoints name it with the property name split into words.

EndpointListspropertyNameMessage (example)
Stamping (POST /api/v4/invoices, by value and by reference) and calculation (POST /api/v4/invoices/calculate)Every list of objects in the invoice and its complements: for example, line items, line item taxes, related CFDIs (CFDIs this one replaces, credits, or pays, with their TipoRelacion key), issuer tax credentials, paid invoices, payroll earnings and deductions, Carta Porte locations, or Comercio Exterior goodsRelatedInvoices[0]'relatedInvoices' no admite elementos null: cada elemento debe ser un objeto. ('relatedInvoices' doesn't accept null elements: each element must be an object.)
Create a product and Update a productproductTaxesProductTaxes[n]'Product Taxes' no admite elementos null: cada elemento debe ser un objeto. ('Product Taxes' doesn't accept null elements: each element must be an object.)
Cancel an invoice, by value and by referencetaxCredentialsTaxCredentials[n]'Tax Credentials' no admite elementos null: cada elemento debe ser un objeto. ('Tax Credentials' doesn't accept null elements: each element must be an object.)

A null element returns only this failure, at its position, also in the lists that have their own rule: taxCredentials (the certificate pair and the empty list), paidInvoices and paidInvoiceTaxes (the empty list), and the Carta Porte locations (minimum 2).

Lists of strings (for example, validationTypes in SAT validations) keep their own rule. The metadata field is a dictionary, not a list: it accepts null values.


Certificates and private keys

Endpoints that receive a private key (.key file) with its password open the key before they use it. If the password doesn't open the key, they return 400 with a validation failure on the password field. The same failure occurs if the file sent as the key isn't a private key: reading the key doesn't tell the two cases apart. The check happens before Fiscalapi calls the PAC and without using a stamp.

Failure fieldValue
errorMessageLa contraseña de la llave privada (.key) es incorrecta o el archivo no es una llave privada. (The private key (.key) password is incorrect or the file isn't a private key.)
attemptedValuenull: the password isn't returned.
propertyNameIt depends on the endpoint (see the next table).
EndpointpropertyName
Stamping with the issuer by value (issuer.taxCredentials)Issuer.TaxCredentials[<n>].Password, with the position of the key in the list (for example, Issuer.TaxCredentials[1].Password)
Upload tax files with a CSD (Certificado de Sello Digital, the digital seal certificate used to sign CFDIs) private key (fileType 1)Password
Upload tax files with an e.firma (the SAT advanced electronic signature that identifies a taxpayer, formerly FIEL) private key (fileType 3)Password
Sign the Carta Manifiesto (Carta Manifiesto: the letter in which a taxpayer authorizes its PAC to certify its CFDIs)Password

The cancellation by value (with taxCredentials) doesn't open the key: it sends the key to the PAC, which returns its own error if the password doesn't match.

Other certificate and key errors:

StatusWhen it happensmessagedetails
400A certificate (.cer) or a private key (.key) received can't be read, in a flow without the previous check; for example, an unreadable .cer when you sign the Carta Manifiesto.Sorry, it's not me, it's you.No se pudo leer el certificado (.cer) o la llave privada (.key).
500Fiscalapi can't decrypt a protected value it stores; for example, the password of a person's certificates when you stamp or cancel by reference, or when you get the default tax files.Sorry, it's not you, it's me.No pudimos descifrar un dato protegido del servidor. Escríbenos a soporte@fiscalapi.com.

The 500 is a server error, not a request error: repeating the request with other data doesn't fix it, so contact support.


Server errors

Changing the request data doesn't fix a server error (5xx). The response includes traceIdentifier (see ApiResponse): include it when you contact support, because it identifies the request in Fiscalapi's logs. The message field tells you which side the cause is on:

StatusmessageWhat to do
4xxSorry, it's not me, it's you.Fix the request.
5xxSorry, it's not you, it's me.Retry or contact support with the traceIdentifier.
500 (unexpected error)Something went wrong on our end.Contact support with the traceIdentifier.

The PAC rejection when you stamp or cancel, the 425 (Too Early) of Download a package by ID, and the 429 (Too Many Requests) of the rate limit keep their own message. Server errors respond as follows:

StatusWhen it happensmessagedetails
500Unexpected error. The technical detail stays only in Fiscalapi's logs, with the same traceIdentifier.Something went wrong on our end.Ocurrió un error inesperado en el servidor. Escríbenos a soporte@fiscalapi.com con el traceIdentifier de esta respuesta.
500An error with its own text, such as the protected value that Fiscalapi can't decrypt (see Certificates and private keys).Sorry, it's not you, it's me.The error's own text.
502An operation that calls Stripe, Fiscalapi's payment provider (purchases and the subscription from the dashboard), when Stripe fails or isn't available.Sorry, it's not you, it's me.No pudimos completar la operación con Stripe. Intenta de nuevo en unos minutos; si continúa, escríbenos a soporte@fiscalapi.com con el traceIdentifier de esta respuesta.
502Create a request for downloads, when the SAT couldn't create the request.Sorry, it's not you, it's me.The error's own text.
503Fiscalapi's database didn't respond in time or isn't available. Repeat the request later.Sorry, it's not you, it's me.La base de datos no respondió a tiempo o no está disponible. Intenta de nuevo en unos minutos; si continúa, escríbenos a soporte@fiscalapi.com con el traceIdentifier de esta respuesta.
503Email an invoice, when the email provider isn't available.Sorry, it's not you, it's me.The error's own text.

Example (unexpected error):

{
  "data": null,
  "succeeded": false,
  "message": "Something went wrong on our end.",
  "details": "Ocurrió un error inesperado en el servidor. Escríbenos a soporte@fiscalapi.com con el traceIdentifier de esta respuesta.",
  "httpStatusCode": 500,
  "traceIdentifier": "5C1E0B7A92D44F3E8A6B1D07F3C29E41"
}

Uncertain stamping result

When you stamp, if the PAC doesn't respond in time or returns a server error without a rejection code, the result is uncertain: the CFDI may have been stamped. Fiscalapi returns the unexpected error (500) and keeps the stamp used and the invoice's folio number (the issuer's own sequential number for the CFDI; not the UUID) to reconcile them; it doesn't refund them.

Don't resend the invoice right away, because you could stamp the same transaction twice. Check your invoices with List invoices and, if the invoice isn't there, contact support with the traceIdentifier of the response. A PAC rejection with its code is a rejection: Fiscalapi refunds the stamp, and the response includes the PAC's message.

Data constraints and concurrency

When it writes the data, Fiscalapi's database also rejects what the request caused and a field validation didn't catch earlier. These responses are client errors and include no validation failure or field: the reason is in details, in Spanish.

StatusWhen it happensmessagedetails
400A record with a value that must be unique already exists.Sorry, it's not me, it's you.Ya existe un registro con los mismos datos únicos.
400A related record doesn't exist or depends on the one being deleted, or a value doesn't meet a database rule.Sorry, it's not me, it's you.La operación no cumple una restricción de los datos: un registro relacionado no existe o depende de este, o un valor no es válido.
400A required value is missing.Sorry, it's not me, it's you.Falta un dato obligatorio.
400A text is longer than its maximum.Sorry, it's not me, it's you.Uno o más campos exceden la longitud máxima permitida.
400A value has a format that can't be converted (for example, a date).Sorry, it's not me, it's you.El formato de uno o más campos es incorrecto.
400A number is greater than the maximum of its column.Sorry, it's not me, it's you.Uno o más valores numéricos exceden el máximo permitido.
409Another request modified or deleted the record between reading and writing it. Nothing is saved.Sorry, it's not me, it's you.Otra petición modificó o eliminó el registro después de leerlo. Vuelve a consultarlo e inténtalo de nuevo.

On a 409, fetch the record again and repeat the request. People keep their own 409 (see Update an issuer). Any other database failure returns the unexpected error (500).


Specific errors

The full tables of level 2 errors are on these pages:

PageWhat it covers
CFDI 4.0 errorsGeneral invoicing, with CFDI40xxx codes.
Payroll 1.2 errorsThe payroll complement, with NOMxx codes.
Carta Porte 3.1 errorsThe Carta Porte complement, with CPxxx codes.
Payments 2.0 errorsThe payment complement (the complement that records each payment received against a PPD invoice; the type P CFDI that carries it is also called REP), with CRPxxxxx codes.
Comercio Exterior 2.0 errorsThe Comercio Exterior complement, with CCExxx codes.

Was this page helpful?