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.

CodeCounts as income?For whom
nominaYesThe rfc_receiver (the employee being paid)
ingresoYesThe rfc_issuer (the party selling goods/services)
egresoNo, it is a correctionReduces a previously issued ingreso from the same RFC
pagoNo, it is a collection complementDocuments the actual payment of a previously issued ingreso

Perspective: rfc_issuer vs rfc_receiver

The 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 nomina where the subject is rfc_receiver means the subject received a salary.
  • An ingreso where the subject is rfc_issuer means the subject received an income.
  • An ingreso where the subject is rfc_receiver means the subject bought goods or services.

Only the subject's side of each invoice is populated. The counterparty (the employer on a nomina, the client on an ingreso the subject issued, the supplier on an ingreso the subject received) comes back with rfc_* and *_name set to null. The fields are always present. In practice, the populated side tells you where the subject is. If both sides are null, the subject's RFC couldn't be determined for that identifier.

Computing monthly income

Monthly income for an identifier is the sum of two components:

  1. Salary income: sum of amount for nomina invoices where rfc_receiver is the subject.
  2. Self-employment income: sum of amount for ingreso invoices issued by the subject, minus the sum of amount for egreso invoices issued against those ingresos 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 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 pago as income → double-counting. pago documents a payment, not new revenue.
  • Treating egreso as a generic expense → understates income. egreso is a correction to a previously issued ingreso, not an arbitrary debit.
  • Ignoring perspective: a nomina where the subject is rfc_issuer is not received salary — the subject paid it.
  • Treating self-issued CFDIs (subject on both sides) as non-income → they are income.
  • Expecting incomes / deductions on ingreso or pago invoices → those fields are nómina-specific.
  • Aggregating across mixed currencies without normalizing (currency field).

Field reference

The same invoice object is returned for individuals and businesses. Nómina-specific fields are only populated for nomina invoices.

Field NameTypeDescriptionExampleNotes
typestringInvoice type."nomina"See Invoice Types below.
amountnumberTotal amount.42487.53
currencystringCurrency code."MXN"Usually MXN.
folio_fiscalstringFiscal identifier of invoice."7A9F33E4-892C-4B8D-A9E1-2F6F0001"May be null.
issue_datestring (date)Official issue date."2025-04-01"YYYY-MM-DD.
rfc_issuerstring or nullIssuer's RFC."CYA010101AAA"Who issued the invoice. null when the issuer is not the subject.
issuer_namestring or nullIssuer's name."Construcciones Yamile S.A. de C.V."Name of individual or company issuing the invoice. null when the issuer is not the subject.
rfc_receiverstring or nullReceiver's RFC."OICE940722GHW"Who received the invoice. null when the receiver is not the subject."
receiver_namestring or nullReceiver's name."José Carlos Aguilar Rincón"Name of individual or company receiving the invoice. null when the receiver is not the subject."
payment_methodstringSAT payment method code."PPD"Populated for ingreso and egreso.
payment_typestringSAT payment form code."99"Populated for ingreso and egreso.
line_itemsarray[object]Product or service lines.See belowPopulated for ingreso and egreso.
invoice_relationsarray[object]Related invoice references.See belowPopulated for ingreso and egreso.
invoice_statusstringInvoice status.""May be an empty string.
zip_code_receiverstringReceiver postal code.""May be an empty string.
payment_infoarray[object] or nullPayment details associated with the invoice.See payment_info belowOnly present when include_payment_info=true.
payroll_typestringPayroll type, SAT TipoNomina.OrdinariaPopulated for nomina
payment_periodicitystringPayment periodicity, SAT PeriodicidadPagoQuincenalPopulated for nomina
job_riskstringJob risk class, SAT RiesgoPuesto.Clase IPopulated for nomina
job_rolestringJob role (puesto) reported in the payroll receipt.AUXILIAR ADMINISTRATIVOPopulated for nomina
job_departmentstringJob department (departamento) reported in the payroll receipt.ADMINISTRACIONPopulated for nomina
job_tenurestringJob tenure (antigüedad) with the employer, as declared in the payroll receipt.P24WPopulated for nomina. ISO-8601 duration.
bankstringBank of the account the payroll was deposited to, SAT c_Banco code."014"Populated for nomina. May be null.
bank_accountstringAccount or CLABE the payroll was deposited to."60619949848"Populated for nomina. May be null.
📘

job_tenure, bank and bank_account

These three come from the payroll receipt itself, as the employer reported it to the SAT — we pass them through, we do not verify them.

job_tenure is an ISO-8601 duration: P[n]Y[n]M[n]D for years, months and days, or P[n]W for weeks. It is the tenure the employer declared on that receipt, and it is not the same field as the profile-level tenure, which is a bucket we compute.

bank_account is the account that receipt was deposited to. It belongs to the receipt, not to the person, so it can differ between receipts. bank only arrives when the account is not a CLABE — a CLABE already encodes the bank in its first three digits — so the two are rarely present together.



incomes and deductions

For 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 NameTypeDescriptionNotes
incomesarray[object]Income breakdown.Populated for nomina.
deductionsarray[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

For ingreso and egreso invoices, the response can include a line_items array with the products or services included in the invoice.

Field NameTypeDescriptionNotes
detailstringProduct or service description.
quantitynumberNumber of units included.
unitstringUnit of measure.
unit_pricenumberPer-unit cost.
amountnumberTotal amount for the line item.
product_service_keystringSAT 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

For ingreso and egreso invoices, the response can include an invoice_relations array with references to related fiscal documents.

Field NameTypeDescription
relation_typestringSAT relationship code (TipoRelacion).
related_fiscal_codestringFolio fiscal of the related invoice.


payment_info

For pago invoices, passing include_payment_info=true adds a payment_info array with one entry per payment recorded in the CFDI's "Complemento de Pagos". Each entry carries its own payment_relations array linking that payment to the ingreso invoices it was applied to.

Field NameTypeDescriptionExampleNotes
total_amount_paidnumberTotal amount paid in this payment record.15000.00
currencystringCurrency of the payment."MXN"Defaults to MXN.
exchange_ratenumberExchange rate applied at time of payment.1Defaults to 1.0 for MXN payments.
payment_formstringSAT payment form code."03"01 = cash, 02 = check, 03 = electronic transfer.
payment_datestring (date)Date of the payment."2025-08-15"YYYY-MM-DD.
payer_issuer_rfcstringRFC of the bank the payment was issued from (Pago.RfcEmisorCtaOrd)."BBA830831LJ2"Optional in the CFDI. May be null.
payer_account_numberstringAccount the payment was issued from (Pago.CtaOrdenante)."014320655074276693"Optional in the CFDI. May be null.
beneficiary_issuer_rfcstringRFC of the bank that received the payment (Pago.RfcEmisorCtaBen)."BBA830831LJ2"Optional in the CFDI. May be null.
beneficiary_account_numberstringAccount that received the payment (Pago.CtaBeneficiario)."012330004435427501"Optional in the CFDI. May be null.
payment_relationsarray[object]Invoices this payment was applied to.See below

Each entry of payment_relations:

Field NameTypeDescription
related_fiscal_codestringFolio fiscal (UUID) of the related invoice that received the payment.
paid_amountnumberThe partial or full amount applied to that related invoice.


Job risk (nomina)

The SAT defines occupational risk as follows:

Job riskDescription
Clase IRiesgo ordinario de vida
Clase IIRiesgo bajo
Clase IIIRiesgo medio
Clase IVRiesgo alto
Clase VRiesgo máximo

Did this page help you?