Invoices
How to interpret invoice data returned by the API and compute income correctly from it.
Invoices in BDI come from CFDIs (Comprobantes Fiscales Digitales) reported to the SAT. The same record can mean very different things depending on its type and on which side of the transaction the subject sits. This page explains how to read them and how to compute monthly income.
Invoices are retrieved from GET /invoices/{identifier} — an 18-char CURP for an individual, or a 12-char RFC for a business. For a business, both invoices it issued and invoices it received are returned.
To improve invoice coverage for individuals, clients that already hold the user's RFC can pass it as the optional rfc field when creating a verification (POST /verifications). When provided, the API uses the client-supplied RFC directly and skips the internal CURP → RFC conversion.
Invoice types
Each invoice has a type field. The type determines whether the amount counts as income, and for which party.
| Code | Counts as income? | For whom |
|---|---|---|
nomina | Yes | The rfc_receiver (the employee being paid) |
ingreso | Yes | The rfc_issuer (the party selling goods/services) |
egreso | No, it is a correction | Reduces a previously issued ingreso from the same RFC |
pago | No, it is a collection complement | Documents the actual payment of a previously issued ingreso |
Perspective: rfc_issuer vs rfc_receiver
rfc_issuer vs rfc_receiverThe same invoice means different things depending on which side of the transaction the subject is on. When querying by CURP or RFC, always check which field the subject appears in:
- A
nominawhere the subject isrfc_receivermeans the subject received a salary. - An
ingresowhere the subject isrfc_issuermeans the subject received an income. - An
ingresowhere the subject isrfc_receivermeans the subject bought goods or services.
Computing monthly income
Monthly income for an identifier is the sum of two components:
- Salary income: sum of
amountfornominainvoices whererfc_receiveris the subject. - Self-employment income: sum of
amountforingresoinvoices issued by the subject, minus the sum ofamountforegresoinvoices issued against thoseingresos by the same RFC.
monthly_income =
Σ nomina.amount (subject as receiver)
+ Σ ingreso.amount (subject as issuer)
− Σ egreso.amount (subject as issuer)
This formula applies to both individuals and businesses. For businesses, salary income is typically zero — they receive income through ingreso invoices, not nomina.
pago invoices: how to read them
pago invoices: how to read thempago invoices are SAT's "Recibo Electrónico de Pago" — a complement issued when a customer pays an invoice that was originally issued under deferred or installment payment terms (PPD). The pago CFDI itself does not represent new revenue; it documents a payment against a previously-issued invoice.
Because of this, per SAT spec the top-level amount, subtotal, total_taxes_transferred, and total_taxes_retained on a pago CFDI are zero or near zero. The actual payment value (amount paid, payment date, payment method) lives in a separate node called "Complemento de Pagos," which is not exposed in the default /invoices response.
To retrieve payment details, pass include_payment_info=true on the request. Each invoice will include a payment_info[] array of payment records, and each entry contains a nested payment_relations[] array linking that specific payment to the ingreso invoices it was applied to.
Use this when:
- Validating whether PPD invoices have been paid.
- Measuring payment behavior (timeliness, partial payments).
- Reconciling invoiced revenue against cash collected in a given period.
Common pitfalls
- Summing
pagoas income → double-counting.pagodocuments a payment, not new revenue. - Treating
egresoas a generic expense → understates income.egresois a correction to a previously issuedingreso, not an arbitrary debit. - Ignoring perspective: a
nominawhere the subject isrfc_issueris not received salary — the subject paid it. - Treating self-issued CFDIs (subject on both sides) as non-income → they are income.
- Expecting
incomes/deductionsoningresoorpagoinvoices → those fields are nómina-specific. - Aggregating across mixed currencies without normalizing (
currencyfield).
Field reference
The same invoice object is returned for individuals and businesses. Nómina-specific fields are only populated for nomina invoices.
| Field Name | Type | Description | Example | Notes |
|---|---|---|---|---|
type | string | Invoice type. | "nomina" | See Invoice Types below. |
amount | number | Total amount. | 42487.53 | |
currency | string | Currency code. | "MXN" | Usually MXN. |
folio_fiscal | string | Fiscal identifier of invoice. | "7A9F33E4-892C-4B8D-A9E1-2F6F0001" | May be null. |
issue_date | string (date) | Official issue date. | "2025-04-01" | YYYY-MM-DD. |
rfc_issuer | string | Issuer's RFC. | "CYA010101AAA" | Who issued the invoice. |
issuer_name | string | Issuer's name. | "Construcciones Yamile S.A. de C.V." | Name of individual or company issuing the invoice. |
rfc_receiver | string | Receiver's RFC. | "OICE940722GHW" | Who received the invoice. |
receiver_name | string | Receiver's name. | "José Carlos Aguilar Rincón" | Name of individual or company receiving the invoice. |
payment_method | string | SAT payment method code. | "PPD" | Populated for ingreso and egreso. |
payment_type | string | SAT payment form code. | "99" | Populated for ingreso and egreso. |
line_items | array[object] | Product or service lines. | See below | Populated for ingreso and egreso. |
invoice_relations | array[object] | Related invoice references. | See below | Populated for ingreso and egreso. |
invoice_status | string | Invoice status. | "" | May be an empty string. |
zip_code_receiver | string | Receiver postal code. | "" | May be an empty string. |
payment_info | array[object] or null | Payment details associated with the invoice. | See Invoice Types and Payment Info below | Only present when include_payment_info=true. |
payroll_type | string | Payroll type, SAT TipoNomina. | Ordinaria | Populated for nomina |
payment_periodicity | string | Payment periodicity, SAT PeriodicidadPago | Quincenal | Populated for nomina |
job_risk | string | Job risk class, SAT RiesgoPuesto. | Clase I | Populated for nomina |
job_role | string | Job role (puesto) reported in the payroll receipt. | AUXILIAR ADMINISTRATIVO | Populated for nomina |
job_department | string | Job department (departamento) reported in the payroll receipt. | ADMINISTRACION | Populated for nomina |
incomes and deductions
incomes and deductionsFor nomina invoices, the response includes incomes and deductions arrays with the line-item breakdown (Percepciones and Deducciones in SAT terminology). For other types, both arrays are empty.
| Field Name | Type | Description | Notes |
|---|---|---|---|
incomes | array[object] | Income breakdown. | Populated for nomina. |
deductions | array[object] | Deduction breakdown. | Populated for nomina. |
Each item has amount, currency, and a free-form detail label (e.g., Sueldo, Aguinaldo, ISR, IMSS). Items can also include key and type SAT codes that identify and group the concept.
line_items
line_itemsFor ingreso and egreso invoices, the response can include a line_items array with the products or services included in the invoice.
| Field Name | Type | Description | Notes |
|---|---|---|---|
detail | string | Product or service description. | |
quantity | number | Number of units included. | |
unit | string | Unit of measure. | |
unit_price | number | Per-unit cost. | |
amount | number | Total amount for the line item. | |
product_service_key | string | SAT product/service identifier. | ClaveProdServ. |
Use line_items to understand what was billed or corrected, but use the top-level amount when computing income totals.
invoice_relations
invoice_relationsFor ingreso and egreso invoices, the response can include an invoice_relations array with references to related fiscal documents.
| Field Name | Type | Description |
|---|---|---|
relation_type | string | SAT relationship code (TipoRelacion). |
related_fiscal_code | string | Folio fiscal of the related invoice. |
Job risk (nomina)
nomina)The SAT defines occupational risk as follows:
| Job risk | Description |
|---|---|
| Clase I | Riesgo ordinario de vida |
| Clase II | Riesgo bajo |
| Clase III | Riesgo medio |
| Clase IV | Riesgo alto |
| Clase V | Riesgo máximo |
Updated 4 days ago
