Toda la documentación de la API en un archivo. Los ejemplos son de cURL y TypeScript; cada página (https://fiscalisto.com/docs/api/.md) trae también los de Python, PHP, .NET, Go y Java. --- # API de facturación > Timbre CFDI 4.0 desde su sistema con una sola llamada: facturas, pagos, notas de crédito, nómina y retenciones, con las revisiones del SAT de Fiscalisto. La API de Fiscalisto timbra comprobantes fiscales (CFDI 4.0) desde su servidor. Usted manda los datos de la factura en JSON; Fiscalisto llena lo que tiene un valor usual, revisa las reglas del SAT antes de gastar un timbre, la sella con el certificado del emisor, la timbra con el PAC y le contesta con la factura timbrada, su UUID, el PDF y el XML. Los nombres de la API están en inglés, como en cualquier API (`customer`, `items`, `unit_price`); los textos, los errores y estas guías están en español. ## Su primera factura en un minuto 1. Abra una cuenta API en Fiscalisto (selector de empresa › **Nueva cuenta API**). Al crearla le damos una llave de pruebas que empieza con `fk_test_` y un emisor de pruebas listo para timbrar. 2. Guarde la llave en una variable de entorno de su servidor: ```bash export FISCALISTO_API_KEY="fk_test_..." ``` 3. Timbre una factura: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pedido-A-1042" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "items": [ { "description": "Mensualidad", "product_key": "86121700", "quantity": 2, "unit_price": 2500 } ] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "03", items: [ { description: "Mensualidad", product_key: "86121700", quantity: 2, unit_price: 2500, }, ], }, { idempotencyKey: "pedido-A-1042" }); ``` La respuesta es la factura timbrada (`201`): ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "folio": "1", "subtotal": 5000, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "...": "..." } ``` 4. Descargue el PDF o el XML: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices/inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg/pdf \ -H "Authorization: Bearer $FISCALISTO_API_KEY" -o factura.pdf ``` ```ts tab="TypeScript" import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.invoices.pdf("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg")); ``` No mandó el uso del CFDI, el método de pago, la moneda, la unidad, el objeto de impuesto ni el IVA: tienen su valor usual (uso G03, PUE, MXN, unidad E48, objeto 02, IVA 16%). Mande solo lo que cambia. Con TypeScript, Python, PHP, Go, Java o C#, los [SDKs](https://fiscalisto.com/docs/api/sdk) le dan tipos para cada campo y la `Idempotency-Key` sin pensarlo: ```ts import { Fiscalisto } from "fiscalisto"; const fiscalisto = new Fiscalisto(process.env.FISCALISTO_API_KEY); const invoice = await fiscalisto.invoices.create({ customer, payment_form: "03", items }); ``` ## Lo que necesita saber - **Pruebas y producción.** Las llaves `fk_test_` timbran gratis en el ambiente de pruebas del SAT; las `fk_live_` timbran CFDI reales y usan un timbre cada una. Vea [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion). - **Idempotency-Key.** Todo lo que timbra la pide. Si la respuesta se pierde, repita la solicitud con la misma: nunca se timbra dos veces. Vea [Idempotencia](https://fiscalisto.com/docs/api/idempotencia). - **Errores claros.** Cada error dice qué campo corregir (`param`), cómo (`message`) y, si el SAT lo rechazó, su código (`sat_code`). Vea [Errores](https://fiscalisto.com/docs/api/errores). - **Webhooks.** Las cancelaciones que el cliente acepta días después, las facturas que el PAC tardó en timbrar y los lotes le llegan como eventos firmados. Vea [Webhooks](https://fiscalisto.com/docs/api/webhooks). ## Qué puede timbrar | Comprobante | Ruta | | --- | --- | | Factura de ingreso, global o de traslado (con complementos) | [`POST /v1/invoices`](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice) | | Complemento de pago (tipo P) | [`POST /v1/payments`](https://fiscalisto.com/docs/api/referencia/pagos-y-notas-de-credito/create-payment) | | Nota de crédito (tipo E) | [`POST /v1/credit_notes`](https://fiscalisto.com/docs/api/referencia/pagos-y-notas-de-credito/create-credit-note) | | Anticipo y su aplicación | [`POST /v1/advances`](https://fiscalisto.com/docs/api/referencia/anticipos/create-advance) | | Recibo de nómina (tipo N) | [`POST /v1/payroll_receipts`](https://fiscalisto.com/docs/api/referencia/nomina/create-payroll-receipt) | | Constancia de retenciones | [`POST /v1/retentions`](https://fiscalisto.com/docs/api/referencia/retenciones/create-retention) | | Hasta 1,000 facturas de una vez | [`POST /v1/invoice_batches`](https://fiscalisto.com/docs/api/referencia/lotes/create-invoice-batch) | ## Para agentes y herramientas - Para que un agente de código (Claude Code, Cursor, Codex…) integre Fiscalisto, conecte el servidor MCP de esta documentación y dele la guía en una página: vea [Fiscalisto para agentes de IA](https://fiscalisto.com/docs/api/agentes). - El documento OpenAPI 3.1 completo está en [`https://api.fiscalisto.com/v1/openapi.json`](https://api.fiscalisto.com/v1/openapi.json), sin llave. Cada campo tiene su descripción y su nombre en el XML del SAT. - Cada página de estas guías existe también en Markdown: agregue `.md` a su dirección (por ejemplo [`/docs/api/idempotencia.md`](https://fiscalisto.com/docs/api/idempotencia.md)). - [`/docs/api/llms.txt`](https://fiscalisto.com/docs/api/llms.txt) es el índice de todas las páginas, y [`/docs/api/llms-full.txt`](https://fiscalisto.com/docs/api/llms-full.txt) las guías completas en un solo archivo. --- # Pruebas y producción > Pruebe gratis con llaves fk_test_ y pase a producción con su emisor, sus timbres y una llave fk_live_. Su cuenta API tiene dos modos que no se mezclan: cada uno tiene sus llaves, sus emisores, sus clientes, sus facturas, sus webhooks y sus eventos. La llave con la que llama decide el modo; cada objeto lo dice en `livemode`. | | Pruebas | Producción | | --- | --- | --- | | Llave | `fk_test_…` | `fk_live_…` | | Validez ante el SAT | Ninguna: no son CFDI reales | CFDI reales | | Costo | Gratis | Un timbre por comprobante | | Emisor | Cualquier RFC; se timbra como el emisor de pruebas del SAT | Su RFC, con su CSD y su e.firma | | Límite de solicitudes | 100 cada 10 segundos | 100 cada 10 segundos | ## Pruebas Al abrir su cuenta API ya tiene un emisor de pruebas y una llave `fk_test_`: puede timbrar en ese momento. En pruebas se aplican las mismas revisiones del SAT que en producción (catálogos, régimen contra uso, totales, complementos) y el PAC timbra en su ambiente de pruebas, así que una factura que sale bien en pruebas sale bien en producción. Puede crear emisores de pruebas con cualquier RFC para imitar su configuración real, pero no se suben archivos: el ambiente de pruebas solo acepta el certificado de pruebas del SAT, así que todo se timbra como `EKU9003173C9` (el campo `stamps_as` del emisor lo dice). Los clientes, en cambio, pueden ser reales o de prueba. Pruebas es gratis, con dos cuidados: una cuenta timbra hasta 1,000 comprobantes de prueba al día (vea [Límites](https://fiscalisto.com/docs/api/limites)), y los correos de las facturas de prueba solo llegan a las personas de su cuenta API, para probar `send_email` en su propio correo sin escribirle a sus clientes. ### Provocar cada rechazo del SAT Para probar cómo responde su sistema cuando el SAT rechaza una factura, use uno de estos RFC como cliente con su llave de pruebas. La API responde `422` con `invoice_rejected`, el `sat_code` y el mensaje para su usuario, exactamente como en producción, y le llega `invoice.failed` a sus webhooks. Los demás datos pueden ser cualquiera; con otros RFC la factura se timbra. | RFC del cliente | `sat_code` | Qué simula | | --- | --- | --- | | `NOM010101FK9` | `CFDI40145` | El nombre del cliente no corresponde con su RFC. | | `CPO010101FK1` | `CFDI40147` | El código postal del cliente no es el de su domicilio fiscal. | | `REG010101FK9` | `CFDI40157` | El régimen fiscal del cliente no es el que tiene en el SAT. | | `NOI010101FK5` | `CFDI40143` | El RFC del cliente no está inscrito en el SAT. | | `USO010101FK4` | `CFDI40161` | El uso del CFDI no corresponde al régimen del cliente. | ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: prueba-rechazo-1" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "NOM010101FK9", "legal_name": "CLIENTE DE PRUEBA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "items": [{ "description": "Consultoría", "product_key": "80101500", "unit_price": 1000 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "NOM010101FK9", legal_name: "CLIENTE DE PRUEBA", tax_system: "601", zip: "33826", }, payment_form: "03", items: [ { description: "Consultoría", product_key: "80101500", unit_price: 1000, }, ], }, { idempotencyKey: "prueba-rechazo-1" }); ``` Solo funcionan en pruebas: en producción esos RFC no existen y el SAT los rechaza por eso. ### Probar desde la documentación Cada página de la [referencia](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice) tiene **Probar**: cambie los campos y pulse **Enviar**. Si entró a Fiscalisto con su cuenta API abierta (o tiene solo una) no necesita llave: la solicitud corre como esa cuenta, en pruebas, y aparece en **Solicitudes** como «Documentación». Si no, ponga su llave `fk_test_…` en **Authorization**. La solicitud llega a la API de verdad (timbra en pruebas, le manda eventos a sus webhooks de pruebas) y ve la respuesta completa con sus encabezados. La llave se queda solo en su navegador. Ahí no se aceptan llaves `fk_live_`: lo de producción se llama desde su servidor. ### Empezar de cero Para que sus pruebas automáticas siempre arranquen igual, reinicie el modo de pruebas: cada emisor de pruebas conserva su RFC y su CSD de pruebas, y se borran sus facturas, clientes, productos, lotes, ligas y descargas, además de los eventos y las solicitudes de pruebas. Las llaves y los webhooks se quedan. Con `seed` (por omisión) quedan 3 clientes y 3 productos de ejemplo en cada emisor. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/test_data/reset \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "seed": true }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testDataReset = await fiscalisto.testData.reset({ seed: true }); ``` Solo con una llave `fk_test_` (con `fk_live_` responde `test_mode_only`) y hasta 20 veces al día. También está en **Desarrolladores › Inicio**, en **Datos de prueba**. ### Un entorno de prueba para cada persona Si varias personas o proyectos usan la misma cuenta, dele a cada uno su entorno de prueba: nace con su propio emisor de pruebas del SAT y una llave `fk_test_` que solo ve los emisores, facturas, webhooks y eventos de ese entorno. Así el reinicio de una persona no le borra nada a otra, y su webhook no recibe los eventos de los demás. Las llaves de pruebas de la cuenta tampoco ven lo que hay en un entorno. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/sandboxes \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Ana" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const sandbox = await fiscalisto.sandboxes.create({ name: "Ana" }); ``` La respuesta trae `key.secret` una sola vez: es la llave del entorno. Con ella, `POST /v1/test_data/reset` vacía solo ese entorno, y tiene sus propios 20 reinicios al día. Los entornos se crean, listan y borran con una llave de pruebas de la cuenta (una del entorno responde `key_in_sandbox`), hasta 25 por cuenta. Al borrar uno, sus llaves dejan de funcionar al momento. También están en **Desarrolladores › Llaves**, en **Entornos de prueba**. ### Adelantar el tiempo con un reloj de prueba Una factura recurrente mensual, una cotización que vence o la factura global de los tickets del mes tardan semanas en pasar. Con un reloj de prueba las ve en segundos: el emisor de pruebas vive en la hora del reloj, que solo avanza cuando usted lo adelanta. Primero el reloj, congelado ahora (o en `frozen_time`): ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/test_clock \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testClock = await fiscalisto.testClocks.create("iss_OufpKqnrvx9p"); ``` Cree sus facturas recurrentes, cotizaciones o tickets como siempre, y adelántelo, por ejemplo tres meses: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/test_clock/advance \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "frozen_time": "2027-01-25T09:00:00-06:00" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testClock = await fiscalisto.testClocks.advance("iss_OufpKqnrvx9p", { frozen_time: "2027-01-25T09:00:00-06:00" }); ``` Al adelantarlo se hace, día por día, lo que habría pasado: se timbra cada periodo de las facturas recurrentes (con `{mes}` y `{año}` de ese periodo), vencen las cotizaciones y salen las facturas globales de tickets, cada cosa con sus webhooks. La respuesta llega cuando terminó, con lo que hizo en `advanced`, y además llega el evento `test_clock.ready`. - Mientras tenga reloj, los procesos diarios no tocan al emisor: nada pasa solo. - El reloj llega hasta dos años adelante. Cada llamada timbra hasta 25 comprobantes; si toca más, se detiene al final de ese día con `stopped_early: true`, y otra llamada sigue desde ahí. - Los CFDI se timbran con la fecha y hora reales, porque el SAT solo acepta las de hoy. El periodo, los vencimientos, `amount_due` y los eventos sí siguen al reloj. - Las ligas que abren sus clientes (cotizaciones, autofactura) van con la hora real. - `DELETE /v1/issuers/iss_…/test_clock` regresa al emisor a la hora real. Solo los emisores de prueba tienen reloj, uno cada uno, y se adelanta hasta 50 veces al día. > **No mezcle los modos.** Un id de un modo no existe en el otro. Si usa una llave `fk_live_` con un `inv_…` de pruebas, la API responde `livemode_mismatch`. ## Pasar a producción 1. **Agregue su emisor** con [`POST /v1/issuers`](https://fiscalisto.com/docs/api/referencia/emisores/create-issuer) usando una llave de producción, o en el panel en **Emisores**. Los datos van tal como están en la constancia de situación fiscal. 2. **Suba su CSD** (certificado de sello digital: `.cer`, `.key` y contraseña) con [`POST /v1/issuers/{id}/certificates`](https://fiscalisto.com/docs/api/referencia/emisores/upload-certificate). 3. **Firme el contrato con el PAC** con la e.firma del RFC: [`POST /v1/issuers/{id}/efirma`](https://fiscalisto.com/docs/api/referencia/emisores/sign-efirma). Es una sola vez por RFC. 4. **Tenga timbres.** Al abrir su primera cuenta API recibe 20 de regalo; después compre paquetes en **Desarrolladores › Timbres**. Consulte los suyos con [`GET /v1/balance`](https://fiscalisto.com/docs/api/referencia/cuenta/get-balance). 5. **Cambie la llave** de su servidor por una `fk_live_` (se crea en **Desarrolladores › Llaves** y pide verificación en dos pasos). El emisor está listo cuando su `status` es `active`. Mientras no lo esté, `requirement` dice qué falta. Vea [Emisores](https://fiscalisto.com/docs/api/emisores). ## Qué cuesta un timbre Cada comprobante timbrado en producción usa un timbre: factura, complemento de pago, nota de crédito, anticipo, recibo de nómina o constancia de retenciones. Cancelar un comprobante también usa uno, solo la primera vez que el SAT acepta la solicitud. No usan timbre las solicitudes que no pasan la revisión, las que el SAT o el PAC rechazan ni las repeticiones con la misma `Idempotency-Key`. Cuando ya no quedan, la API responde `402` con `insufficient_balance` y no timbra. Compre un paquete y repita la solicitud con la misma `Idempotency-Key`. Para que no lleguen a acabarse: con pocos timbres le llega el evento `balance.low` a sus webhooks y un correo (la cantidad se elige en **Desarrolladores › Timbres**, 100 si no la cambia), y otra vez al acabarse. Ahí mismo puede activar la **Recarga automática**: compramos el paquete que elija con su tarjeta cuando queden los que diga, hasta las veces al mes que diga. `GET /v1/balance` trae ambas cosas en `low_balance_at` y `auto_recharge`. Comprar timbres no se puede con una llave de la API, a propósito: una llave filtrada no puede hacer cargos a su tarjeta. --- # SDKs: TypeScript, Python, PHP, .NET, Go y Java > Timbre desde Node, Bun, Deno, Workers, Python, PHP, .NET, Go o Java con tipos para cada campo, reintentos seguros y webhooks verificados en una línea. Los seis SDKs son la misma API con los nombres de su lenguaje: se generan del mismo documento [OpenAPI](https://api.fiscalisto.com/v1/openapi.json), así que tienen cada ruta y cada campo, con su descripción y el nombre que lleva en el XML del SAT. Ninguno tiene dependencias, salvo Jackson en Java. | SDK | Funciona en | Instalar | | --- | --- | --- | | TypeScript | Node 20 o más reciente, Bun, Deno, Cloudflare Workers | `npm install fiscalisto` | | Python | Python 3.10 o más reciente | `pip install fiscalisto` | | PHP | PHP 8.1 o más reciente, con `curl` y `json` | `composer require fiscalisto/fiscalisto` | | .NET (C#) | .NET 8 o más reciente | `dotnet add package Fiscalisto` | | Go | Go 1.23 o más reciente | `go get fiscalisto.com/go` | | Java | Java 17 o más reciente (también Kotlin) | Maven `com.fiscalisto:fiscalisto` | ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; const fiscalisto = new Fiscalisto(); // la llave viene de FISCALISTO_API_KEY const invoice = await fiscalisto.invoices.create( { customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826" }, payment_form: "03", items: [{ description: "Mensualidad", product_key: "86121700", quantity: 2, unit_price: 2500 }], }, { idempotencyKey: "pedido-A-1042" }, ); console.log(invoice.uuid, invoice.total); ``` También puede pasar la llave al crear el cliente: `new Fiscalisto("fk_test_…")`, `Fiscalisto("fk_test_…")`, `new Fiscalisto('fk_test_…')`, `new FiscalistoClient("fk_test_…")` (en C# y en Java) o `fiscalisto.NewClient("fk_test_…")`. Con una llave `fk_test_` todo se timbra en el ambiente de pruebas del SAT; con una `fk_live_`, en producción (vea [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion)). ## Cada ruta, un método Los métodos llevan el nombre del recurso y lo que hacen. Los campos son los mismos de la API, en inglés y con guion bajo, así que la [referencia](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice) vale igual: cada página trae el ejemplo en los seis lenguajes. | Ruta | TypeScript | Python | PHP | | --- | --- | --- | --- | | `POST /v1/invoices` | `invoices.create({…})` | `invoices.create({…})` | `invoices->create([…])` | | `GET /v1/invoices/{id}` | `invoices.get("inv_…")` | `invoices.get("inv_…")` | `invoices->get('inv_…')` | | `GET /v1/invoices/{id}/pdf` | `invoices.pdf(id)`: `Uint8Array` | `invoices.pdf(id)`: `bytes` | `invoices->pdf($id)`: `string` | | `GET /v1/invoices/{id}/xml` | `invoices.xml(id)` | `invoices.xml(id)` | `invoices->xml($id)` | | `POST /v1/invoices/{id}/cancel` | `invoices.cancel(id, { motive: "02" })` | `invoices.cancel(id, {"motive": "02"})` | `invoices->cancel($id, ['motive' => '02'])` | | `POST /v1/payments` | `payments.create({…})` | `payments.create({…})` | `payments->create([…])` | | `POST /v1/credit_notes` | `creditNotes.create({…})` | `credit_notes.create({…})` | `creditNotes->create([…])` | | `POST /v1/invoice_batches` | `invoiceBatches.create({…})` | `invoice_batches.create({…})` | `invoiceBatches->create([…])` | | `GET /v1/sat_catalogs/{name}` | `satCatalogs.get("product_keys", { q })` | `sat_catalogs.get("product_keys", {"q": q})` | `satCatalogs->get('product_keys', ['q' => $q])` | | `POST /v1/webhook_endpoints/{id}/roll_secret` | `webhookEndpoints.rollSecret(id)` | `webhook_endpoints.roll_secret(id)` | `webhookEndpoints->rollSecret($id)` | En Python los recursos y métodos van con guion bajo (`credit_notes`, `roll_secret`); en TypeScript y PHP, como `creditNotes` y `rollSecret`; en C#, con mayúscula y `Async` (`fiscalisto.CreditNotes.CreateAsync(…)`, `fiscalisto.WebhookEndpoints.RollSecretAsync(id)`), y los campos como propiedades (`TaxId`, `UnitPrice`). En TypeScript cada respuesta tiene su tipo (`Invoice`, `Customer`…); en Python es un `TypedDict` que su editor y `mypy` revisan; en PHP es un arreglo con su forma documentada para PHPStan y Psalm; en C# es una clase (`Invoice`, `Customer`…) con la descripción de cada campo en su editor, y lo que manda también (`InvoiceCreate`, `CancelInvoiceParams`…), con `required` en lo obligatorio para que el compilador le avise. En Go, cada recurso es un campo del cliente (`client.Invoices.Create(ctx, …)`, `client.WebhookEndpoints.RollSecret(ctx, id, nil)`), lo que manda es un struct (`InvoiceCreate`, `CancelInvoiceParams`…) y lo opcional es un apuntador: `fiscalisto.String("PUE")`, `fiscalisto.Float64(2500)`, `fiscalisto.Int64(10)`. Un cliente es un id o sus datos: `fiscalisto.IDOf[fiscalisto.InvoiceCreateCustomer]("cus_…")` o `fiscalisto.ObjectOf(fiscalisto.InvoiceCreateCustomer{…})`. En Java, cada recurso es un método (`fiscalisto.invoices().create(…)`, `fiscalisto.webhookEndpoints().rollSecret(id)`) y cada clase tiene setters encadenados (`new InvoiceCreate().setPaymentForm("03")`); los importes son `BigDecimal` y los setters aceptan también un número. ```ts tab="TypeScript" import { writeFile } from "node:fs/promises"; await writeFile("factura.pdf", await fiscalisto.invoices.pdf(invoice.id)); ``` ## Idempotency-Key sin pensarlo Lo que timbra lleva siempre su `Idempotency-Key`. Si no pasa una, el SDK crea una por llamada y la repite en sus reintentos, así que un corte de red nunca timbra dos veces. Pase la suya (`idempotencyKey`, `idempotency_key=`, `'idempotency_key' =>`, `new RequestOptions { IdempotencyKey = … }`, `fiscalisto.WithIdempotencyKey(…)` o `RequestOptions.idempotencyKey(…)`) cuando quiera que repetir la llamada desde su sistema, aunque se haya reiniciado, conteste la misma factura. Vea [Idempotencia](https://fiscalisto.com/docs/api/idempotencia). ## Reintentos seguros Cada SDK manda una `Idempotency-Key` nueva en cada `POST` (la misma en sus reintentos) y reintenta dos veces, con una espera que crece (medio segundo, uno, dos… hasta ocho), solo lo que no puede hacer nada dos veces: - Los `429` (respeta `Retry-After` hasta 60 segundos) y las solicitudes con la misma `Idempotency-Key` aún en proceso (`409` `request_in_progress`). - Los errores de red y los `5xx`: las lecturas y los borrados no cambian nada al repetirse, y un `POST` con la misma llave contesta lo de la primera vez. Si usted pasa su propia llave (el id de su pedido), sus reintentos también quedan protegidos aunque su proceso se reinicie. Una solicitud propia sin llave, con el método genérico de solicitudes, no se repite si pudo haber llegado. Cambie cuántos reintentos y cuánto esperar cada respuesta, para todo el cliente o en una llamada: ```ts tab="TypeScript" const fiscalisto = new Fiscalisto(undefined, { maxRetries: 4, timeout: 30_000 }); // milisegundos await fiscalisto.invoices.get("inv_…", { maxRetries: 0 }); ``` En .NET puede pasar su propio `HttpClient` (por ejemplo de `IHttpClientFactory`) en `FiscalistoClientOptions.HttpClient`; use un solo `FiscalistoClient` en toda la aplicación (regístrelo como singleton). En Go pase su propio `*http.Client` con `fiscalisto.WithHTTPClient(…)` y en Java su `HttpClient` con `ClientOptions.builder().httpClient(…)`; los clientes de Go y de Java también se comparten entre hilos y goroutines. Por omisión cada respuesta se espera 80 segundos: timbrar puede tardar lo que tarde el PAC. ## Listas Recorrer la lista le da todos los objetos, página por página, sin que usted maneje `starting_after`. Para una sola página, lea `data` y `has_more`: ```ts tab="TypeScript" for await (const invoice of fiscalisto.invoices.list({ status: "valid", created_gte: "2026-10-01", limit: 100 })) { console.log(invoice.uuid, invoice.total); } const page = await fiscalisto.customers.list({ limit: 10 }); console.log(page.data.length, page.has_more); ``` ## Errores Un error de la API lanza `FiscalistoError` (en PHP, C# y Java, `FiscalistoException`; en Go la llamada devuelve un `*fiscalisto.Error`) con los mismos datos del [cuerpo de error](https://fiscalisto.com/docs/api/errores). Decida con el código; muestre el mensaje. Cada código está en `ErrorCode` (`ErrorCode.InvoiceRejected`, `ErrorCode.INVOICE_REJECTED` en Python, `ErrorCode::INVOICE_REJECTED` en PHP, `ErrorCode.INVOICE_REJECTED` en Java, `fiscalisto.CodeInvoiceRejected` en Go), con su descripción en su editor: un código mal escrito no compila. | Dato | TypeScript | Python | PHP | C# | Go | Java | | --- | --- | --- | --- | --- | --- | --- | | Tipo (`invalid_request_error`, `invoice_error`…) | `error.type` | `e.type` | `$e->type` | `e.Type` | `e.Type` | `e.getType()` | | Código (`invoice_rejected`…) | `error.code` | `e.code` | `$e->errorCode` | `e.Code` | `e.Code` | `e.getCode()` | | Qué corregir | `error.message` | `e.message` | `$e->getMessage()` | `e.Message` | `e.Message` | `e.getMessage()` | | Campo con el problema | `error.param` | `e.param` | `$e->param` | `e.Param` | `e.Param` | `e.getParam()` | | Código del SAT (`CFDI40145`…) | `error.satCode` | `e.sat_code` | `$e->satCode` | `e.SatCode` | `e.SatCode` | `e.getSatCode()` | | El comprobante que quedó | `error.invoice` | `e.invoice` | `$e->invoice` | `e.Invoice` | `e.Invoice` | `e.getInvoice()` | | Estado HTTP | `error.status` | `e.status` | `$e->status` | `e.StatusCode` | `e.StatusCode` | `e.getStatusCode()` | | Id de la solicitud, para soporte | `error.requestId` | `e.request_id` | `$e->requestId` | `e.RequestId` | `e.RequestID` | `e.getRequestId()` | ```ts tab="TypeScript" import { ErrorCode, Fiscalisto, FiscalistoError } from "fiscalisto"; try { await fiscalisto.invoices.create(datos, { idempotencyKey: pedido.id }); } catch (error) { if (error instanceof FiscalistoError && error.code === ErrorCode.InvoiceRejected) { // El SAT no la aceptó: error.message dice qué corregir, error.param en qué campo. await avisarAlCliente(error.message); } else { throw error; } } ``` Si la API no contesta, el tipo es `connection_error` (código `timeout` o `connection_failed`). Con lo que timbra, repita la llamada con la misma `Idempotency-Key` para saber si se timbró. Si lo que contestó no es la API (la página de un proxy o del portal de una red), el tipo es `api_error` con el código `invalid_response`. ## Webhooks Verificar un webhook no necesita la llave de la API, solo el secreto del endpoint (`whsec_…`): `verifyWebhook` (TypeScript), `verify_webhook` (Python), `Webhook::verify` (PHP), `FiscalistoClient.Webhooks.Verify` (C#), `fiscalisto.VerifyWebhook` (Go) y `Webhooks.verify` (Java) revisan la firma y la hora y le devuelven el evento: según `type`, `data.object` es una factura, una constancia de retenciones o un lote. Pase el cuerpo tal como llegó, sin interpretarlo. Si la firma no es válida o el evento tiene más de 5 minutos, lanza `FiscalistoWebhookError` (en PHP, `WebhookException`; en C#, `FiscalistoWebhookException`; en Java, `Webhooks.VerificationException`; en Go devuelve un error con `errors.Is(err, fiscalisto.ErrWebhookSignature)`): conteste `400`. ```ts tab="TypeScript" // Next.js (app/webhooks/fiscalisto/route.ts); en Bun, Deno, Hono o Workers es igual. import { FiscalistoWebhookError, verifyWebhook } from "fiscalisto"; export async function POST(request: Request) { try { const event = await verifyWebhook( await request.text(), request.headers, process.env.FISCALISTO_WEBHOOK_SECRET as string, ); if (event.type === "invoice.stamped") await guardar(event.data.object.uuid); return new Response(null, { status: 200 }); } catch (error) { if (error instanceof FiscalistoWebhookError) return new Response(null, { status: 400 }); throw error; } } ``` Con un cliente ya creado, `fiscalisto.webhooks.verify(…)` (`$fiscalisto->webhooks->verify(…)` en PHP) hace lo mismo. Durante un cambio de secreto llegan dos firmas y basta con que una coincida. Vea [Webhooks](https://fiscalisto.com/docs/api/webhooks). ## Proyectos de inicio Una app pequeña que ya timbra, para copiar lo que necesite: un formulario que timbra una factura de prueba con su `Idempotency-Key`, la descarga del PDF y el XML, el manejo de un rechazo del SAT y los webhooks con la firma verificada. Ninguna necesita base de datos. | Proyecto | Descargar | Correrlo | | --- | --- | --- | | Next.js (App Router) | [nextjs.zip](https://fiscalisto.com/docs/api/starters/nextjs.zip) | `npm install && npm run dev` | | Laravel | [laravel.zip](https://fiscalisto.com/docs/api/starters/laravel.zip) | `composer install && php artisan serve` | | Django | [django.zip](https://fiscalisto.com/docs/api/starters/django.zip) | `pip install -r requirements.txt && python manage.py runserver` | | ASP.NET Core | [aspnet.zip](https://fiscalisto.com/docs/api/starters/aspnet.zip) | `dotnet run` | Ponga su llave en `FISCALISTO_API_KEY` (cada uno trae su `.env.example` y su README) y, para los webhooks en su computadora, corra `npx fiscalisto listen --forward-to http://localhost:…/webhooks/fiscalisto` (vea [CLI](https://fiscalisto.com/docs/api/cli#webhooks-en-su-computadora)). ## Postman, Bruno e Insomnia Para probar sin escribir código, importe la [colección de la API](https://fiscalisto.com/docs/api/fiscalisto.postman_collection.json): una carpeta por recurso, cada ruta con su ejemplo y una `Idempotency-Key` nueva en lo que timbra. Postman la abre directo; en Bruno use **Import Collection › Postman Collection**, y en Insomnia **Import**. La llave va en la variable `apiKey` de la colección. Al crear una llave de pruebas en **Desarrolladores › Llaves**, descárguela con su llave ya puesta. ## Otros lenguajes Para Ruby, Rust o cualquier otro, llame a la API con su cliente HTTP (cada página de la referencia trae el ejemplo con `curl` y `fetch`) o genere un cliente con el documento [OpenAPI](https://api.fiscalisto.com/v1/openapi.json). --- # CLI: facturar desde su terminal > Inicie sesión por el navegador, deje el SDK listo en su proyecto, timbre, cancele y reciba los webhooks en su computadora con un solo comando. Con --json, para agentes y scripts. El CLI de Fiscalisto viene en el paquete `fiscalisto` de npm. No hay que instalar nada: corra `npx fiscalisto` en cualquier computadora con Node 20 o más reciente. ```bash npx fiscalisto login # se abre el navegador; autorice y vuelva npx fiscalisto init # SDK, llave en .env y un ejemplo que timbra node --env-file=.env fiscalisto-ejemplo.mjs ``` En esos tres pasos su proyecto ya timbra en pruebas: sin copiar llaves y sin leer la referencia antes. ## Iniciar sesión `fiscalisto login` le muestra un código y abre `fiscalisto.com/cli`. Ahí elija su cuenta API (o cree una), revise que el código sea el mismo que en su terminal y pulse **Autorizar**. El CLI recibe una llave de pruebas nueva, con el nombre de su computadora («CLI · mi-mac»), y la guarda en `~/.config/fiscalisto/config.json`, que solo usted puede leer. | Comando | Qué hace | | --- | --- | | `fiscalisto login` | Llave de pruebas por el navegador | | `fiscalisto login --live` | Pide también una de producción; necesita la verificación en dos pasos | | `fiscalisto login --key fk_test_…` | Guarda una llave que ya tiene, sin navegador (servidores, CI) | | `fiscalisto whoami` | Cuenta, modo, timbres y emisores | | `fiscalisto logout` | Olvida las llaves en esta computadora | Las llaves del CLI aparecen en **Desarrolladores › Llaves** como cualquier otra: revóquelas ahí cuando quiera. El código del navegador vence en 10 minutos y sirve una sola vez. El CLI usa, en este orden: `--api-key`, la variable `FISCALISTO_API_KEY` y la llave guardada. Con `--live` usa la de producción guardada y le pregunta antes de timbrar (`--yes` no pregunta). ## Dejar su proyecto listo `fiscalisto init` reconoce su proyecto (`package.json`, `pyproject.toml` o `requirements.txt`, `composer.json`, `go.mod`, `pom.xml` o `build.gradle`, un `.csproj`) y: 1. Pone `FISCALISTO_API_KEY` en `.env` y agrega `.env` a `.gitignore`. 2. Instala el [SDK](https://fiscalisto.com/docs/api/sdk) con su gestor (npm, pnpm, yarn, bun, pip, uv, poetry, composer, `go get` o `dotnet add package`) y lo anota en sus dependencias (con pip, en `requirements.txt`). En Java le dice qué dependencia agregar a su `pom.xml` o `build.gradle`. 3. Escribe un ejemplo que timbra una factura de prueba y le dice cómo correrlo. Con `--lang node|python|php|go|dotnet|java` elige el lenguaje, con `--no-install` no instala nada y con `--agents` agrega a su `AGENTS.md` (o `CLAUDE.md`) una sección de cómo facturar y deja la guía completa en `.claude/skills/fiscalisto/SKILL.md`, para que su agente de código use el SDK, la idempotencia y los [RFC de prueba](https://fiscalisto.com/docs/api/pruebas-y-produccion#provocar-cada-rechazo-del-sat) sin que se lo explique. Vea [Fiscalisto para agentes de IA](https://fiscalisto.com/docs/api/agentes). ## Facturas ```bash fiscalisto invoices create --example # una factura de prueba fiscalisto invoices create --customer ICV060329BY0 --name "INMOBILIARIA CVA" \ --regime 601 --zip 33826 --item "Consultoría|80101500|1000" fiscalisto invoices create --data @factura.json # el mismo cuerpo que POST /v1/invoices fiscalisto invoices list --status valid --customer ICV060329BY0 fiscalisto invoices get inv_… fiscalisto invoices pdf inv_… -o factura.pdf # también xml; sin -o, inv_….pdf fiscalisto invoices send inv_… --to cliente@empresa.mx fiscalisto invoices cancel inv_… --motive 02 ``` Cada `create` lleva una [Idempotency-Key](https://fiscalisto.com/docs/api/idempotencia): si se corta la conexión y lo repite con `--idempotency-key`, no se timbra dos veces. Un rechazo del SAT se ve con su código y su explicación, como en la API. `fiscalisto sat-lists check RFC [RFC…]` revisa uno o varios RFC en las [listas del SAT](https://fiscalisto.com/docs/api/listas-del-sat). ## Webhooks en su computadora ```bash fiscalisto listen --forward-to http://localhost:3000/webhooks ``` `listen` sigue los eventos de su cuenta y se los manda a su servidor local, firmados igual que los [webhooks](https://fiscalisto.com/docs/api/webhooks) de verdad (`webhook-id`, `webhook-timestamp`, `webhook-signature`). La firma usa un secreto de esta computadora, que el CLI le muestra al empezar y es siempre el mismo: póngalo en `FISCALISTO_WEBHOOK_SECRET` de su app y su verificación funciona sin cambios. Con `--events invoice.stamped,invoice.failed` solo reenvía esos. Si su servidor no contesta (lo está reiniciando), `listen` reintenta cada 3 segundos hasta un minuto, en orden, y no se salta eventos. Lo que `listen` lee cada 2 segundos no llena **Solicitudes** ni la gráfica de **Uso**. En otra terminal, provoque el evento que quiere probar: | Comando | Lo que pasa en pruebas | | --- | --- | | `fiscalisto trigger invoice.stamped` | Timbra una factura de prueba | | `fiscalisto trigger invoice.failed` | El SAT la rechaza (con el RFC de prueba `NOM010101FK9`) | | `fiscalisto trigger invoice.canceled` | Timbra una y la cancela | | `fiscalisto trigger invoice_batch.completed` | Un lote de dos facturas | `trigger` solo funciona con llaves de pruebas. Para mandar a un endpoint cualquier otro evento, sin timbrar, use `fiscalisto webhooks test whe_… --type invoice.paid` (es [**Mandar evento de prueba**](https://fiscalisto.com/docs/api/webhooks#un-evento-de-prueba-sin-timbrar)). `fiscalisto webhooks list|create|delete` y `fiscalisto events list|get` hacen lo mismo que el panel. ## Cualquier ruta de la API ```bash fiscalisto api /v1/account fiscalisto api POST /v1/customers --data '{"tax_id":"ICV060329BY0","legal_name":"INMOBILIARIA CVA","tax_system":"601","zip":"33826"}' fiscalisto api /v1/invoices/inv_…/pdf > factura.pdf ``` `fiscalisto issuers list` muestra sus emisores y `fiscalisto issuers get iss_…` uno con su estado. `api` llama cualquier ruta de la [referencia](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice) con su llave. `--data` acepta JSON, `@archivo.json` o `-` (lo lee de la entrada). ## La API sin internet `fiscalisto mock` levanta en su computadora un servidor que contesta cada ruta como la API, con la misma forma de cada objeto y de cada error, sin internet ni llave real. Sirve para desarrollar en un avión y para las pruebas de su proyecto (en CI, sin red). ```bash npx fiscalisto mock --port 12111 ``` - Acepta cualquier llave `fk_test_` (`fk_test_mock`). - Lo que crea queda en memoria mientras corre: un cliente creado se lee, se lista, se cambia y se borra. Una factura se crea `valid`, con su PDF y su XML de ejemplo. - Repetir un `POST` con la misma `Idempotency-Key` contesta lo mismo, como la API. - No revisa las reglas del SAT ni timbra. Antes de producción, pruebe contra la API con su llave `fk_test_` (el modo de pruebas es gratis). Apunte su SDK a él con la dirección de la API: `baseUrl` en TypeScript, `base_url` en Python y PHP, `WithBaseURL` en Go, `.baseUrl` en Java y `BaseUrl` en .NET. En las pruebas de Node, sin abrir un puerto: ```ts import { Fiscalisto } from "fiscalisto"; import { createMock } from "fiscalisto/mock"; const mock = createMock(); const fiscalisto = new Fiscalisto("fk_test_mock", { baseUrl: "http://mock", fetch: (url, init) => mock.fetch(new Request(url, init)), }); ``` ## Para agentes y scripts Con `--json` cada comando contesta un solo JSON (y `listen` una línea por evento), los errores incluidos: `{"error": {"type", "code", "message", "param"}}`, iguales a los de la API. El CLI sale con código 0 si todo salió bien, 2 si el comando está mal escrito y 1 en cualquier otro error. ```bash fiscalisto invoices create --example --json | jq -r .uuid ``` ### La documentación en su agente Agregue la documentación como servidor MCP y su agente de código la consulta mientras programa (`get_started`, `search_docs`, `get_page`, `list_pages`), sin llave: ```bash claude mcp add --transport http fiscalisto-docs https://fiscalisto.com/docs/api/mcp ``` Además de buscar y leer las guías (`search_docs`, `get_page`, `list_pages`), el servidor explora la referencia de la API: | Herramienta | Qué hace | | --- | --- | | `get_started` | La guía de integración completa en una página ([SKILL.md](https://fiscalisto.com/docs/api/SKILL.md)). | | `list_operations` | Cada operación con su `operationId`, método y ruta, por sección (`tag`). | | `describe_operation` | Una operación por su `operationId`, su método del SDK (`invoices.create`), «POST /v1/invoices» o en palabras («create invoice»): parámetros, cada campo del cuerpo y de la respuesta, ejemplos y el código con el SDK del `language` que elija (`ts`, `python`, `php`, `csharp`, `go`, `java`). | | `describe_object` | Los campos de un objeto (`Invoice`, `Event`…) con un ejemplo. | | `explain_error` | Qué significa un `error.code` y cómo resolverlo. | | `call_api` | Prueba una solicitud de verdad en modo de pruebas con su llave `fk_test_` (`api_key`) y devuelve el código, el `Request-Id` y el JSON. Nunca acepta una `fk_live_`. | Así su agente puede leer la ruta, armar el cuerpo, probarlo contra su modo de pruebas y corregirlo con el error antes de escribir el código. La solicitud queda en **Desarrolladores › Solicitudes** con la llave que usó. En Cursor, Codex, VS Code u otro cliente, use la misma dirección como servidor MCP de tipo HTTP ([cómo, en cada uno](https://fiscalisto.com/docs/api/agentes#conectar-la-documentación-mcp)). Cada página tiene además **Copiar como Markdown**, **Abrir en ChatGPT** y **Abrir en Claude** arriba del título. `fiscalisto docs` imprime la guía completa para agentes ([llms.txt](https://fiscalisto.com/docs/api/llms.txt)) y `fiscalisto open llaves` (o `panel`, `facturas`, `webhooks`, `eventos`, `docs`) abre esa página del panel. --- # Fiscalisto para agentes de IA > Pídale a su agente de código «agrega facturación con Fiscalisto a mi app»: conecte la documentación como servidor MCP, dele la guía en una página y deje que pruebe en modo de pruebas antes de escribir el código. Claude Code, Cursor, Codex, Copilot y los demás agentes de código pueden integrar Fiscalisto solos si leen lo correcto. Esta página dice cómo darles la documentación, la guía de integración y una forma de probar, para que el código salga bien a la primera: con el SDK oficial, una `Idempotency-Key` por comprobante, los datos del cliente como el SAT los pide y los webhooks verificados. ## Lo más rápido En la carpeta de su proyecto: ```bash npx fiscalisto login # su llave de pruebas, por el navegador npx fiscalisto init --agents # SDK, llave en .env, AGENTS.md y la guía para su agente ``` `--agents` agrega a su `AGENTS.md` (o `CLAUDE.md`) una sección de cómo facturar y deja la guía completa en `.claude/skills/fiscalisto/SKILL.md`, que Claude Code y otros agentes leen solos cuando la tarea es facturar. Después pídale a su agente, por ejemplo: «Agrega facturación con Fiscalisto: cuando un pedido se pague, timbra su factura y mándasela al cliente». ## Conectar la documentación (MCP) El servidor MCP de esta documentación no pide llave ni cuenta. Su agente busca y lee las guías, describe cada ruta con el código de su lenguaje y prueba solicitudes en modo de pruebas mientras programa. - **Cursor**: [instalar con un clic](cursor://anysphere.cursor-deeplink/mcp/install?name=fiscalisto-docs&config=eyJ1cmwiOiJodHRwczovL2Zpc2NhbGlzdG8uY29tL2RvY3MvYXBpL21jcCJ9). - **VS Code**: [instalar con un clic](https://vscode.dev/redirect/mcp/install?name=fiscalisto-docs&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Ffiscalisto.com%2Fdocs%2Fapi%2Fmcp%22%7D). - **Claude Code**: `claude mcp add --transport http fiscalisto-docs https://fiscalisto.com/docs/api/mcp` - **Codex**: `codex mcp add fiscalisto-docs --url https://fiscalisto.com/docs/api/mcp` - **Claude** (web y escritorio): en [Personalizar › Conectores](https://claude.ai/customize/connectors) › Agregar conector personalizado, con la dirección `https://fiscalisto.com/docs/api/mcp`. - **Otro cliente**: la dirección `https://fiscalisto.com/docs/api/mcp` como servidor MCP de tipo HTTP. ### A mano, en cada agente La dirección es `https://fiscalisto.com/docs/api/mcp` (MCP por HTTP, sin llave). #### Cursor **Instalar** abre Cursor y agrega el servidor. A mano, en `.cursor/mcp.json` de su proyecto (o `~/.cursor/mcp.json` para todos): ```json { "mcpServers": { "fiscalisto-docs": { "url": "https://fiscalisto.com/docs/api/mcp" } } } ``` #### VS Code **Instalar** abre VS Code y agrega el servidor para Copilot. A mano, en `.vscode/mcp.json`: ```json { "servers": { "fiscalisto-docs": { "type": "http", "url": "https://fiscalisto.com/docs/api/mcp" } } } ``` #### Claude Code ```bash claude mcp add --transport http fiscalisto-docs https://fiscalisto.com/docs/api/mcp ``` Con `--scope project` queda en el `.mcp.json` del proyecto, para todo su equipo. #### Codex ```bash codex mcp add fiscalisto-docs --url https://fiscalisto.com/docs/api/mcp ``` El CLI, la extensión de Codex y la app de ChatGPT para escritorio comparten esta configuración. #### Claude (web y escritorio) En [Personalizar › Conectores](https://claude.ai/customize/connectors), **Agregar conector personalizado** con la dirección `https://fiscalisto.com/docs/api/mcp`. #### Otro cliente Agregue la dirección como servidor MCP de tipo HTTP (streamable HTTP). ### Sus herramientas | Herramienta | Qué hace | | --- | --- | | `get_started` | La guía de integración completa en una página. Su agente la pide primero. | | `search_docs` | Busca en las guías y la referencia, en español o en inglés. | | `get_page`, `list_pages` | Lee una página completa o las lista todas. | | `list_operations` | Cada operación con su `operationId`, método y ruta, por sección. | | `describe_operation` | Una operación por su `operationId`, su método del SDK (`invoices.create`), «POST /v1/invoices» o en palabras («create invoice»): sus campos y el código con el SDK del `language` que elija. | | `describe_object` | Los campos de un objeto (`Invoice`, `Event`…) con un ejemplo. | | `explain_error` | Qué significa un `error.code` y cómo resolverlo. | | `call_api` | Prueba una solicitud de verdad en modo de pruebas con su llave `fk_test_` y devuelve el código, el `Request-Id` y el JSON. Nunca acepta una `fk_live_`. | Las solicitudes de `call_api` quedan en **Desarrolladores › Solicitudes** con la llave que usó. ## La guía en una página [`/docs/api/SKILL.md`](https://fiscalisto.com/docs/api/SKILL.md) es todo lo que un agente necesita para integrar Fiscalisto: la llave, el SDK de cada lenguaje, timbrar con `Idempotency-Key`, los datos del cliente, los rechazos del SAT, los webhooks firmados, cancelar, probar y pasar a producción. Es una [Agent Skill](https://agentskills.io): guárdela en `.claude/skills/fiscalisto/SKILL.md` de su proyecto (lo hace `npx fiscalisto init --agents`) y su agente la usa cuando la tarea es facturar. Es el mismo texto que devuelve `get_started`. ## La documentación como texto - [`/docs/api/llms.txt`](https://fiscalisto.com/docs/api/llms.txt): el índice de todas las páginas ([llmstxt.org](https://llmstxt.org)). - Cada página existe en Markdown: agregue `.md` a su dirección (por ejemplo [`/docs/api/webhooks.md`](https://fiscalisto.com/docs/api/webhooks.md)), o pídala con `Accept: text/markdown`. Cada página tiene además **Copiar como Markdown**, **Abrir en ChatGPT** y **Abrir en Claude** arriba del título. - [`/docs/api/llms-full.txt`](https://fiscalisto.com/docs/api/llms-full.txt): todas las guías y la referencia en un archivo, con los ejemplos de cURL y TypeScript. - El documento OpenAPI 3.1 completo, sin llave: [`https://api.fiscalisto.com/v1/openapi.json`](https://api.fiscalisto.com/v1/openapi.json). - `npx fiscalisto docs` imprime el índice en la terminal. ## Probar sin llave y con llave - **Sin llave:** `npx fiscalisto mock` levanta la API en `http://127.0.0.1:12111`, sin internet, para que su agente escriba y corra pruebas desde el primer minuto. Contesta como la API pero no revisa las reglas del SAT. Vea [CLI](https://fiscalisto.com/docs/api/cli). - **Con su llave de pruebas:** `call_api` del MCP, o el CLI con `--json` (`npx fiscalisto invoices create --example --json`), timbran en el ambiente de pruebas del SAT sin costo. Los [RFC de prueba](https://fiscalisto.com/docs/api/pruebas-y-produccion#provocar-cada-rechazo-del-sat) provocan cada rechazo del SAT para que su agente maneje los errores. > **Su llave.** Dele a su agente solo la llave de pruebas (`fk_test_`) y guárdela en `.env`, nunca en el código. La de producción la pone usted en su servidor cuando todo funcione. --- # n8n, Zapier y Make: facturar sin programar > Timbre facturas, registre tickets para autofactura y reciba los eventos de Fiscalisto en sus flujos de n8n, Zapier o Make, con su llave de la API y sin escribir código. Fiscalisto tiene conectores para las tres herramientas de automatización más usadas. Cada uno usa su llave de la API (`fk_test_…` para probar, `fk_live_…` para facturar de verdad) y hace lo mismo que la API: lo que timbre desde un flujo aparece en **Desarrolladores › Facturas** como cualquier otra factura, con su mismo costo por timbre. | Herramienta | Cómo se agrega | Acciones | Disparadores | | --- | --- | --- | --- | | n8n | Nodo de la comunidad `n8n-nodes-fiscalisto` | Facturas, clientes, tickets, listas del SAT y RFC | «Fiscalisto Trigger», con los eventos que elija | | Zapier (en inglés) | App «Fiscalisto», en revisión para el directorio de Zapier | «Create Invoice», «Create Customer», «Create Self-Invoicing Ticket», «Request Customer Tax Data», «Send Invoice by Email», «Find Customer», «Check RFC in SAT Lists» | «Invoice Stamped», «Invoice Rejected», «Invoice Canceled», «Ticket Invoiced», «Customer Tax Data Received» y «Low Stamp Balance» | | Make | App «Fiscalisto», en revisión para el directorio de Make | «Timbrar una factura», «Ver una factura», «Buscar facturas», «Crear un cliente», «Registrar un ticket de autofactura», «Pedirle sus datos fiscales a un cliente», «Revisar un RFC en las listas del SAT» y «Hacer una llamada a la API» (cualquier ruta) | «Eventos de Fiscalisto», con los eventos que elija | ## Conectar su cuenta 1. En **Desarrolladores › Llaves** pulse **Crear llave**. Para un flujo que solo consulta, elija **Solo lectura** en **Permisos**. 2. En su herramienta, al agregar la primera acción de Fiscalisto, pegue la llave donde dice «Llave de la API» (en Zapier, «API Key»). 3. La herramienta la prueba con `GET /v1/account` y le muestra el nombre de su cuenta. Empiece con la llave de pruebas: las facturas se timbran con un PAC de pruebas, no cuestan timbres y no valen ante el SAT. Cuando el flujo haga lo que quiere, cambie la llave por la de producción. ## Eventos Los disparadores crean un webhook en su cuenta al activar el flujo y lo borran al apagarlo; lo verá en **Desarrolladores › Webhooks** con una descripción que empieza con «n8n», «Zapier» o «Make». n8n revisa la firma de cada entrega y Zapier vuelve a leer el evento de la API con su llave antes de correr el flujo, así que nadie más puede hacerse pasar por Fiscalisto; en Make, la dirección del webhook es secreta y solo la conoce su escenario. Los eventos que existen están en [Webhooks](https://fiscalisto.com/docs/api/webhooks). ## Ejemplos - **Tienda en línea → factura.** Cuando entra un pedido pagado (Shopify, WooCommerce, Stripe), cree un ticket con `POST /v1/tickets` y mándele al comprador la liga `url` del ticket; él pone sus datos y la factura se timbra sola. Lo que nadie facture entra en la factura global. Ver [Autofactura](https://fiscalisto.com/docs/api/autofactura). - **CRM → factura.** Al ganar una venta en su CRM, busque el cliente por RFC (o créelo) y timbre la factura con sus conceptos. - **Pedir los datos fiscales.** Si no tiene el RFC del cliente, «Pedirle sus datos fiscales a un cliente» le manda una liga; cuando el cliente la llena llega `customer_data_request.completed` y el flujo sigue. Ver [Datos del cliente](https://fiscalisto.com/docs/api/datos-del-cliente). - **Avisar al equipo.** Con `invoice.failed` o `balance.low`, mande un correo o un mensaje a su equipo. - **Revisar proveedores.** Antes de pagarle a un proveedor, revise su RFC en las listas 69-B y 69 del SAT. Ver [Listas del SAT](https://fiscalisto.com/docs/api/listas-del-sat). ## Otra herramienta Cualquier herramienta que haga solicitudes HTTP sirve: use `https://api.fiscalisto.com` con el encabezado `Authorization: Bearer fk_test_…` y un cuerpo JSON como en la [referencia](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice). Para timbrar sin repetir facturas cuando la herramienta reintenta, mande también `Idempotency-Key` con un valor que no cambie entre reintentos, como el número de pedido. Ver [Idempotencia](https://fiscalisto.com/docs/api/idempotencia). --- # Cambios > Lo nuevo en la API, los SDKs y el CLI de Fiscalisto, del más reciente al más antiguo. Nada de lo que ya funciona deja de funcionar sin aviso. La API se versiona por fecha: cada respuesta dice en el encabezado `Fiscalisto-Version` en qué versión se escribió, y hoy hay una sola, `2026-10-24`. Lo que agregamos (rutas, campos, eventos) llega en la versión que ya usa, sin cambiar nada de lo que existe. Un cambio que no sea compatible será una versión nueva, anunciada aquí y por correo, y su cuenta se queda en la suya hasta que usted la mueva. Ver [Versiones](https://fiscalisto.com/docs/api/versiones). Reciba cada cambio por correo en [Avisos por correo](/avisos?tema=cambios), o en su lector con el RSS `https://fiscalisto.com/docs/api/cambios.rss`. Las fallas del servicio tienen los suyos en [Estado del servicio](/estado). ## 27 de octubre de 2026 - **La API está abierta.** Cualquier persona puede crear su cuenta API desde el selector de empresa (o desde [API de facturación](/api-facturacion)), timbrar en pruebas sin costo y pasar a producción con sus emisores y sus paquetes de timbres. Su cuenta empieza con 20 timbres de producción de regalo. Ver [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion). - **Todas las constancias de retenciones con complemento.** `POST /v1/retentions` emite ahora las claves 17 y 19 a 24 con su complemento: `trust_lease` (arrendamiento en fideicomiso), `share_sale` (enajenación de acciones), `prize` (premios), `trust` (fideicomiso no empresarial), `retirement_plan` (planes de retiro 1.1), `mortgage_interest` (intereses hipotecarios) y `financial_derivatives` (operaciones con derivados). Las claves 06 (enajenación de acciones) y 15 (remanente distribuible) ya no piden complemento, como dice el SAT. En `GET /v1/sat_catalogs/retention_codes` solo la 26 queda con `supported: false`. En los SDKs 0.1.9. Ver [Retenciones](https://fiscalisto.com/docs/api/retenciones). - **Lo declarado sin CFDI aparte.** Cada cruce de `GET /v1/accounting/cross_checks` (y de `/cfdi`) trae `declared_without_cfdi`: lo que la declaración dice que llegó sin factura (ingresos nominales adicionales en `isr_income`, IVA al 16% de cobros sin factura en `vat_charged`). Solo el resto se compara con sus CFDI, así que un hotel que declara cobros sin factura ya no sale con `differs`. En los SDKs 0.1.8. Ver [Contabilidad](https://fiscalisto.com/docs/api/contabilidad). - **`vat_withheld` cuenta las constancias de retenciones.** El IVA que le retuvieron en el cruce (`GET /v1/accounting/cross_checks`) suma también el de las constancias de retenciones que el emisor recibió, como las de las plataformas de hospedaje, en el mes en que se emitió (como se declara). El ISR de esas constancias se acredita en el ISR provisional según el régimen. En `GET /v1/accounting/cross_checks/cfdi` aparecen con `type` `withholding_certificate`. Ver [Contabilidad](https://fiscalisto.com/docs/api/contabilidad#los-cfdi-de-una-cifra). En los SDKs 0.1.7, `type` del CFDI del cruce incluye `withholding_certificate`. - **Documentación.** Cada evento tiene su página con el evento completo tal como llega a su webhook, y [Tipos de evento](https://fiscalisto.com/docs/api/webhooks#tipos-de-evento) aparece arriba de Webhooks y Eventos en la referencia, con cada evento enlazado a su página. **Mandar evento de prueba** (`POST /v1/webhook_endpoints/{id}/test`) manda el objeto de cada evento (una cotización, un ticket, una descarga…) en lugar de una factura. La referencia usa todo el ancho de la pantalla, con el código y la respuesta en una columna más ancha, y **Probar** ya no se abre fuera de la pantalla. - **Listo para agentes de código.** Nueva página [Fiscalisto para agentes de IA](https://fiscalisto.com/docs/api/agentes): cómo conectar el servidor MCP de la documentación en Claude Code, Cursor, VS Code y Codex, y la guía de integración en una página en [`/docs/api/SKILL.md`](https://fiscalisto.com/docs/api/SKILL.md) (una Agent Skill). El MCP tiene `get_started` (esa guía), busca también en inglés («create invoice», «verify webhook signature»), `describe_operation` acepta el método del SDK (`invoices.create`) o palabras, `list_operations` acepta la sección en inglés y `explain_error` sugiere el código parecido. `describe_operation` nombra el objeto que contesta en lugar de repetir sus campos (`full: true` los incluye). `llms-full.txt` escribe los campos de cada objeto una sola vez y pasa de 1.7 a 1.1 MB. - **SDKs 0.1.6:** `fiscalisto init --agents` deja además la guía en `.claude/skills/fiscalisto/SKILL.md`, y la sección de `AGENTS.md` apunta al MCP, al servidor de prueba y a verificar webhooks. Cada README tiene «Con agentes de IA». - **Cumplimiento SAT en la API.** Con la e.firma guardada del emisor, `POST /v1/issuers/{id}/compliance_opinion/refresh`, `…/tax_status_certificate/refresh` y `…/sat_mailbox/refresh` traen del SAT la opinión de cumplimiento (32-D), la constancia de situación fiscal y los avisos del buzón tributario (1 timbre por consulta en producción, cada uno una vez cada 10 minutos; regresa si el SAT no contesta). Se leen gratis en `GET /v1/issuers/{id}/compliance_opinion` y `/tax_status_certificate` (con `/pdf`) y en `GET /v1/sat_mailbox_messages`. Nuevo `GET /v1/sat_findings`: los hallazgos de las facturas del emisor por tipo, sin los revisados. En pruebas se inventan al instante. Ver [Cumplimiento SAT](https://fiscalisto.com/docs/api/cumplimiento-sat). - **SDKs 0.1.5:** opinión, constancia y buzón (`issuers.complianceOpinion`, `satMailboxMessages`, `satFindings`…), sucursales (`branches`), almacenes (`warehouses`), existencias (`products.stock`, `warehouses.stock`), kárdex (`stockMovements`) y ajustes (`stockAdjustments`); `branch` y `warehouse` en las facturas. La 0.1.4 trajo los filtros de eventos (`type` con comodín, `delivery_success`) y `fiscalisto events list --failed` en el CLI. - **Inventario en la API.** Los productos tienen `track_stock`, `cost`, `barcode` y `min_stock`; nuevas rutas `/v1/warehouses` (con `/stock`), `GET /v1/products/{id}/stock`, `/v1/stock_movements` (el kárdex) y `/v1/stock_adjustments`. Una factura con `product` saca sus existencias del almacén de su sucursal, o del que diga `warehouse`, y cada concepto de la factura dice su `product`. Error nuevo: `inventory_refused`. Ver [Inventario](https://fiscalisto.com/docs/api/inventario). - **Sucursales en la API.** Nuevas rutas `/v1/branches` para crear, ver, cambiar, archivar y borrar las sucursales de un emisor (cada una con su código postal, su dirección para el PDF y sus series), y `branch` en `POST /v1/invoices`: la factura sale con el `LugarExpedicion`, la hora y la serie de esa sucursal, y sus pagos y notas de crédito la siguen. El objeto de factura dice su `branch`. Errores nuevos: `branch_exists` y `branch_has_invoices`. Ver [Sucursales](https://fiscalisto.com/docs/api/sucursales). - **Errores más claros.** `stamp_rate_limit` dice cuánto esperar y que repita con la misma `Idempotency-Key`; `idempotency_key_missing` ya no habla solo de facturas (también nómina, retenciones y lotes); un webhook con `http://` o `localhost` le sugiere `fiscalisto listen`. - **CLI.** `--help` funciona en cada comando (`fiscalisto invoices create --help`); `invoices create` con `--customer` pide `--payment-form` en lugar del error de la API; `issuers get` se lee sin `--json`; los montos en dólares ya no repiten la moneda, y los complementos de pago muestran lo que pagaron en lugar de $0.00. - **Panel.** Un webhook cuya última entrega falló dice **Con errores**, y los complementos de pago muestran lo que pagaron. - **`GET /v1/events` filtra como Stripe.** `type` acepta un comodín por recurso (`invoice.*`, `retention.*`) y el nuevo `delivery_success=false` deja los eventos que algún webhook no recibió (`true`, los que llegaron a todos). Ver [Webhooks](https://fiscalisto.com/docs/api/webhooks#reintentos). - **Reenviar es un intento aparte.** Un reenvío (en el panel) se manda una vez, ahora, sin empezar otra ronda de reintentos; si llega, los reintentos automáticos que faltaban ya no se mandan. - **Webhooks en el panel, como Stripe.** **Desarrolladores › Webhooks** muestra la lista y el webhook elegido con sus últimos 7 días, sus entregas (filtrables y con enlace propio) y cada intento; **Nuevo webhook** elige primero los eventos, por recurso y con un ejemplo de cada uno. Un webhook de pruebas ya no pide su contraseña para verlo o agregarlo. El Workbench cambia de alto, se abre en pantalla completa, copia un enlace a lo que está viendo y filtra solicitudes (método, ruta, código de error) y eventos. ## 26 de octubre de 2026 - **Nuevos precios de los paquetes grandes.** 10,000 timbres: $4,790 + IVA ($5,556.40 con IVA); 50,000: $22,900 + IVA ($26,564 con IVA); 100,000: $41,900 + IVA ($48,604 con IVA). Los de 500 y 2,000 no cambian, y los paquetes ya comprados conservan sus timbres. Ver **Desarrolladores › Timbres**. - **Los paquetes de 50,000 y 100,000 timbres se pagan solo por transferencia (SPEI).** Los de 500, 2,000 y 10,000 siguen con tarjeta, OXXO o transferencia, y la **Recarga automática** con tarjeta compra paquetes de hasta 10,000. - **Nuevo `GET /v1/accounting/cross_checks/cfdi`**: los CFDI que forman lo facturado de una cifra del cruce de un mes (`month`, `key`, también las retenciones de nómina y a proveedores), con lo que aporta cada uno y el ajuste de Impuestos, para ver por qué una cifra `differs`. Ver [Contabilidad](https://fiscalisto.com/docs/api/contabilidad#los-cfdi-de-una-cifra). - **Facturas en pesos colombianos, florines, rupias y dinares iraquíes con los decimales de c_Moneda.** `COP`, `HUF`, `IDR`, `IRR`, `LBP` y otras se timbraban sin decimales (y `IQD` con 0 en vez de 3), y el PAC las rechazaba por el rango de los impuestos; ahora van con los decimales del catálogo del SAT. - **El motivo de cancelación `04` es solo para una factura global** (una venta incluida se facturó después a nombre del cliente). En otra factura contesta `cancellation_failed`. - **Cancelar una constancia con el motivo `01`** revisa que `substitution` sea otra constancia vigente del mismo emisor. - **«Probar» en la referencia sin llave** funciona solo para quien administra las llaves de la cuenta API (como el panel), y nunca cambia la versión de la API de la cuenta. - **Guías nuevas:** [Nómina](https://fiscalisto.com/docs/api/nomina) y [Constancias de retenciones](https://fiscalisto.com/docs/api/retenciones). La documentación tiene buscador, y cada operación de la referencia dice qué hace. - **SDKs 0.1.3:** `fiscalisto init` también deja listo un proyecto de Go, .NET o Java, y `fiscalisto docs` lee la guía de la API para agentes. - **`GET /v1/quotes?status=` ya no corta la lista antes de tiempo.** Con muchas cotizaciones, solo revisaba las 500 más recientes y contestaba `has_more: false` aunque hubiera más con ese estado; ahora sigue hasta llenar la página. - **`GET /v1/issuers` con una llave limitada a algunos emisores** trae páginas completas; antes filtraba después de leer la página y podía contestar una lista vacía con `has_more: true`. - **Dos `POST /v1/retentions/:id/send` a la vez ya no mandan la constancia dos veces**: el segundo contesta `email_failed` («se está enviando»). - **Una constancia de retenciones que el SAT ya canceló siempre queda cancelada**, aunque falle el registro interno del uso. - **Los números de las pólizas (`D-202610-00001`) ya no cambian entre consultas** cuando dos son del mismo día y tipo. Un CFDI del mes que llega después sí recorre los que siguen: para relacionarlas use `uuid`. - **`summary` lee las declaraciones de RESICO de personas físicas** (ingresos percibidos, impuesto mensual, ISR retenido, IVA acreditable del periodo) y más cifras de personas morales tal como las imprime el SAT (IVA retenido, actos al 0% y exentos). Las declaraciones ya guardadas se vuelven a leer de su PDF en la siguiente consulta, sin ir al SAT. - **El cruce con las retenciones.** `GET /v1/accounting/cross_checks` también compara, en los meses en que hubo, el ISR retenido por salarios contra los recibos de nómina (`isr_withheld_salaries`) y el ISR e IVA retenidos a proveedores contra las facturas recibidas (`isr_withheld_suppliers`, `vat_withheld_suppliers`). Ver [Contabilidad](https://fiscalisto.com/docs/api/contabilidad). - **Un campo obligatorio vacío dice «no puede estar vacío»** (antes «debe tener al menos 1 caracteres»). - **Los webhooks ya no se pierden cuando llegan muchos cambios juntos.** Más de 12 eventos a la vez (un lote grande, por ejemplo) no se guardaban, y con muchos destinos que fallaban se dejaban de reintentar las entregas. - **Las listas con `limit=100` ya no fallan con muchos registros.** `GET /v1/invoices?limit=100` contestaba `500` en cuanto el emisor tenía más de 100 facturas (y lo mismo otras listas y los eventos con muchos objetos); ahora contesta la página completa. Las páginas siguientes (`starting_after`) tardan lo mismo al principio que al final de la lista. - **Con `Accept-Language: en`, los errores de `limit` y de las fechas (`created_gte`…) dicen en inglés exactamente qué falló**, en vez del mensaje general del código. - **`GET /v1/sat_catalogs/product_keys` y `unit_keys` ya no contestan `catalog_unavailable` por un fallo de red**: el catálogo se lee de los archivos de la propia aplicación. - **Las cifras de cada declaración con nombres fijos.** Cada `SatDeclaration` trae `summary`: sus obligaciones (`isr`, `vat`, `isr_salaries`…) con cifras que no cambian de nombre aunque el SAT cambie su formato (`tax_base`, `creditable`, `withheld`, `to_pay`…), `null` donde no se pudo leer. Ver [Las cifras con nombres fijos](https://fiscalisto.com/docs/api/declaraciones-del-sat#las-cifras-con-nombres-fijos). - **El año de declaraciones mes por mes.** `GET /v1/sat_declarations/calendar` dice por mes qué obligaciones se presentaron, cuáles faltan y cuándo vencen las que siguen. `GET /v1/sat_declarations` filtra también por `month` y `type`. - **Webhooks de declaraciones:** `sat_declaration.created` al encontrar una nueva y `sat_declaration.updated` al llegar sus archivos o cifras. - **Contabilidad completa.** Las pólizas (`GET /v1/accounting/journal_entries`) ahora incluyen los pagos de los complementos, la nómina y la depreciación de los activos fijos, y traen `type` (`invoice`, `payment_received`, `payment_made`, `payroll` o `depreciation`). Un PUE se registra cobrado o pagado según su forma de pago. `uuid` y `tax_id` son `null` en la póliza de depreciación. Nuevo `GET /v1/accounting/financial_statements` con el estado de resultados y el balance general del mes, y `GET /v1/accounting/cross_checks`, que compara cada mes lo declarado al SAT con lo facturado. Ver [Contabilidad](https://fiscalisto.com/docs/api/contabilidad). - **`{periodo}` en una factura recurrente sigue su frecuencia.** En una semanal dice la semana que cubre (`2 al 8 de noviembre`) y en una de varios meses todos sus meses (`1 de noviembre al 31 de diciembre`); antes siempre decía el mes de la fecha. En las mensuales no cambia. - **Los errores de los campos de una factura llegan en español y con el nombre de la API.** Antes algunos llegaban en inglés (`Invalid input: expected number, received string`, `Too big: expected array to have <=200 items`); ahora dicen, por ejemplo, `items.0.quantity: debe ser un número`, y los campos con formato fijo dicen cuál (`date` es `AAAA-MM-DDTHH:MM:SS` en hora de la Ciudad de México, `product_key` son 8 dígitos). - **`currency` se revisa contra c_Moneda.** Un código que no existe (`XYZ`) contesta `400` con `param: "currency"`, uno en minúsculas (`usd`) se guarda en mayúsculas, y `exchange_rate` distinto de 1 en pesos (MXN) contesta `400`; antes se timbraba tal cual. - **`send_email: true` sin correo contesta `400` `parameter_missing`** con `param: "customer.email"`, como ya hacían los pagos. Antes la factura se timbraba y no se mandaba a nadie. Lo mismo al crear una factura recurrente, donde `send_email` es `true` por omisión: sin correo, mande `invoice.customer.email` o `send_email: false`. - **`metadata` se revisa también en los anticipos**, y un error en ella dice la llave (`metadata.pedido`). - **Constancias de retenciones:** un periodo que termina después del mes en curso contesta `retention_invalid` (la constancia es de lo ya retenido); `dividends`, `interest` y `foreign_payment` con otra clave (`code` 14, 16 y 18) contestan `400` en vez de no timbrarse, y `totals.exempt` mayor que `totals.operation` dice eso en vez de «los montos no pueden ser negativos». - **Recibos de nómina:** una fecha que no existe (`2026-02-30`) contesta `400` con `param` (antes se leía como el 2 de marzo); un periodo más largo que la periodicidad del empleado (un mes en una nómina semanal) y un periodo que se cruza con otro recibo vigente del empleado contestan `payroll_invalid`, para no pagar dos veces los mismos días. Pedir el mismo periodo otra vez sigue contestando el recibo que ya tiene. Los errores de empleados dicen `end_date`, `start_date` o `integrated_daily_salary` cuando son de ese campo. - **Descargas del SAT:** la misma descarga pedida otra vez mientras el SAT trabaja en ella contesta esa misma, sin cobrar otro timbre ni gastar las solicitudes idénticas que acepta el SAT. `counterparty_tax_id` debe ser un RFC, y en `GET /v1/sat_invoices` `date_gte` después de `date_lte` contesta `400` en vez de una lista vacía. - **Fechas que no existen.** Un día como `2026-02-30` contesta `400` («no es un día del calendario») en todos los campos y filtros de fecha; antes algunos lo leían como el 2 de marzo. - **Un webhook por dirección.** Crear un endpoint con la `url` de otro (o cambiarle la `url` a la de otro) contesta `400` `endpoint_exists` con el id del que ya la tiene; antes había dos y cada evento llegaba dos veces, por ejemplo al correr dos veces un script de instalación. - **Claves del SAT revisadas antes del PAC.** Un `product_key` o `unit_key` que no está en c_ClaveProdServ o c_ClaveUnidad contesta `400` con su campo (`items.1.product_key` en una factura), en productos y en facturas; un producto acepta `unit_price` con hasta 6 decimales, como el CFDI, y con `unit_key` sin `unit_name` toma el nombre del catálogo. - **Clientes revisados al guardarlos.** Un régimen que no es del tipo de persona del RFC (601 a un RFC de 13 caracteres), un régimen o código postal que no está en el catálogo, o un `use` que no va con el régimen contestan `400` con su campo, en vez de guardarse y fallar al facturar. Cambiar solo el correo de un cliente se guarda como antes. - **SDKs 0.1.2** en los siete paquetes (npm `fiscalisto` y `@fiscalisto/react`, PyPI, Packagist, NuGet, Go y Maven Central), con todo lo de esta fecha y del 25 de octubre: los códigos de error nuevos (como `endpoint_exists`), versiones por fecha, `expand[]` y errores en inglés. Desde ahora cada SDK se publica solo al cambiar la API. - **Un lote no acepta la misma `reference` dos veces**, para que un pedido no se facture doble: contesta `400` con `param` en la repetida y dice en qué factura del lote ya iba. - **Una factura recurrente que no se pudo timbrar dice por qué con el código correcto:** `invoice_invalid` cuando Fiscalisto la detuvo antes del PAC (un cliente borrado del catálogo, el plan), `insufficient_balance` sin timbres, e `invoice_rejected` solo cuando la rechazó el PAC o el SAT. - **Declaraciones del SAT.** `POST /v1/sat_declarations/refresh` entra al SAT con la e.firma guardada del emisor y trae las declaraciones que presentó (provisionales, la anual de persona moral y las de Declaraciones y pagos), con su PDF, su acuse, sus cifras y el coeficiente de utilidad. En producción usa 1 timbre por consulta, una vez cada 10 minutos por emisor; listarlas con `GET /v1/sat_declarations` y bajar sus archivos es gratis. En pruebas trae declaraciones de ejemplo al instante. Ver [Declaraciones del SAT](https://fiscalisto.com/docs/api/declaraciones-del-sat). - **Varias notas de crédito o pagos a la misma factura a la vez se atienden en orden.** Antes, las que llegaban mientras otra se timbraba recibían `409` y un mensaje que culpaba a la `Idempotency-Key`; ahora esperan su turno (hasta 12 segundos) y se revisan contra el saldo que dejó la anterior. - **`invoice_invalid` para las reglas que Fiscalisto revisa antes del PAC.** Un pago que excede el saldo, una nota mayor que lo que queda o una factura cancelada contestaban `invoice_rejected`, que es para lo que rechaza el PAC o el SAT. - **Por qué no se puede pagar una factura.** En vez de «no está abierta para este cliente», el error dice si está cancelada, si es PUE, si es de otro cliente o si ya está pagada. - **La misma solicitud escrita de otra forma es la misma.** Con una `Idempotency-Key`, el cuerpo se compara como JSON y no byte por byte: el curl con sangría de la guía, un SDK compacto, el `json.dumps` de Python o los campos en otro orden ya no contestan `idempotency_key_reused`. Los parámetros de la URL también se comparan sin importar su orden. - **Una solicitud repetida contesta con el mismo status.** Repetir un timbrado ya hecho contesta `201` (antes `200`), un lote `202` y una cotización ya facturada `201`, siempre con `idempotent-replayed: true` y con los mismos avisos (`warnings`) que la primera vez. - **Errores más claros.** Un campo obligatorio que falta dice `parameter_missing` aunque acepte varias formas, `metadata` con otro tipo dice «debe ser un objeto», y un valor enorme (un id de 5,000 caracteres) ya no regresa completo en el mensaje. - **Dos cambios al mismo objeto a la vez ya no se pisan.** Dos actualizaciones del mismo producto o cliente al mismo tiempo (una cambia `sku`, otra `unit_price`) guardaban solo una; ahora la segunda espera a la primera y se guardan las dos. Si espera más de 15 segundos contesta `409` `request_in_progress`. - **Un parámetro de la URL que no existe se rechaza.** `GET /v1/invoices?statuss=valid` (con una s de más) contestaba la lista completa sin filtrar; ahora contesta `400` `parameter_unknown` con el parámetro y los que sí acepta ese endpoint. - **Los entornos de prueba** no pasan de 25 aunque se creen muchos a la vez. - **`customer` en las listas acepta el id del cliente.** `GET /v1/invoices`, `/v1/receivables` y `/v1/advances` filtran por `customer=RFC` o `customer=cus_…`, el RFC en mayúsculas o minúsculas. Antes un `cus_…` contestaba una lista vacía, y un valor que no es RFC ni cliente ahora contesta `400` en vez de una lista vacía. - **Clientes, productos y empleados traen `created_at`**, como los demás objetos. En los clientes y productos de antes de hoy es la fecha de su último cambio. **La lista de empleados** sale de la más nueva a la más vieja, como las demás (antes salía en un orden que parecía al azar). - **Un SKU es de un solo producto.** Crear o cambiar un producto con el SKU de otro contesta `400` `customer_exists` con `param: "sku"` y el nombre del que lo tiene, como ya decía la referencia de errores; antes se guardaban dos productos con el mismo SKU. - **Repetir un timbrado ya hecho varias veces a la vez contesta la factura a todas.** Antes, mientras una repetición contestaba, las demás que llegaban en ese momento recibían `409` `request_in_progress`. - **Pruebas acepta 100 solicitudes cada 10 segundos, como producción** (antes 25), para que una suite de pruebas en paralelo no se tope antes con el límite. Los timbrados de prueba siguen con su propio límite. - **El texto que contesta la factura es el que se timbró.** Saltos de línea, tabuladores y espacios de más en descripciones y nombres se guardan como un espacio, y los caracteres de control se quitan, como ya salían en el XML; antes el JSON y el PDF los conservaban. - **`npx fiscalisto mock`** compara la `Idempotency-Key` igual que la API y contesta `409` cuando la llave ya se usó con otro cuerpo. ## 25 de octubre de 2026 - **Cancelar ya no usa timbres.** Cancelar facturas y constancias de retenciones es gratis, en la API y en la app. - **Las descargas del SAT pagan según su tamaño.** En producción, una descarga usa 1 timbre por cada 100 CFDI que trae (al menos 1): el primero al pedirla y los demás cuando el SAT dice cuántos tiene, antes de descargarlos. Sin timbres suficientes termina en `failed`. Ver [Límites](https://fiscalisto.com/docs/api/limites#lo-que-usa-timbres-sin-timbrar). - **Paquete de 100,000 timbres.** $36,900 + IVA ($42,804 con IVA), unos 37 centavos más IVA por timbre, en **Desarrolladores › Timbres**. - **Revisar RFC, consultar constancias y descargar del SAT usan timbres.** En producción, revisar RFC en las listas del SAT y consultar constancias usan 1 timbre por cada 10, y cada descarga pedida al SAT usa 1. Sin timbres, `402 insufficient_balance`. En pruebas sigue sin costo. Ver [Límites](https://fiscalisto.com/docs/api/limites#lo-que-usa-timbres-sin-timbrar). - **Precio abierto e impuestos locales en productos.** Los productos aceptan `open_price` (sin precio fijo: cada concepto con él manda su `unit_price`, o la factura se rechaza con `items.N.unit_price`) y `local_taxes`, como el ISH de hospedaje. Una factura con esos productos y sin `local_taxes` los agrega sola, calculados sobre los conceptos de cada producto. Ver [Complementos](https://fiscalisto.com/docs/api/complementos#ejemplo-hospedaje-con-ish). - **Workbench.** En cualquier página de Desarrolladores, la tecla `` ` `` abre un panel con el resumen de la cuenta, las solicitudes, los eventos, los webhooks, un inspector de objetos y un shell que llama a la API en pruebas, sin dejar la página. - **Inspector de solicitudes.** En **Desarrolladores › Solicitudes**, cada solicitud dice su versión de la API y se ve **Como código** en curl o en cualquiera de los seis SDKs; las repetidas con la misma `Idempotency-Key` dicen **Repetida** y aparecen junto a la original. - **La API sin internet.** `npx fiscalisto mock` contesta cada ruta en su computadora con la forma de la API, guarda en memoria lo que crea y repite lo mismo con la misma `Idempotency-Key`; en las pruebas de Node, `createMock()` de `fiscalisto/mock`. Ver [CLI](https://fiscalisto.com/docs/api/cli#la-api-sin-internet). - **Errores en inglés.** Con `Accept-Language: en` (o la opción `language` de los SDKs) los mensajes de error llegan en inglés, con el original en `message_es` cuando el inglés es general. Ver [Errores](https://fiscalisto.com/docs/api/errores#en-inglés). - **Expandir objetos.** Con `expand[]=issuer` (o `recurring_invoice.last_invoice`, `data.invoice`…) un id llega como el objeto completo en la misma respuesta, en `GET` y en `POST`. En los SDKs de TypeScript, Python y PHP, con la opción `expand`. Ver [Expandir objetos](https://fiscalisto.com/docs/api/expandir). - **Versiones por fecha.** Cada cuenta queda en la versión de la API del día en que se creó (`api_version` en `GET /v1/account`) y se mueve solo con `POST /v1/account`. Una solicitud puede pedir otra con el encabezado `Fiscalisto-Version`, y cada endpoint de webhooks puede tener la suya. Los seis SDKs mandan la versión para la que se hicieron. Ver [Versiones](https://fiscalisto.com/docs/api/versiones). - **Relojes de prueba.** `POST /v1/issuers/{id}/test_clock` congela la hora de un emisor de pruebas y `POST /v1/issuers/{id}/test_clock/advance` la adelanta, haciendo día por día lo que habría pasado: facturas recurrentes, cotizaciones que vencen y facturas globales de tickets, con sus webhooks y el evento `test_clock.ready`. Los emisores traen `test_clock`. Ver [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion#adelantar-el-tiempo-con-un-reloj-de-prueba). - **Idempotency-Key en todo `POST`.** Como en Stripe y Resend: con la misma llave, un reintento de crear un cliente, un webhook, una cotización o una factura recurrente, de mandar un correo o de cualquier otro `POST` contesta lo mismo (`idempotent-replayed: true`) sin hacerlo dos veces, durante 24 horas. Los SDKs, la CLI y los módulos de n8n, Zapier y Make la ponen solos en cada `POST`, así sus reintentos siempre son seguros. Ver [Idempotencia](https://fiscalisto.com/docs/api/idempotencia). - **Explorador de la API para agentes.** El servidor MCP de la documentación (`https://fiscalisto.com/docs/api/mcp`) agrega `list_operations`, `describe_operation` (con el código del SDK del lenguaje que pida), `describe_object`, `explain_error` y `call_api`, que prueba una solicitud en modo de pruebas con su llave `fk_test_`. Ver [CLI](https://fiscalisto.com/docs/api/cli#la-documentación-en-su-agente). - **Entornos de prueba.** `POST /v1/sandboxes` le da a cada persona del equipo su propio modo de pruebas: un emisor de pruebas del SAT y una llave `fk_test_` que solo ve sus emisores, facturas, webhooks y eventos, con un reinicio que no toca los de nadie más. Emisores, webhooks y `GET /v1/account` (`key.sandbox`) agregan `sandbox`. También en **Desarrolladores › Llaves**. Ver [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion#un-entorno-de-prueba-para-cada-persona). - **Contabilidad.** `GET /v1/accounting/journal_entries` da una póliza por cada CFDI del mes, `GET /v1/accounting/trial_balance` la balanza y `GET /v1/accounting/accounts` el catálogo con el código agrupador del SAT. `GET /v1/accounting/electronic_accounting` descarga la contabilidad electrónica (catálogo, balanza y, si el SAT las pide, pólizas) lista para el Buzón Tributario, y `GET /v1/accounting/diot/file` el TXT de la DIOT. Ver [Contabilidad](https://fiscalisto.com/docs/api/contabilidad). - **Tiendas en línea.** `POST /v1/store_connections` conecta una tienda de Shopify o una cuenta de Mercado Libre: cada pedido pagado se vuelve un ticket de autofactura, la liga va en el pedido de Shopify, en Mercado Libre se factura sola con los datos fiscales del comprador y el CFDI se sube al pedido. Un pedido cancelado o reembolsado cancela su ticket. Para WooCommerce, el plugin **Fiscalisto Autofactura**. Ver [Tiendas en línea](https://fiscalisto.com/docs/api/tiendas). - **Conciliación bancaria.** `POST /v1/bank_transactions` recibe los depósitos de la cuenta del emisor (de la API de su banco o de un estado de cuenta), sin repetir los que ya mandó, y sugiere la factura que pagó cada uno. `POST /v1/bank_transactions/{id}/match` la concilia y la factura queda pagada; una PPD se paga con `POST /v1/payments` y `bank_transaction`, que timbra el complemento y lo concilia. Ver [Conciliación bancaria](https://fiscalisto.com/docs/api/conciliacion-bancaria). - **Cuentas por cobrar.** Cada factura trae `payment_status`, `amount_due` y `amount_paid`, que bajan con sus complementos de pago y notas de crédito; `GET /v1/receivables` dice cuánto debe cada cliente y `GET /v1/invoices?payment_status=open` lista lo pendiente. Una PUE con `paid_later` se marca con `POST /v1/invoices/{id}/mark_paid`. Eventos `invoice.paid` e `invoice.partially_paid`. Ver [Cuentas por cobrar](https://fiscalisto.com/docs/api/cuentas-por-cobrar). - **Cotizaciones.** `POST /v1/quotes` hace una cotización con los conceptos de una factura, para un cliente de su catálogo o alguien que aún no le da sus datos fiscales. Su cliente la ve, la acepta, pide cambios o la rechaza en una liga (eventos `quote.viewed`, `quote.accepted`, `quote.changes_requested` y `quote.rejected`); con `auto_invoice` la factura se timbra sola al aceptarla, o con `POST /v1/quotes/{id}/invoice`. Ver [Cotizaciones](https://fiscalisto.com/docs/api/cotizaciones). - **Facturas recurrentes.** `POST /v1/recurring_invoices` timbra sola una factura cada mes, semana o día, a la hora que elija, con `{mes}` y `{año}` llenos en cada una y sin repetir nunca un periodo. Cada factura trae `recurring_invoice`; si un periodo no sale llega `recurring_invoice.failed`. Ver [Facturas recurrentes](https://fiscalisto.com/docs/api/facturas-recurrentes). - **n8n, Zapier y Make.** Conectores para timbrar facturas, registrar tickets, pedir datos fiscales, revisar las listas del SAT y correr flujos con cada evento, sin programar. Ver [n8n, Zapier y Make](https://fiscalisto.com/docs/api/automatizaciones). - **Autofactura.** `POST /v1/tickets` registra cada venta y su comprador se factura solo con el código y el total, en el portal del emisor o dentro de su página con `fiscalisto.js` o `@fiscalisto/react`; lo que nadie factura entra en la factura global. Eventos `ticket.invoiced` y `ticket.included_in_global`. Ver [Autofactura](https://fiscalisto.com/docs/api/autofactura). - **Equipo de la cuenta API.** Roles para desarrolladores (**Desarrollador**, **Solo lectura**) y **Desarrolladores › Actividad**: quién creó o cambió llaves, webhooks y emisores, y cuándo. - **Límite en cada respuesta.** El encabezado `RateLimit-Policy` dice cuántas solicitudes acepta su cuenta cada 10 segundos. En **Desarrolladores › Llaves** cada llave muestra sus solicitudes y errores de los últimos 7 días, y **Solicitudes** se filtra por llave. Ver [Límites](https://fiscalisto.com/docs/api/limites). - **Reiniciar los datos de prueba.** `POST /v1/test_data/reset` deja el modo de pruebas como nuevo, con 3 clientes y 3 productos de ejemplo si quiere; también en **Desarrolladores › Inicio**. Ver [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion#empezar-de-cero). - **Descargas del SAT.** `POST /v1/sat_downloads` le pide al SAT las facturas que un emisor recibió o emitió en un periodo, con la e.firma que guarda con `keep_for_downloads`; al terminar llega `sat_download.completed` y las facturas quedan en `GET /v1/sat_invoices`, con su XML. En pruebas trae facturas de ejemplo al instante. Ver [Descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat). - **Proyectos de inicio.** Next.js, Laravel, Django y ASP.NET Core: timbran, descargan el PDF y el XML y verifican los webhooks. Ver [SDKs](https://fiscalisto.com/docs/api/sdk#proyectos-de-inicio). - **Colección para Postman, Bruno e Insomnia.** Todas las rutas con su ejemplo, en `/docs/api/fiscalisto.postman_collection.json`, o con su llave de pruebas al crearla en **Desarrolladores › Llaves**. Ver [SDKs](https://fiscalisto.com/docs/api/sdk#postman-bruno-e-insomnia). - **SDKs de Go y Java.** `fiscalisto.com/go` (Go 1.23, sin dependencias) y `com.fiscalisto:fiscalisto` (Java 17, con Jackson), generados del mismo OpenAPI: structs y clases para cada campo, `Idempotency-Key` automática, listas que se recorren solas y webhooks verificados. Cada página de la referencia trae su ejemplo en Go y en Java. Ver [SDKs](https://fiscalisto.com/docs/api/sdk). - **Avisos de cambios y del estado.** Este registro por correo ([suscribirse](/avisos?tema=cambios)) y por RSS (`/docs/api/cambios.rss`). En [Estado del servicio](/estado), **Incidentes recientes**, avisos por correo cuando algo falla y cuando se resuelve, y `/estado.rss`. - **Llaves de solo lectura y llaves que vencen.** Al crear una llave elija **Permisos** (**Solo lectura** responde `key_read_only` a lo que cambia algo) y **Vence** (30 días a 1 año, con aviso por correo 7 días y 1 día antes). `GET /v1/account` agrega `key.read_only` y `key.expires_at`. Ver [Autenticación](https://fiscalisto.com/docs/api/autenticacion#llaves-de-solo-lectura). - **Timbres que no se acaban.** El evento `balance.low` y un correo cuando quedan pocos timbres (usted elige cuántos) y otra vez al acabarse, y **Recarga automática** con su tarjeta en **Desarrolladores › Timbres**. `GET /v1/balance` agrega `low_balance_at` y `auto_recharge`. Ver [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion#qué-cuesta-un-timbre). ## 24 de octubre de 2026 - **Depurar desde Solicitudes.** Cada error trae **Cómo arreglarlo**; cada solicitud, **Copiar como curl**, y en pruebas **Repetir en pruebas** con el cuerpo editable. Los mensajes de validación ahora están en español (`items.0.unit_price: debe ser mayor o igual a 0.`). - **Códigos de error con nombre.** `ErrorCode` en los cuatro SDKs: `error.code === ErrorCode.InvoiceRejected` en vez del texto, con la descripción de cada código en su editor. Ver [SDKs](https://fiscalisto.com/docs/api/sdk#errores). - **Estado del servicio.** [fiscalisto.com/estado](https://fiscalisto.com/estado) dice si el timbrado, la API y las listas del SAT funcionan ahora y en los últimos 90 días; `https://fiscalisto.com/estado.json` es lo mismo para sus monitores. Ver [Errores](https://fiscalisto.com/docs/api/errores#si-la-api-no-contesta). - **SDK de .NET.** `dotnet add package Fiscalisto`: una clase para cada objeto y cada solicitud, `await foreach` en las listas y `FiscalistoClient.Webhooks.Verify`. Cada página de la referencia trae su ejemplo en C#. Ver [SDKs](https://fiscalisto.com/docs/api/sdk). - **Anticipos.** `customer`, `email_cc` y `metadata` de `POST /v1/advances` se documentan como en las facturas, así que los SDKs ya los revisan. - **Documentación para agentes.** Servidor MCP en `https://fiscalisto.com/docs/api/mcp` y, en cada página, **Copiar como Markdown**, **Abrir en ChatGPT** y **Abrir en Claude**. Si entró a Fiscalisto con su cuenta API, los ejemplos ya traen su llave de pruebas. Ver [CLI](https://fiscalisto.com/docs/api/cli#la-documentación-en-su-agente). - **Revisión de su integración.** En **Desarrolladores › Inicio**, lo que dicen sus solicitudes de pruebas antes de pasar a producción: Idempotency-Key, rechazos del SAT y webhooks. - **Alta de emisores por liga.** `POST /v1/issuers/{id}/onboarding_links` da una página donde su cliente sube su CSD y firma con su e.firma, sin que esos archivos pasen por su plataforma; al terminar llega `issuer.ready`. Ver [Emisores](https://fiscalisto.com/docs/api/emisores#que-su-cliente-suba-sus-archivos). - **Datos del cliente.** `POST /v1/tax_ids/validate` revisa RFC, nombre, régimen, código postal y uso del CFDI antes de timbrar, y con el idCIF los compara con la constancia en el SAT. `POST /v1/customer_data_requests` crea una liga para que su cliente dé sus datos, con el evento `customer_data_request.completed`. Ver [Datos fiscales del cliente](https://fiscalisto.com/docs/api/datos-del-cliente). - **CLI.** `npx fiscalisto login` inicia sesión por el navegador, `init` deja el SDK y la llave en su proyecto, `listen` le manda los webhooks a su computadora y `trigger` los provoca. Con `--json`, para agentes. Ver [CLI](https://fiscalisto.com/docs/api/cli). - **Listas del SAT.** `GET /v1/sat_lists/{tax_id}` y `POST /v1/sat_lists/check` (hasta 1,000 RFC) revisan la 69-B, la 69-B Bis y las del artículo 69. Ver [Listas del SAT](https://fiscalisto.com/docs/api/listas-del-sat). - **Evento de prueba.** `POST /v1/webhook_endpoints/{id}/test` le manda a un endpoint cualquier evento con un objeto de ejemplo, sin timbrar, y contesta lo que respondió su servidor. Ver [Webhooks](https://fiscalisto.com/docs/api/webhooks#un-evento-de-prueba-sin-timbrar). - **RFC de prueba.** Cinco RFC que en pruebas provocan cada rechazo común del SAT (nombre, código postal, régimen, uso del CFDI). Ver [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion#provocar-cada-rechazo-del-sat). - **Probar sin llave.** En la referencia, si entró a Fiscalisto con su cuenta API abierta, **Probar** funciona sin pegar la llave. - **Panel.** **Desarrolladores** tiene **Facturas** (filtros, PDF, XML y cancelar), **Eventos** (lo que se mandó y cada entrega, con **Reenviar**) y la gráfica de **Uso** de los últimos 30 días. - **SDKs.** TypeScript, Python y PHP, generados del documento [OpenAPI](https://api.fiscalisto.com/v1/openapi.json). Ver [SDKs](https://fiscalisto.com/docs/api/sdk). - **Primera versión de la API**: facturas, pagos, notas de crédito, anticipos, nómina, retenciones, lotes, cancelación, emisores, catálogos, webhooks e idempotencia. --- # Autenticación y llaves > Cómo se identifica su sistema con su llave secreta, cómo limitarla a ciertos emisores, a solo lectura o a un plazo, y cómo cambiarla sin cortes. Cada solicitud lleva su llave secreta en el encabezado `Authorization`: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/account \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const account = await fiscalisto.account.get(); ``` También se acepta como usuario de HTTP Basic, sin contraseña (`curl -u "$FISCALISTO_API_KEY:"`), para las herramientas que solo saben de esa forma. ## Formato Una llave es `fk_test_` o `fk_live_` seguido de 40 letras y números. El prefijo dice el modo (vea [Pruebas y producción](https://fiscalisto.com/docs/api/pruebas-y-produccion)). Fiscalisto guarda solo su huella: si la pierde, cree otra. ## Dónde guardarla - En una variable de entorno o en el gestor de secretos de su servidor, nunca en el código ni en el repositorio. - Nunca en el navegador ni en una app que se instala en teléfonos: quien la tenga timbra a su nombre. La API no contesta a solicitudes del navegador (las rechaza con `browser_not_supported`); la excepción es **Probar** en la referencia de esta documentación, y solo con llaves de pruebas. - Una llave por sistema o por ambiente, con un nombre que diga dónde vive, para revocar solo la que se filtró. ## Llaves limitadas a algunos emisores Al crear una llave en **Desarrolladores › Llaves** puede marcar **Solo para algunos emisores**. Esa llave solo ve y timbra con esos RFC; lo que toca a otro emisor, crea emisores nuevos o es de toda la cuenta (como los webhooks) responde `403` con `key_restricted`. Sirve, por ejemplo, para darle a cada sucursal o a cada cliente de su plataforma una llave que solo factura con su RFC. ## Llaves de solo lectura En **Permisos** elija **Solo lectura** para una llave que consulta y no cambia nada: todas las solicitudes `GET` (facturas, PDF, XML, saldo, catálogos, eventos) y tres `POST` que solo revisan: `POST /v1/tax_ids/validate`, `POST /v1/sat_lists/check` y `POST /v1/payroll_receipts/preview`. Lo demás (timbrar, cancelar, enviar, crear o cambiar datos) responde `403` con `key_read_only`. Sirve para un tablero, un reporte o la herramienta de su equipo de soporte. Se puede combinar con **Solo para algunos emisores**. ## Llaves que vencen En **Vence** elija si la llave deja de funcionar en 30 días, 90 días, 6 meses o 1 año (**Nunca**, si no). Le avisamos por correo 7 días y 1 día antes, a los propietarios y administradores de la cuenta. Al vencer responde `401` con `invalid_api_key`. **Cambiar llave** le da una nueva con el mismo plazo, contado desde ese día. `GET /v1/account` dice de la llave que hizo la solicitud si es de solo lectura (`key.read_only`) y cuándo vence (`key.expires_at`), para que su sistema avise antes. ## Elegir el emisor Lo que crea comprobantes, clientes o productos lleva el emisor (`iss_…`) en `issuer`, en el cuerpo; las listas lo llevan en la consulta (`?issuer=iss_…`). Si la cuenta, o la llave, tiene un solo emisor, puede omitirlo. Si tiene varios y no lo manda, la API responde `issuer_required`. Los ids de lo demás (`inv_…`, `cus_…`) ya dicen de qué emisor son. ## Cambiar una llave sin cortes En **Desarrolladores › Llaves** elija **Cambiar llave** en su menú. Le damos una nueva con el mismo nombre y los mismos permisos, y usted elige cuándo deja de servir la anterior: ahora mismo, en 1 hora, en 24 horas o en 7 días. Ponga la nueva en su servidor dentro de ese plazo. Si una llave se filtró, cámbiela con **Ahora mismo** o revóquela. ## Límites - Hasta 50 llaves por modo. - Crear llaves de producción pide verificación en dos pasos del usuario. - Una dirección IP que manda muchas llaves inválidas seguidas recibe `429` por un minuto; nunca afecta a las llaves válidas. ## Errores de autenticación | Código | Qué pasó | | --- | --- | | [`missing_api_key`](https://fiscalisto.com/docs/api/errores#missing_api_key) | No llegó el encabezado `Authorization`. | | [`invalid_api_key`](https://fiscalisto.com/docs/api/errores#invalid_api_key) | La llave no existe, se revocó o ya expiró. | | [`key_restricted`](https://fiscalisto.com/docs/api/errores#key_restricted) | La llave está limitada a otros emisores. | | [`key_read_only`](https://fiscalisto.com/docs/api/errores#key_read_only) | La llave es de solo lectura y la solicitud cambia algo. | | [`livemode_mismatch`](https://fiscalisto.com/docs/api/errores#livemode_mismatch) | El objeto es del otro modo. | --- # Idempotencia > Repita cualquier solicitud sin miedo: con la misma Idempotency-Key nada se hace dos veces, ni se timbra, ni se crea, ni se manda un correo. Una red que se corta a la mitad deja a su sistema sin saber si la factura se timbró. Por eso todo lo que timbra exige el encabezado `Idempotency-Key`: si repite la solicitud con la misma llave, Fiscalisto le contesta el comprobante que ya timbró en vez de timbrar otro. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pedido-A-1042" \ -H "Content-Type: application/json" \ -d '{ "customer": "cus_OufpKqnrvx9p1", "payment_form": "04", "items": [{ "product": "prd_OufpKqnrvx9p1" }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: "cus_OufpKqnrvx9p1", payment_form: "04", items: [{ product: "prd_OufpKqnrvx9p1" }], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Dónde se usa Todo `POST` de la API acepta `Idempotency-Key`, como en Stripe o Resend: - **Lo que timbra la exige**: facturas, sustituciones, complementos de pago, notas de crédito, anticipos y su aplicación, recibos de nómina, constancias de retenciones, lotes y facturar una cotización. Sin ella responden `idempotency_key_missing`. - **Lo demás la acepta**: crear un cliente, un producto, un webhook, una cotización, una factura recurrente, un ticket, una descarga del SAT, mandar un correo, conectar una tienda… Con la llave, un reintento contesta lo mismo que la primera vez, con `idempotent-replayed: true`, y no crea nada otra vez ni manda otro correo. `GET` y `DELETE` no la necesitan: repetirlos no cambia nada. ## Cómo elegirla - De 1 a 255 caracteres ASCII, sin acentos ni ñ (8 o más en lo que timbra). Si no es así, `400` `idempotency_key_invalid`. - Una por comprobante, tomada de algo que su sistema ya tiene y que no cambia al reintentar: el id del pedido, del cobro o de la fila en su base de datos (`pedido-A-1042`, `pago-7781`). Si no tiene uno, genere un UUID al crear el pedido y guárdelo con él. - Nunca la genere en el momento de llamar: un reintento tendría otra llave y timbraría dos veces. ## Qué contesta al repetirla | Situación | Respuesta | | --- | --- | | La primera vez | `201` con el comprobante timbrado (o `202` si el PAC aún no contesta). | | Repetida, ya timbrado | `201` (lo mismo que la primera vez) con el mismo comprobante y el encabezado `idempotent-replayed: true`. No usa otro timbre. | | Repetida mientras la primera sigue timbrando | `409` `request_in_progress` con `Retry-After`. Espere y repítala. | | Repetida con otro cuerpo | `409` `idempotency_key_reused`. No se timbra nada. | | La primera no pasó la revisión (`422` `invoice_invalid`) | La llave queda libre: mande el cuerpo corregido con la misma llave. | | La primera la rechazó el SAT (`422` `invoice_rejected`) o no había timbres (`402`) | Repetirla con el mismo cuerpo lo intenta de nuevo. Con otro cuerpo, `409` `idempotency_key_reused`. | La llave guarda el cuerpo con el que se usó desde que llegó al PAC. Si el SAT rechazó una factura y la corrige, el cuerpo es otro: use una llave nueva (`pedido-A-1042-2`). Repetir con la misma llave y el mismo cuerpo sirve cuando el problema no estaba en los datos: el PAC no respondió, se acabaron los timbres y ya compró, o el emisor aún no tenía su CSD. Un `invoice_invalid` nunca llegó al PAC, así que ahí sí puede corregir y repetir con la misma llave. Varias solicitudes con la misma llave al mismo tiempo (un doble clic, dos servidores) timbran una sola vez: una recibe el comprobante y las demás `409` `request_in_progress` hasta que termine; repetidas, reciben el mismo comprobante. ## Cuánto dura una llave Una llave es de su cuenta y de su modo: la misma en pruebas y en producción, o en dos [entornos de prueba](https://fiscalisto.com/docs/api/pruebas-y-produccion#un-entorno-de-prueba-para-cada-persona), son llaves distintas. - En todo `POST` la llave queda ligada a esa ruta y ese cuerpo 24 horas: usarla para otra cosa responde `409` `idempotency_key_reused`, aunque sea otra ruta. - Solo se guardan las respuestas correctas (`2xx`). Si la primera vez respondió un error, no se hizo nada y la llave queda libre: corrija y repita con la misma llave. - Lo que timbra guarda además el comprobante de cada llave para siempre: una llave que ya timbró siempre contesta ese comprobante, aunque pasen meses. Después de 24 horas, la llave de un `POST` que no timbra vuelve a estar libre. ## Reintentos recomendados Los SDKs ([TypeScript, Python, PHP, .NET, Go y Java](https://fiscalisto.com/docs/api/sdk)) ya hacen todo esto en cada `POST`: ponen una `Idempotency-Key` si usted no la da, la conservan en cada reintento y esperan lo que diga `Retry-After`. Así ningún reintento del SDK crea o manda algo dos veces. Lo siguiente es para llamar al API directo. Reintente con la misma llave cuando: - no llegó respuesta (se cortó la conexión o se acabó el tiempo de espera); - llegó `409` `request_in_progress`, `429` o un `5xx`, esperando lo que diga `Retry-After` o, si no viene, con espera creciente (1, 2, 4, 8 segundos). No reintente un `400`, `402`, `403`, `404` o `422` sin cambiar algo: la respuesta sería la misma. ```js title="reintentar.js" async function timbrar(body, idempotencyKey) { for (let intento = 0; intento < 5; intento++) { let res; try { res = await fetch("https://api.fiscalisto.com/v1/invoices", { method: "POST", headers: { Authorization: `Bearer ${process.env.FISCALISTO_API_KEY}`, "Idempotency-Key": idempotencyKey, "Content-Type": "application/json", }, body: JSON.stringify(body), }); } catch { // Sin respuesta: repetir con la misma llave nunca timbra dos veces. } if (res && res.status !== 409 && res.status !== 429 && res.status < 500) return res.json(); const espera = Number(res?.headers.get("retry-after")) || 2 ** intento; await new Promise((r) => setTimeout(r, espera * 1000)); } throw new Error("Fiscalisto no respondió; reintente más tarde con la misma Idempotency-Key."); } ``` > **409 idempotency_key_reused no se reintenta.** El reintento del ejemplo vuelve a mandar el mismo cuerpo, así que un `409` es siempre `request_in_progress`. Si su código puede cambiar el cuerpo entre intentos, revise `error.code` antes de esperar. --- # Versiones > Cada cuenta usa una versión de la API, por fecha. Un cambio que no es compatible llega como una versión nueva, y usted decide cuándo moverse. La API se versiona por fecha, como Stripe. Cada respuesta dice en el encabezado `Fiscalisto-Version` en qué versión se escribió: ```http HTTP/1.1 200 OK Fiscalisto-Version: 2026-10-24 ``` Hoy hay una sola versión, `2026-10-24`. ## Qué es una versión nueva Lo que **no** rompe nada llega sin versión nueva, en la que ya usa: rutas, campos, eventos, parámetros opcionales y códigos de error nuevos. Escriba su integración para que ignore los campos que no conoce (los SDKs ya lo hacen). Una versión nueva es para lo que sí cambiaría lo que ya funciona: un campo que cambia de nombre o de significado, o que deja de existir. Cada una se anuncia en [Cambios](https://fiscalisto.com/docs/api/cambios) y por correo en [Avisos por correo](/avisos?tema=cambios), con lo que cambió y cómo adaptarse. ## Qué versión recibe 1. **La de la solicitud**, si manda el encabezado `Fiscalisto-Version`. Los SDKs mandan la versión para la que se hicieron, así que un SDK siempre recibe lo que espera. 2. **La de su cuenta**, si no lo manda (por ejemplo con `curl`). Su cuenta queda en la versión más reciente del día en que la creó, y no se mueve sola. Una versión que no existe responde `400` con `api_version_invalid` y la lista de las que hay. ```bash curl https://api.fiscalisto.com/v1/account \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Fiscalisto-Version: 2026-10-24" ``` `GET /v1/account` dice la de su cuenta (`api_version`) y la más reciente (`latest_api_version`). ## Moverse a una versión nueva 1. Lea en [Cambios](https://fiscalisto.com/docs/api/cambios) qué cambió. 2. Pruébela en modo de pruebas mandando `Fiscalisto-Version` con la nueva fecha (o actualice el SDK). 3. Cuando todo funcione, mueva su cuenta: lo que llama sin encabezado y los webhooks sin versión propia pasan a la nueva. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/account \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -d '{ "api_version": "2026-10-24" }' \ -H "Content-Type: application/json" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const account = await fiscalisto.account.update({ api_version: "2026-10-24" }); ``` Se cambia con una llave de la cuenta con todos los permisos (no con una de solo lectura, restringida a emisores ni de un entorno de prueba), y queda en **Desarrolladores › Actividad**. Puede volver a la anterior de la misma forma. ## Webhooks Cada evento trae `api_version`, la versión en la que se escribió su `data.object`. Un endpoint recibe sus eventos en la versión de su cuenta, o en la suya propia si la tiene (`api_version` al crearlo o cambiarlo con `POST /v1/webhook_endpoints/{id}`; `null` vuelve a la de la cuenta). Para mover los webhooks sin cortes: cree un segundo endpoint con la versión nueva, adapte su código para recibir las dos, y borre el viejo. ## En los SDKs Cada SDK trae la versión para la que se hizo y la manda en cada solicitud. Para pedir otra, o ninguna (la de la cuenta): ```ts const fiscalisto = new Fiscalisto("fk_test_…", { apiVersion: "2026-10-24" }); // null: la de la cuenta ``` ```python fiscalisto = Fiscalisto("fk_test_…", api_version="2026-10-24") # None: la de la cuenta ``` ```php $fiscalisto = new Fiscalisto('fk_test_…', ['api_version' => '2026-10-24']); // null: la de la cuenta ``` ```go client, err := fiscalisto.NewClient("fk_test_…", fiscalisto.WithAPIVersion("2026-10-24")) // "": la de la cuenta ``` ```java var client = new FiscalistoClient(ClientOptions.builder().apiKey("fk_test_…").apiVersion("2026-10-24").build()); ``` ```csharp var client = new FiscalistoClient("fk_test_…", new FiscalistoClientOptions { ApiVersion = "2026-10-24" }); ``` Una respuesta guardada por [idempotencia](https://fiscalisto.com/docs/api/idempotencia) se repite tal como se contestó la primera vez, en esa versión. --- # Errores > Cada error dice qué pasó, qué campo corregir y cómo. Aquí está cada código que puede recibir. Un error responde con un código HTTP 4xx o 5xx y siempre con el mismo cuerpo: ```json { "error": { "type": "invoice_error", "code": "invoice_rejected", "message": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital.", "param": "customer", "sat_code": "CFDI40145", "invoice": "inv_roVZuc6krJsw08oPyaiBiYX4fWH2pyEN1x", "doc_url": "https://fiscalisto.com/docs/api/errores#invoice_rejected", "request_id": "req_FfCT3zz9ktKinNLSGMqu" } } ``` | Campo | Qué es | | --- | --- | | `type` | La familia del error. Su código decide qué hacer con este. | | `code` | La razón precisa, estable: no cambia entre versiones. Está en la lista de abajo. | | `message` | Qué pasó y cómo corregirlo, en español, para mostrarlo a una persona. Puede mejorar con el tiempo: no lo compare en su código. | | `param` | El campo que hay que corregir, con su ruta en el cuerpo (`items.0.unit_price`, `customer.zip`). | | `sat_code` | El código del SAT o del PAC cuando ellos lo rechazaron (`CFDI40145`). | | `doc_url` | Esta página, en el código del error. | | `request_id` | El id de la solicitud, el mismo del encabezado `Request-Id`. Búsquelo en **Desarrolladores › Solicitudes** o mándelo a soporte. | Un rechazo del SAT o del PAC trae además el id del comprobante rechazado en `invoice` (o `retention` en una constancia), para consultarlo. ## Qué hacer con cada tipo | `type` | HTTP | Qué hacer | | --- | --- | --- | | `invalid_request_error` | 400 | Corregir la solicitud: `param` y `message` dicen qué. | | `authentication_error` | 401 | Revisar la llave. | | `balance_error` | 402 | Comprar timbres y repetir con la misma `Idempotency-Key`. | | `permission_error` | 403 | Usar una llave que pueda con ese emisor. | | `not_found_error` | 404 | Revisar el id y el modo de la llave. | | `idempotency_error` | 409 | `request_in_progress`: esperar y repetir. `idempotency_key_reused`: usar otra llave. | | `invoice_error` | 422 | Corregir los datos del comprobante: el SAT, el PAC o la revisión de Fiscalisto no lo aceptan. | | `rate_limit_error` | 429 | Esperar lo que dice `Retry-After` y repetir. | | `api_error` | 500, 503 | Repetir con la misma `Idempotency-Key`, con espera creciente. | > **Nunca se cobra un error.** Ningún error usa un timbre: ni las revisiones de Fiscalisto, ni los rechazos del SAT o del PAC. Una factura rechazada queda guardada con `status: "rejected"` y su `error`, y puede consultarla con su id. ## Si la API no contesta Un `500`, un `503` o un corte de red: repita con la misma `Idempotency-Key` y espera creciente (los [SDKs](https://fiscalisto.com/docs/api/sdk#reintentos-seguros) ya lo hacen). Para saber si es general, vea [fiscalisto.com/estado](https://fiscalisto.com/estado): dice si el timbrado, la API y las listas del SAT funcionan, desde cuándo fallan y cómo funcionaron en los últimos 90 días, revisado cada 15 minutos. Para sus monitores, lo mismo en JSON con `GET https://fiscalisto.com/estado.json`, sin llave: ```json { "status": "operativo", "checked_at": "2026-10-24T18:45:00.000Z", "page": "https://fiscalisto.com/estado", "components": [ { "id": "pac", "name": "Timbrado y cancelación", "status": "operativo", "since": null }, { "id": "api", "name": "API", "status": "operativo", "since": null }, { "id": "sat_lists", "name": "Listas del SAT (69-B y 69)", "status": "operativo", "since": null } ] } ``` `status` es `operativo`, `degradado` o `interrumpido`; `since`, desde cuándo, mientras no sea `operativo`. Con `?history=1` trae también los últimos 90 días (`days`: `day`, `status` y `minutes` con fallas). Se actualiza cada minuto; no lo consulte más seguido. Para enterarse sin consultar: avisos por correo en [Estado del servicio](/estado) o el RSS `https://fiscalisto.com/estado.rss`, con cada incidente al empezar y al resolverse. ## En inglés Con el encabezado `Accept-Language: en` los errores llegan en inglés (y `Content-Language: en`). Los de validación dicen exactamente lo mismo que en español (`unit_price: must be greater than or equal to 0.`). Los demás dicen en inglés qué significa su `code`, y traen el mensaje original, más preciso, en `message_es`. Los datos no se traducen: los catálogos, nombres y campos del CFDI son los del SAT. ```bash curl https://api.fiscalisto.com/v1/invoices/inv_nope \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Accept-Language: en" ``` En los SDKs, la opción `language: "en"` (`language="en"` en Python, `'language' => 'en'` en PHP, `WithLanguage("en")` en Go, `.language("en")` en Java y `Language = "en"` en .NET). ## Errores de validación Antes de mandar al PAC, Fiscalisto revisa la factura completa con los catálogos y las reglas del SAT (por ejemplo, que el uso del CFDI corresponda al régimen del cliente o que los totales cuadren). Así casi todo error llega como `parameter_invalid` o `invoice_invalid`, con `param`, sin gastar tiempo ni timbre en el PAC. Un campo que no existe nunca se ignora en silencio: responde `parameter_unknown`, porque casi siempre es un nombre mal escrito. ## Solicitud inválida `type`: `invalid_request_error`. ### parameter_missing **HTTP 400.** Falta un campo obligatorio. `param` dice cuál. ### parameter_invalid **HTTP 400.** Un campo tiene un valor que no se acepta (formato, catálogo del SAT, rango). `param` dice cuál y `message` cómo corregirlo. ### parameter_unknown **HTTP 400.** Mandó un campo o un parámetro de la URL que no existe (casi siempre un nombre mal escrito, como legalName en vez de legal_name, o ?statuss= en vez de ?status=). Nunca se ignora en silencio. ### body_too_large **HTTP 413.** El cuerpo pasa de 10 MB. Divida el trabajo en varios lotes. ### data_request_completed **HTTP 400.** El cliente ya mandó sus datos con esa liga: una solicitud completada no se cancela. ### invalid_json **HTTP 400.** El cuerpo no es un objeto JSON válido. ### unsupported_content_type **HTTP 400.** Mande el cuerpo con Content-Type: application/json. ### method_not_allowed **HTTP 405.** La ruta existe pero no con ese método (por ejemplo, PUT: la API usa POST para actualizar). ### browser_not_supported **HTTP 400.** La API se llama desde su servidor, nunca desde el navegador: la llave secreta quedaría expuesta. ### livemode_mismatch **HTTP 400.** El objeto es del otro modo: una llave fk_test_ solo ve lo de pruebas y una fk_live_ lo de producción. ### issuer_required **HTTP 400.** La cuenta tiene varios emisores y la solicitud no dice cuál: mande `issuer` (iss_…). ### issuer_exists **HTTP 400.** Ese RFC ya es un emisor de la cuenta en este modo. ### issuer_archived **HTTP 400.** El emisor está archivado: ya no timbra. Sus comprobantes se pueden consultar y cancelar. ### certificate_invalid **HTTP 400.** El CSD no sirve: no es un certificado de sello (es una e.firma), está vencido, el .key no corresponde o la contraseña no abre la llave. ### certificate_required **HTTP 400.** Suba primero el CSD del emisor; después firme el contrato con su e.firma. ### efirma_invalid **HTTP 400.** La e.firma no sirve: no es una e.firma (es un CSD), es de otro RFC, está vencida o la contraseña no abre la llave. ### efirma_required **HTTP 400.** Para pedirle al SAT las facturas de un emisor en producción, guarde antes su e.firma: `POST /v1/issuers/{id}/efirma` con keep_for_downloads: true. También si la guardada venció. ### test_mode_only **HTTP 400.** Esto solo existe en pruebas (como reiniciar los datos de prueba): use su llave fk_test_. ### sat_request_rejected **HTTP 400.** El SAT rechazó la solicitud de descarga. El mensaje trae su motivo, por ejemplo que ya se pidió el mismo periodo con los mismos filtros demasiadas veces (el SAT pone un límite de por vida). ### ticket_invalid **HTTP 400.** El ticket no se pudo registrar: el folio trae caracteres no válidos, la fecha de venta está en el futuro o es de hace más de un año, el producto ya no está en el catálogo… El mensaje dice cuál. ### ticket_folio_taken **HTTP 400.** Ese folio ya está registrado para el emisor con otro total o con otra propina (409). Con el mismo total se contesta el ticket que ya existe. ### ticket_not_pending **HTTP 400.** El ticket ya se facturó o entró en una factura global: no se puede cancelar. ### recurring_invalid **HTTP 400.** La factura recurrente no se pudo guardar: el día, la hora, la zona o la factura no son válidos. El mensaje dice cuál. ### recurring_limit **HTTP 400.** El emisor ya tiene el máximo de facturas recurrentes activas (25 en pruebas, 5,000 en producción). Pause o borre las que ya no use. ### recurring_paused **HTTP 400.** La factura recurrente está en pausa: reanúdela con active: true para timbrarla. ### recurring_not_due **HTTP 400.** Aún no toca timbrar la factura recurrente (409): su next_invoice_on es después de hoy. Para una factura antes de tiempo, use `POST /v1/invoices`. ### invoice_not_markable **HTTP 400.** Solo una factura de ingreso PUE vigente se marca pagada o por cobrar; una PPD se paga con su complemento de pago (type P). ### bank_transaction_invalid **HTTP 400.** El depósito no se puede conciliar, clasificar o reabrir así (ya está conciliado, las facturas son de otro cliente o una está cancelada…). El mensaje dice por qué. ### diot_empty **HTTP 400.** El mes no tiene pagos a proveedores: esa DIOT se presenta sin operaciones desde el portal del SAT. ### store_connection_failed **HTTP 400.** La tienda no aceptó la conexión o no respondió como se esperaba (llaves equivocadas, permisos read_orders/write_orders que faltan, una tienda que no vende en pesos). El mensaje dice qué contestó. ### store_platform_unavailable **HTTP 400.** Esa plataforma aún no está disponible en Fiscalisto. ### sandbox_invalid **HTTP 400.** No se pudo crear el entorno de prueba: falta su nombre o la cuenta ya tiene 25 (borre uno que ya no use). ### api_version_invalid **HTTP 400.** No existe esa versión de la API (en Fiscalisto-Version o api_version). El mensaje dice cuáles hay; vea Versiones. ### expand_invalid **HTTP 400.** Un campo de expand[] no existe en la respuesta, no es el id de otro objeto, o se pidieron demasiados (hasta 10 campos, 4 niveles). Vea Expandir objetos. ### test_clock_unsupported **HTTP 400.** Los relojes de prueba son de emisores de prueba: use su llave fk_test_ y un emisor de pruebas. ### test_clock_exists **HTTP 409.** El emisor ya tiene un reloj de prueba: adelántelo, o bórrelo para volver a la hora real. ### test_clock_invalid **HTTP 400.** frozen_time no sirve: no es una fecha y hora ISO 8601, es anterior a la hora del reloj (solo avanza) o va más de dos años adelante de la hora real. ### test_clock_advancing **HTTP 409.** El reloj se está adelantando en otra solicitud (409, con Retry-After). Espere a test_clock.ready y repita. ### store_already_connected **HTTP 400.** Esa tienda ya está conectada a este emisor. ### store_not_authorized **HTTP 400.** El vendedor aún no autoriza la conexión en authorize_url. ### store_disconnected **HTTP 400.** La tienda se desconectó: conéctela otra vez con `POST /v1/store_connections`. ### quote_invalid **HTTP 400.** La cotización no se pudo guardar o ya no cambia (aceptada, rechazada o facturada), o no se puede mandar así. El mensaje dice por qué. ### quote_not_draft **HTTP 400.** Solo se borra una cotización que nunca se envió. ### quote_not_invoiceable **HTTP 400.** La cotización se rechazó o venció: no se factura. Si venció, cámbiele valid_until (es una versión nueva). ### quote_customer_missing **HTTP 400.** La cotización no tiene los datos fiscales del cliente: agrégueselos con `POST /v1/quotes/{id}` (customer) o mándela con auto_invoice para que los dé al aceptarla. ### idempotency_key_missing **HTTP 400.** Lo que timbra exige el encabezado Idempotency-Key (8 a 255 caracteres, uno nuevo por comprobante). Así un reintento nunca timbra dos veces. ### idempotency_key_invalid **HTTP 400.** Idempotency-Key es de 1 a 255 caracteres ASCII, sin acentos ni ñ: el id de su pedido o un UUID. ### invoice_not_stamped **HTTP 400.** La acción es para facturas timbradas (enviar, cancelar, sustituir). ### substitution_unsupported **HTTP 400.** Los complementos de pago y las notas de crédito no se sustituyen: se cancelan y se emite otro. ### retention_not_stamped **HTTP 400.** La acción es para constancias timbradas. ### customer_exists **HTTP 400.** Ya hay un cliente con ese RFC en el catálogo del emisor (o un producto con ese SKU). ### branch_exists **HTTP 400.** El emisor ya tiene una sucursal con ese código postal (cada una lleva el suyo); el mensaje dice cuál. Si está archivada, restáurela con archived: false. ### branch_has_invoices **HTTP 400.** La sucursal ya facturó, así que no se borra: archívela con archived: true y sus facturas la siguen nombrando. ### inventory_refused **HTTP 400.** Inventario no lo permite: el mensaje dice por qué (un lote que no es de ese producto, otro ajuste del mismo almacén en curso). ### email_failed **HTTP 400.** No se pudo mandar el correo; el comprobante sí está timbrado. ### endpoint_exists **HTTP 400.** Ya hay un webhook con esa dirección en este modo; el mensaje dice cuál. Cámbielo en vez de crear otro. ### endpoint_limit **HTTP 400.** La cuenta llegó al máximo de webhooks en este modo. ## Autenticación `type`: `authentication_error`. ### missing_api_key **HTTP 401.** Falta el encabezado Authorization: Bearer fk_test_… o fk_live_…. ### invalid_api_key **HTTP 401.** La llave no existe, se revocó o expiró. Cree otra en Desarrolladores › Llaves. ## Permisos `type`: `permission_error`. ### key_restricted **HTTP 403.** La llave está limitada a algunos emisores y la solicitud toca otro (o crea uno nuevo, o usa algo de toda la cuenta como webhooks). ### key_in_sandbox **HTTP 403.** La llave es de un entorno de prueba y la solicitud es de toda la cuenta (como crear o borrar entornos): use una llave de pruebas de la cuenta. ### issuer_not_allowed **HTTP 403.** La llave no puede usar ese emisor. ### key_read_only **HTTP 403.** La llave es de solo lectura: puede hacer GET, validar RFC, revisar listas del SAT y calcular recibos de nómina, pero nada que timbre, cancele, envíe o cambie datos. ## No encontrado `type`: `not_found_error`. ### route_not_found **HTTP 404.** No existe esa ruta. Revise la dirección y la versión (/v1). ### issuer_missing **HTTP 404.** No existe ese emisor en la cuenta (o es del otro modo). ### test_clock_missing **HTTP 404.** El emisor no tiene reloj de prueba: créelo con `POST /v1/issuers/{id}/test_clock`. ### resource_missing **HTTP 404.** No existe un objeto con ese id (o es de otro emisor o del otro modo). ## Idempotencia `type`: `idempotency_error`. ### idempotency_key_reused **HTTP 409.** Esa Idempotency-Key ya se usó en las últimas 24 horas con otra solicitud (otra ruta u otro cuerpo). Use una nueva para cada operación distinta. ### request_in_progress **HTTP 409.** Otra solicitud con la misma Idempotency-Key sigue en proceso, o otra solicitud está cambiando el mismo objeto (dos actualizaciones del mismo cliente a la vez esperan su turno hasta 15 segundos) (409, con Retry-After). Repítala en un momento con la misma llave: contestará lo mismo. ## Comprobantes `type`: `invoice_error`. ### invoice_invalid **HTTP 422.** La factura no cumple una regla del SAT que Fiscalisto revisa antes de mandarla al PAC (uso contra régimen, totales, complementos…). No se usó ningún timbre. ### invoice_rejected **HTTP 422.** El PAC o el SAT no la aceptaron. `sat_code` trae su código (CFDI40xxx) y `message` qué corregir. La factura queda como rejected; no se usó ningún timbre. ### cancellation_failed **HTTP 422.** El SAT no aceptó la solicitud de cancelación; `message` dice por qué. ### payroll_invalid **HTTP 422.** El recibo de nómina no se puede calcular o timbrar con esos datos (periodo, días, registro patronal del emisor…). ### retention_invalid **HTTP 422.** La constancia de retenciones no cumple una regla del SAT. No se usó ningún timbre. ### retention_rejected **HTTP 422.** El PAC o el SAT no aceptaron la constancia; `sat_code` trae su código. ### batch_invalid **HTTP 422.** Una factura del lote no pasa la revisión; `param` dice cuál (invoices.3.items.0.unit_price). Ninguna del lote se timbró. ## Timbres `type`: `balance_error`. ### insufficient_balance **HTTP 402.** La cuenta no tiene timbres de producción: para timbrar, revisar RFC en las listas, consultar constancias o pedir descargas al SAT. Compre un paquete en Desarrolladores › Timbres. En pruebas nunca pasa. Para enterarse antes, escuche balance.low o active la recarga automática. ## Límites `type`: `rate_limit_error`. ### issuer_creation_limit **HTTP 429.** Se crearon 500 emisores en 24 horas. Escríbanos si necesita más. ### sat_refresh_too_soon **HTTP 429.** Las declaraciones de ese emisor se consultaron en el SAT hace menos de 10 minutos (cada consulta entra al SAT con su e.firma). Retry-After dice cuántos segundos faltan; lo que ya trajo está en `GET /v1/sat_declarations`. ### stamp_rate_limit **HTTP 429.** Demasiados timbres seguidos del mismo emisor. Espere lo que dice Retry-After; para muchas facturas use `POST /v1/invoice_batches`. ### daily_limit **HTTP 429.** Llegó al tope del día de algo que no se paga con timbres: comprobantes en pruebas (1,000), RFC revisados en las listas del SAT o constancias consultadas. Retry-After dice cuántos segundos faltan para la medianoche de la Ciudad de México. ### rate_limit **HTTP 429.** Demasiadas solicitudes con la llave. Espere lo que dice Retry-After y reintente. ## Errores de Fiscalisto `type`: `api_error`. ### sat_unavailable **HTTP 502.** El SAT no contestó o no dejó entrar con la e.firma del emisor. El mensaje trae lo que dijo; no se usó ningún timbre. Reintente en 10 minutos. ### catalog_unavailable **HTTP 503.** El catálogo del SAT no respondió (503). Reintente en unos segundos. ### internal_error **HTTP 500.** Algo falló de nuestro lado. Repita la solicitud con la misma Idempotency-Key: nunca se timbra dos veces. Si sigue, escríbanos con el request_id. --- # Objetos y convenciones > Cómo son las rutas, los ids, los montos, las fechas, metadata y las versiones de la API. ## Rutas y métodos - Todo vive en `https://api.fiscalisto.com/v1`. - Los cuerpos van en JSON con `Content-Type: application/json`, y las respuestas también son JSON (salvo el PDF y el XML). - `GET` consulta, `POST` crea y también actualiza (`POST /v1/customers/{id}` cambia solo los campos que manda), `DELETE` borra o archiva. No hay `PUT` ni `PATCH`. - Los nombres de los campos van en inglés y en `snake_case`. El RFC siempre se llama `tax_id`, también dentro de los complementos. ## Objetos Cada objeto trae `id`, `object` (qué es: `invoice`, `customer`, `event`…) y `livemode`. Los ids empiezan con su tipo: | Prefijo | Objeto | | --- | --- | | `iss_` | Emisor | | `inv_` | Factura, complemento de pago, nota de crédito, anticipo o recibo de nómina | | `ret_` | Constancia de retenciones | | `cus_` | Cliente | | `prd_` | Producto | | `emp_` | Empleado | | `bat_` | Lote de facturas | | `whe_` | Webhook | | `evt_` | Evento | Los comprobantes también se pueden consultar por su folio fiscal (UUID), que siempre va en mayúsculas. ## Montos Los montos son números en la moneda del comprobante, con decimales (`2500`, `1160.5`), nunca centavos enteros ni texto. Usted manda el precio unitario antes de impuestos (`unit_price`) y Fiscalisto calcula los impuestos, los redondeos del SAT y los totales; la respuesta los trae en `subtotal`, `discount`, `taxes` y `total`. Las tasas van como fracción, igual que en el XML: `0.10` es 10%. La del IVA trasladado va como texto porque también puede ser `"exento"` (`"0.16"`, `"0.08"`, `"0"`, `"exento"`); las de retención van como número (`0.106667` son dos terceras partes del IVA). ## Fechas - Lo que pasó en Fiscalisto (`created_at`, `canceled_at`) va en ISO 8601 con la hora de la Ciudad de México: `2026-10-24T10:15:42-06:00`. - La fecha de emisión del CFDI (`date`) va sin zona, como la lleva el XML: `2026-10-24T10:15:42`, en hora de la Ciudad de México. ## Campos vacíos Un campo que no aplica viene en `null`, nunca se omite: su código puede leer `invoice.cancellation` sin revisar si existe. Al crear, omita lo que tiene su valor usual. La API puede agregar campos nuevos a los objetos sin cambiar de versión, así que su código debe ignorar los que no conoce. ## metadata Guarde sus propios datos en un objeto con `metadata`: el id del pedido, del cliente en su sistema, de la sucursal. ```json { "metadata": { "pedido": "A-1042", "sucursal": "monterrey" } } ``` - Hasta 50 llaves de hasta 40 caracteres, con valores de texto de hasta 500. - No van al CFDI ni al PDF: son solo para usted. - Vuelven en el objeto, en las listas y en los webhooks. - La aceptan los emisores y todo lo que se timbra (facturas, pagos, notas de crédito, anticipos, recibos de nómina y constancias de retenciones). Cada factura de un lote lleva `reference` en su lugar. ## Versiones Cada respuesta trae el encabezado `Fiscalisto-Version` con la versión de la API que contestó (hoy `2026-10-24`) y cada evento la trae en `api_version`. Agregar campos, eventos, rutas o códigos de error no cambia la versión. Un cambio que pudiera romper su código (quitar o renombrar un campo, cambiar su tipo) saldría con una versión nueva, con aviso. ## Encabezados de la respuesta | Encabezado | Qué es | | --- | --- | | `Request-Id` | El id de la solicitud. Guárdelo en sus registros: con él la encuentra en **Desarrolladores › Solicitudes**. | | `Fiscalisto-Version` | La versión de la API. | | `Idempotent-Replayed` | `true` cuando la respuesta es la guardada de una solicitud anterior con la misma `Idempotency-Key`. | | `Retry-After` | En un `429`, `409` o `503`: cuántos segundos esperar antes de repetir. | --- # Listas y paginación > Recorra facturas, clientes o eventos página por página con limit y starting_after. Las rutas que listan (`GET /v1/invoices`, `GET /v1/customers`, `GET /v1/events`…) contestan una página, la más reciente primero: ```json { "object": "list", "data": [{ "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "...": "..." }], "has_more": true, "url": "/v1/invoices" } ``` | Parámetro | Qué hace | | --- | --- | | `limit` | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | El `id` del último objeto de la página anterior: la siguiente empieza después de él. | Mientras `has_more` sea `true` hay más: pida la siguiente con el `id` del último de `data`. ```bash tab="cURL" curl -G https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -d limit=100 \ -d starting_after=inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.invoices.list({ limit: 100, starting_after: "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", })) { console.log(item.id); } ``` ```js title="todas-las-facturas.js" async function* facturas(filtros = {}) { let startingAfter; while (true) { const q = new URLSearchParams({ limit: "100", ...filtros }); if (startingAfter) q.set("starting_after", startingAfter); const res = await fetch(`https://api.fiscalisto.com/v1/invoices?${q}`, { headers: { Authorization: `Bearer ${process.env.FISCALISTO_API_KEY}` }, }); const page = await res.json(); yield* page.data; if (!page.has_more) return; startingAfter = page.data.at(-1).id; } } for await (const f of facturas({ status: "valid", created_gte: "2026-10-01" })) { console.log(f.uuid, f.total); } ``` Las páginas no se saltan ni repiten objetos aunque se creen otros mientras recorre la lista. ## Filtros Cada lista tiene los suyos, por ejemplo: - Facturas: `issuer`, `status`, `type` (I, E, P, T o N), `customer` (RFC del cliente), `created_gte` y `created_lte`. - Constancias de retenciones: `issuer`, `status`, `recipient` (RFC) y `year`. - Clientes: `tax_id`. Productos: `sku` y `product_key`. - Eventos: `type` (`invoice.stamped`…) y `object` (el id del comprobante, para ver todo lo que le pasó). Las listas de comprobantes, clientes, productos, empleados y lotes son de un emisor: mande `issuer` si la cuenta tiene varios. Los catálogos del SAT (`q` busca por clave o descripción) y los anticipos contestan una sola lista, sin páginas. La [referencia](https://fiscalisto.com/docs/api/referencia/facturas/list-invoices) de cada ruta dice cuáles acepta. --- # Expandir objetos > Pida con expand[] que los ids de otros objetos lleguen como esos objetos, en la misma respuesta, sin una llamada más por cada uno. Muchos objetos guardan el id de otro: una factura tiene su `issuer` y su `recurring_invoice`, una factura recurrente su `last_invoice`, un ticket su `invoice`. Con `expand[]` ese id llega como el objeto completo, en la misma respuesta: ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/invoices/inv_OufpKqnrvx9p1?expand[]=issuer&expand[]=recurring_invoice" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.get("inv_OufpKqnrvx9p1", { "expand[]": "recurring_invoice" }); ``` ```json { "id": "inv_OufpKqnrvx9p1", "object": "invoice", "issuer": { "id": "iss_OufpKqnrvx9p", "object": "issuer", "legal_name": "ESCUELA KEMPER URGATE", "…": "…" }, "recurring_invoice": { "id": "rin_OufpKqnrvx9p1", "object": "recurring_invoice", "…": "…" } } ``` Sin `expand[]`, los ids siguen siendo ids. ## Cómo se escribe - Un parámetro `expand[]` por campo (`expand[]=issuer&expand[]=invoice`), o varios separados por comas (`expand=issuer,invoice`). - Con puntos se entra a un objeto expandido: `expand[]=recurring_invoice.last_invoice`. Hasta 4 niveles. - En una lista, cada elemento se expande: `expand[]=data.issuer`, o solo `expand[]=issuer`. - Hasta 10 campos por solicitud, y hasta 200 objetos por respuesta (cada id se lee una vez, aunque se repita en la página). - Sirve en `GET` y en `POST`: `POST /v1/products?expand[]=issuer` contesta el producto creado con su emisor. Se expande cualquier campo que guarda el id de otro objeto (`iss_`, `inv_`, `cus_`, `prd_`, `rin_`, `quo_`, `tkt_`, `btx_`, `emp_`, `ret_`, `bat_`, `sdl_`, `sat_`, `stc_`, `cdr_`, `whe_`, `evt_`, `sbx_`). Cada objeto se lee con su misma llave, como lo haría su `GET`: una llave restringida a ciertos emisores no ve nada fuera de ellos. Un campo que la respuesta no tiene, o que no es el id de otro objeto, responde `400` con `expand_invalid` en un `GET`. En un `POST` lo que pidió ya se hizo, así que la respuesta llega sin expandir. Si el objeto ya no existe, queda su id. Un campo en `null` se queda en `null`. ## En los SDKs ```ts const invoice = await fiscalisto.invoices.get("inv_…", { expand: ["issuer", "recurring_invoice"] }); ``` ```python invoice = fiscalisto.invoices.get("inv_…", expand=["issuer", "recurring_invoice"]) ``` ```php $invoice = $fiscalisto->invoices->get('inv_…', [], ['expand' => ['issuer', 'recurring_invoice']]); ``` Los tipos de los SDKs dicen el id (`issuer: string`); con `expand` el campo trae el objeto. En Go, Java y .NET, que convierten cada campo a su tipo, lea el objeto relacionado con su propio `get`. --- # Webhooks > Reciba en su servidor cada factura timbrada, rechazada o cancelada, con eventos firmados que se reintentan durante un día. Un webhook es una dirección de su servidor a la que Fiscalisto manda un evento cada vez que cambia un comprobante de su cuenta. Con ellos su sistema se entera de lo que no pasa en la respuesta de una solicitud: una cancelación que el cliente acepta días después, una factura que el PAC tardó en timbrar o un lote que terminó. ## Tipos de evento Son todos los que hay. Cada uno trae en `data.object` el objeto completo, igual que su `GET`, con el estado que tenía cuando pasó. Abra uno para ver el evento entero tal como llega, campo por campo. | Evento | Cuándo llega | `data.object` | | --- | --- | --- | | [`invoice.stamped`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-stamped) | Se timbró un CFDI: factura, complemento de pago, nota de crédito, anticipo, su nota de aplicación, sustitución o recibo de nómina. Llega también cuando uno que quedó en `processing` (el PAC no contestó) se timbra después. | Factura (`inv_…`), `status: "valid"` | | [`invoice.failed`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-failed) | El SAT o el PAC rechazaron un CFDI. Llega aunque la solicitud ya le haya contestado `422`, y cuando uno en `processing` termina rechazado. | Factura, `status: "rejected"`, con `error` | | [`invoice.cancellation_pending`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-cancellation-pending) | Pidió la cancelación y el cliente debe aceptarla en su buzón tributario (vea [cuándo](https://fiscalisto.com/docs/api/cancelacion#cuándo-debe-aceptarlo-el-cliente)). Sigue vigente mientras tanto. | Factura, `status: "pending"`, `cancellation.status: "pending"` | | [`invoice.canceled`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-canceled) | Se canceló: de inmediato, o porque el cliente aceptó o no contestó en 3 días hábiles. | Factura, `status: "canceled"`, `cancellation.status: "accepted"` | | [`invoice.cancellation_rejected`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-cancellation-rejected) | El cliente rechazó la cancelación, o el SAT nunca recibió la solicitud. Sigue vigente; puede pedirla otra vez. | Factura, `status: "valid"`, `cancellation.status: "rejected"` o `"lapsed"` | | [`invoice.partially_paid`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-partially-paid) | Llegó un pago por una parte de una factura PPD: un complemento de pago o una nota de crédito. Ver [Cuentas por cobrar](https://fiscalisto.com/docs/api/cuentas-por-cobrar). | Factura, con lo que falta en `amount_due` | | [`invoice.paid`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-paid) | Una factura de ingreso ya no debe nada: su último complemento, una nota de crédito, un depósito conciliado o `mark_paid`. | Factura, con `payment_status: paid` | | [`retention.stamped`](https://fiscalisto.com/docs/api/referencia/eventos/retention-stamped) | Se timbró una constancia de retenciones. | Constancia (`ret_…`), `status: "valid"` | | [`retention.failed`](https://fiscalisto.com/docs/api/referencia/eventos/retention-failed) | El SAT o el PAC rechazaron una constancia. | Constancia, `status: "rejected"` | | [`retention.canceled`](https://fiscalisto.com/docs/api/referencia/eventos/retention-canceled) | Se canceló una constancia (no piden aceptación). | Constancia, `status: "canceled"` | | [`invoice_batch.completed`](https://fiscalisto.com/docs/api/referencia/eventos/invoice-batch-completed) | Ya no queda ninguna factura del lote por timbrar: cada una se timbró o falló. Cada factura del lote manda además su `invoice.stamped` o `invoice.failed`. | Lote (`bat_…`), `status: "completed"` | | [`issuer.ready`](https://fiscalisto.com/docs/api/referencia/eventos/issuer-ready) | Un emisor de producción ya puede timbrar: tiene su CSD y firmó el contrato con el PAC. En pruebas, al terminar una [liga de alta](https://fiscalisto.com/docs/api/emisores#que-su-cliente-suba-sus-archivos). | Emisor (`iss_…`), `status: "active"` | | [`customer_data_request.completed`](https://fiscalisto.com/docs/api/referencia/eventos/customer-data-request-completed) | Un cliente mandó sus datos fiscales con una [liga](https://fiscalisto.com/docs/api/datos-del-cliente#pedirle-sus-datos-al-cliente). | Solicitud (`cdr_…`), `status: "completed"` | | [`sat_download.completed`](https://fiscalisto.com/docs/api/referencia/eventos/sat-download-completed) | Terminó una [descarga del SAT](https://fiscalisto.com/docs/api/descargas-del-sat): sus facturas ya están en `GET /v1/sat_invoices`. | Descarga (`sdl_…`), `status: "completed"` | | [`sat_download.failed`](https://fiscalisto.com/docs/api/referencia/eventos/sat-download-failed) | El SAT no pudo terminar una descarga; el motivo viene en `error`. | Descarga (`sdl_…`), `status: "failed"` | | [`sat_declaration.created`](https://fiscalisto.com/docs/api/referencia/eventos/sat-declaration-created) | Una [consulta de declaraciones](https://fiscalisto.com/docs/api/declaraciones-del-sat) encontró una nueva. Sus archivos y cifras pueden llegar después. | Declaración (`dcl_…`) | | [`sat_declaration.updated`](https://fiscalisto.com/docs/api/referencia/eventos/sat-declaration-updated) | Llegó el PDF, el acuse, los estados financieros o las cifras de una declaración. | Declaración (`dcl_…`), como queda | | [`ticket.invoiced`](https://fiscalisto.com/docs/api/referencia/eventos/ticket-invoiced) | Su comprador facturó un ticket en el portal de [autofactura](https://fiscalisto.com/docs/api/autofactura). Llega también el `invoice.stamped` de su factura. | Ticket (`tkt_…`), `status: "invoiced"`, con la factura en `invoice` | | [`recurring_invoice.failed`](https://fiscalisto.com/docs/api/referencia/eventos/recurring-invoice-failed) | Una [factura recurrente](https://fiscalisto.com/docs/api/facturas-recurrentes) no pudo timbrar su periodo (sin timbres, el cliente borrado, el SAT la rechazó). Llega una vez; se intenta de nuevo cada día. | Factura recurrente (`rin_…`), con el motivo en `last_error` | | [`quote.viewed`](https://fiscalisto.com/docs/api/referencia/eventos/quote-viewed) | El cliente abrió por primera vez la liga de una [cotización](https://fiscalisto.com/docs/api/cotizaciones). | Cotización (`quo_…`) | | [`quote.accepted`](https://fiscalisto.com/docs/api/referencia/eventos/quote-accepted) | Se aceptó una cotización: el cliente en su liga o usted con `POST /v1/quotes/{id}/accept`. | Cotización, con `decided_by` | | [`quote.changes_requested`](https://fiscalisto.com/docs/api/referencia/eventos/quote-changes-requested) | El cliente pidió cambios en la liga. A lo más una vez cada 10 minutos. | Cotización, con lo que escribió en `customer_comment` | | [`quote.rejected`](https://fiscalisto.com/docs/api/referencia/eventos/quote-rejected) | El cliente rechazó la cotización en su liga. | Cotización, con su motivo en `customer_comment` | | [`ticket.included_in_global`](https://fiscalisto.com/docs/api/referencia/eventos/ticket-included-in-global) | Un ticket que nadie facturó entró en la factura global del periodo. | Ticket (`tkt_…`), `status: "global"`, con la factura global en `global_invoice` | | [`balance.low`](https://fiscalisto.com/docs/api/referencia/eventos/balance-low) | A la cuenta le quedan pocos timbres de producción: al bajar a `low_balance_at` y otra vez al acabarse, una vez cada uno hasta que vuelva a tener más. Ver [Qué cuesta un timbre](https://fiscalisto.com/docs/api/pruebas-y-produccion#qué-cuesta-un-timbre). | Saldo (`object: "balance"`), como `GET /v1/balance`; siempre `livemode: true` | | [`test_clock.ready`](https://fiscalisto.com/docs/api/referencia/eventos/test-clock-ready) | Terminó de adelantarse un [reloj de prueba](https://fiscalisto.com/docs/api/pruebas-y-produccion#adelantar-el-tiempo-con-un-reloj-de-prueba): ya se hizo lo de cada día. Solo en pruebas. | Reloj de prueba (`object: "test_clock"`) | Lo que no manda evento: crear o cambiar clientes, productos, emisores y webhooks (eso lo hace su sistema y ya tiene la respuesta), y los envíos por correo. ```json title="invoice.stamped" { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.stamped", "api_version": "2026-10-24", "livemode": false, "created_at": "2026-10-24T10:15:42-06:00", "data": { "object": { "id": "inv_OufpKqnrvx9p28SINJVjjWKYRhojBTJb8P", "object": "invoice", "status": "valid", "...": "..." } } } ``` | Campo | Qué es | | --- | --- | | `id` | El id del evento. Es el mismo en el encabezado `webhook-id` y en cada reintento: úselo para no procesarlo dos veces. | | `type` | Uno de los eventos de la tabla. | | `api_version` | La versión de la API con la que se armó el objeto. | | `livemode` | `false` si viene de pruebas (`fk_test_`), `true` de producción. | | `created_at` | Cuándo pasó, hora de la Ciudad de México. | | `data.object` | El objeto completo. | La página de cada evento (en la tabla de arriba) tiene un ejemplo completo de lo que manda, y [`GET /v1/events`](https://fiscalisto.com/docs/api/referencia/eventos/list-events) devuelve los de los últimos 30 días. ## Agregar un webhook Cada `url` va en un solo endpoint por modo, para que ningún evento llegue dos veces: crear otro con la misma contesta `400` `endpoint_exists` con el id del que ya la tiene, para cambiarlo con `POST /v1/webhook_endpoints/{id}`. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/webhook_endpoints \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "events": ["invoice.stamped", "invoice.failed", "invoice.canceled"] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookEndpoint = await fiscalisto.webhookEndpoints.create({ url: "https://mi-plataforma.mx/webhooks/fiscalisto", events: [ "invoice.stamped", "invoice.failed", "invoice.canceled", ], }); ``` La respuesta trae el `secret` (`whsec_…`) una sola vez: guárdelo en su servidor. Sin `events` recibe todos. También puede agregarlo en **Desarrolladores › Webhooks**. Cada modo tiene sus propios webhooks: uno creado con una llave `fk_test_` solo recibe eventos de pruebas. La dirección debe ser `https` y pública. No se siguen redirecciones: use la dirección final. Para probar en su computadora no hace falta un túnel: `npx fiscalisto listen --forward-to http://localhost:3000/webhooks/fiscalisto` le reenvía los eventos de su cuenta de pruebas firmados igual que los de verdad, y `npx fiscalisto trigger invoice.stamped` provoca uno (vea [CLI](https://fiscalisto.com/docs/api/cli#webhooks-en-su-computadora)). Si prefiere registrar un endpoint de verdad, abra un túnel (`cloudflared tunnel --url http://localhost:3000` o `ngrok http 3000`) y registre la dirección `https` que le da. ## Verificar la firma Cada evento llega como `POST` con tres encabezados, según [Standard Webhooks](https://www.standardwebhooks.com): | Encabezado | Qué es | | --- | --- | | `webhook-id` | El id del evento (`evt_…`). Es el mismo en cada reintento. | | `webhook-timestamp` | Cuándo se mandó este intento, en segundos desde 1970. | | `webhook-signature` | `v1,` y la firma HMAC-SHA256 en base64. Puede traer varias separadas por espacio. | Verifique siempre la firma antes de confiar en el evento: cualquiera puede mandar un `POST` a su dirección. Con los [SDKs](https://fiscalisto.com/docs/api/sdk#webhooks) es una línea y no necesita la llave de la API: `verifyWebhook(cuerpo, encabezados, secreto)` en TypeScript, `verify_webhook(…)` en Python, `Webhook::verify(…)` en PHP, `fiscalisto.VerifyWebhook(…)` en Go, `Webhooks.verify(…)` en Java y `FiscalistoClient.Webhooks.Verify(…)` en C#. En otro lenguaje, la librería `standardwebhooks` hace lo mismo (Ruby, Rust y los demás). ```ts title="Node.js (Express) con el SDK" import express from "express"; import { verifyWebhook } from "fiscalisto"; const app = express(); // El cuerpo tal como llegó, sin convertirlo a JSON: la firma es de esos bytes. app.post("/webhooks/fiscalisto", express.raw({ type: "application/json" }), async (req, res) => { let event; try { event = await verifyWebhook(req.body, req.headers, process.env.FISCALISTO_WEBHOOK_SECRET); } catch { return res.status(400).send("Firma inválida"); } res.sendStatus(200); // Responda rápido; procese después. procesar(event); }); ``` ```python title="Python (Flask) con el SDK" import os from flask import Flask, request from fiscalisto import FiscalistoWebhookError, verify_webhook app = Flask(__name__) @app.post("/webhooks/fiscalisto") def fiscalisto(): try: event = verify_webhook( request.get_data(), request.headers, os.environ["FISCALISTO_WEBHOOK_SECRET"] ) except FiscalistoWebhookError: return "Firma inválida", 400 procesar(event) return "", 200 ``` ### Sin librería La firma es el HMAC-SHA256 de `{webhook-id}.{webhook-timestamp}.{cuerpo}` con su secreto sin el prefijo `whsec_`, decodificado de base64: ```python title="verificar.py" import base64, hashlib, hmac, json, time def verificar(secreto, headers, cuerpo: bytes): msg_id = headers["webhook-id"] timestamp = headers["webhook-timestamp"] if abs(time.time() - int(timestamp)) > 300: raise ValueError("Evento viejo: puede ser una repetición maliciosa") llave = base64.b64decode(secreto.removeprefix("whsec_")) firmado = f"{msg_id}.{timestamp}.".encode() + cuerpo esperada = base64.b64encode(hmac.new(llave, firmado, hashlib.sha256).digest()).decode() for firma in headers["webhook-signature"].split(" "): version, _, valor = firma.partition(",") if version == "v1" and hmac.compare_digest(valor, esperada): return json.loads(cuerpo) raise ValueError("Firma inválida") ``` Rechace los eventos con más de 5 minutos de diferencia en `webhook-timestamp`: así nadie puede repetirle uno viejo. ## Probar sus webhooks Con una llave `fk_test_` todo pasa en pruebas y sus webhooks de pruebas reciben los eventos de verdad: 1. Agregue un webhook de pruebas con la dirección de su túnel (vea arriba). 2. Timbre una factura con su llave de pruebas: con `curl`, con un SDK o con **Probar** en la [referencia](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice). Le llega `invoice.stamped`. 3. Para `invoice.failed`, timbre a uno de los [RFC de prueba](https://fiscalisto.com/docs/api/pruebas-y-produccion#provocar-cada-rechazo-del-sat), como `NOM010101FK9`; para `invoice.canceled`, cancele la factura con `"motive": "02"`. 4. En **Desarrolladores › Webhooks** vea cada entrega con lo que contestó su servidor, y reenvíela cuantas veces quiera mientras corrige su código. **Desarrolladores › Eventos** tiene todos los eventos, con lo que se mandó y cada entrega. ### Un evento de prueba, sin timbrar Para probar su código con cualquier evento, mande uno de prueba a su webhook. Le llega en ese momento, una sola vez, firmado como los reales, con un objeto de ejemplo (sus id no existen), aunque el webhook no escuche ese tipo: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/webhook_endpoints/whe_8Kq2ZtR4mNw1Lp0sXc7V/test \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "invoice.failed" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookTest = await fiscalisto.webhookEndpoints.test("whe_8Kq2ZtR4mNw1Lp0sXc7V", { type: "invoice.failed" }); ``` La respuesta trae el evento que se mandó y lo que contestó su servidor: ```json { "object": "webhook_test", "event": { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.failed", "...": "..." }, "delivery": { "ok": true, "status": 200, "error": null, "response_body": "{\"received\":true}", "duration_ms": 182 } } ``` Sin `type` manda `invoice.stamped`. En el panel es **Mandar evento de prueba**, en cada webhook. El evento de prueba no sale en `GET /v1/events`, pero sí en las entregas del webhook, y puede reenviarlo. ## Responder - Conteste un código `2xx` en menos de 15 segundos. Cualquier otro código, una redirección o no contestar a tiempo cuenta como falla. - Conteste primero y procese después (en una cola o en segundo plano), para no pasar de los 15 segundos. - Un evento puede llegar más de una vez. Guarde el `webhook-id` de los que ya procesó e ignore los repetidos. - Los eventos pueden llegar en otro orden. Para saber el estado actual de un comprobante use el `status` del objeto que trae el evento o consúltelo con `GET`. ## Reintentos Si su servidor falla, reintentamos el mismo evento durante un día: a los 5 segundos, 5 minutos, 30 minutos, 2 horas, 5 horas, 10 horas y 10 horas. Cada intento trae el mismo `webhook-id` y un `webhook-timestamp` nuevo. Un webhook que falla todos los reintentos queda en `status: "failing"`. Si sigue así 5 días, se apaga (`"disabled"`) y deja de recibir eventos; vuelva a encenderlo con [`POST /v1/webhook_endpoints/{id}`](https://fiscalisto.com/docs/api/referencia/webhooks/update-webhook-endpoint) y `"disabled": false`, o en el panel. En **Desarrolladores › Webhooks** cada webhook muestra sus últimos 7 días (entregados, fallidos, tasa de error y respuesta promedio) y sus entregas con lo que contestó su servidor, y puede reenviar cualquiera. **Reenviar** es un intento aparte, ahora: no cambia los reintentos automáticos, y si llega, ya no se reintenta. Si se le pasaron eventos, [`GET /v1/events`](https://fiscalisto.com/docs/api/referencia/eventos/list-events) tiene los de los últimos 30 días; filtre por `type` (uno, o todos los de un recurso con un comodín: `invoice.*`), por `object` (el id del comprobante) o con `delivery_success=false` por los que algún webhook no recibió: ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/events?type=invoice.*&delivery_success=false" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.events.list({ type: "invoice.*", delivery_success: false })) { console.log(item.id); } ``` ## Cambiar el secreto [`POST /v1/webhook_endpoints/{id}/roll_secret`](https://fiscalisto.com/docs/api/referencia/webhooks/roll-webhook-secret) le da un secreto nuevo. El anterior sigue firmando 24 horas: mientras tanto cada evento trae dos firmas, una con cada secreto, y la verificación pasa con cualquiera. Cambie su servidor al nuevo dentro de ese plazo. Para otro plazo mande `previous_secret_expires_in` en segundos; si el secreto se filtró, mande `0` y el anterior deja de firmar en ese momento. ```bash tab="cURL" curl -X POST https://api.fiscalisto.com/v1/webhook_endpoints/whe_0q1Vmmw52sbDpmtsKD1E/roll_secret \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "previous_secret_expires_in": 0 }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookEndpoint = await fiscalisto.webhookEndpoints.rollSecret("whe_0q1Vmmw52sbDpmtsKD1E", { previous_secret_expires_in: 0 }); ``` --- # Límites de solicitudes > Cuántas solicitudes y cuántos timbres por segundo acepta la API, y qué hacer con un 429. | Límite | Producción | Pruebas | | --- | --- | --- | | Solicitudes por cuenta (todas sus llaves juntas) | 100 cada 10 segundos | 100 cada 10 segundos | | Comprobantes timbrados por emisor | 20 cada 10 segundos | 5 cada 10 segundos | | Comprobantes timbrados por cuenta al día | Sin tope (cada uno usa un timbre) | 1,000 | | RFC revisados en las listas del SAT al día | 200,000 | 5,000 | | Constancias consultadas en el SAT al día | 2,000 | 100 | | Ligas para pedir datos fiscales al día | 20,000 | 200 | | Ligas de alta de emisores al día | 20,000 | 200 | | Descargas pedidas al SAT al día | 300 | 50 | | Consultas de declaraciones al SAT al día | 2,000 | 100 | | Opiniones, constancias y buzón del emisor pedidos al SAT al día | 2,000 | 100 | | Tickets de autofactura registrados al día | 100,000 | 2,000 | | Facturas recurrentes activas por emisor | 5,000 | 25 | | Cotizaciones creadas al día | 20,000 | 200 | | Depósitos mandados a conciliación al día | 100,000 | 5,000 | | Reinicios de los datos de prueba al día | — | 20 | | Avances de relojes de prueba al día | — | 50 | | Entornos de prueba por cuenta | — | 25 | | Endpoints de webhooks por modo | 16 | 16 | | Tiendas conectadas por emisor | 10 | 10 | | Facturas por lote | 1,000 | 1,000 | | Emisores nuevos por cuenta | 500 en 24 horas | 20 en 24 horas | | Tamaño del cuerpo de una solicitud | 10 MB | 10 MB | Cada respuesta trae el límite de su cuenta en el encabezado `RateLimit-Policy`: `100;w=10` (100 solicitudes cada 10 segundos), en producción y en pruebas, para que su cliente reparta las solicitudes sin esperar un 429. Al pasarse, la API responde `429` con el código `rate_limit` (por cuenta), `stamp_rate_limit` (por emisor) o `daily_limit` (los topes del día) y el encabezado `Retry-After` con los segundos que hay que esperar; en `daily_limit`, los que faltan para la medianoche de la Ciudad de México. Espere ese tiempo y repita la solicitud; si timbra, con la misma `Idempotency-Key`. ## Lo que usa timbres sin timbrar En producción, cinco cosas usan timbres de su cuenta, además de su tope del día: | Qué | Timbres | | --- | --- | | RFC revisados en las listas del SAT | 1 por cada 10 | | Constancias consultadas en el SAT | 1 por cada 10 | | Descargas pedidas al SAT | 1 por cada 100 CFDI que traen (al menos 1) | | Consultas de declaraciones al SAT | 1 por consulta (una vez cada 10 minutos por emisor) | | Opinión de cumplimiento, constancia y buzón del emisor pedidos al SAT | 1 por consulta (cada uno una vez cada 10 minutos por emisor) | Se cuentan por mes de la Ciudad de México y una parte cuenta como entera: el primer RFC del mes toma un timbre que cubre hasta el décimo. Sin timbres, la API responde `402 insufficient_balance` y no cuenta la solicitud. Una constancia que ya consultó ese día, una descarga que el SAT rechaza y una consulta de declaraciones, opinión, constancia o buzón a la que el SAT no contesta no cuentan. Una descarga paga su primer timbre al pedirla y los demás cuando el SAT dice cuántos CFDI tiene, antes de descargarlos; sin timbres suficientes termina en `failed`. Lo que lleva el mes está en **Desarrolladores › Timbres**. En pruebas no se usan timbres. Los topes del día cuidan lo que no se paga con timbres: en pruebas todo es gratis, y las consultas al SAT salen de nuestros servidores. En pruebas los correos de las facturas solo llegan a las personas de su cuenta API; en producción, a quien usted indique. Repetir una solicitud que ya se timbró (`idempotent-replayed: true`) no cuenta contra el límite de timbres. ## Muchas facturas a la vez Para timbrar cientos o miles de facturas, como las cuotas de fin de mes, no las mande una por una: use un [lote](https://fiscalisto.com/docs/api/lotes). Un lote acepta hasta 1,000 facturas en una solicitud, las revisa todas antes de empezar y las timbra en segundo plano a la velocidad que permite el PAC, sin que usted tenga que manejar los límites. ## Si necesita más Escríbanos a [hola@fiscalisto.com](mailto:hola@fiscalisto.com) con su volumen y su caso. --- # El SAT para desarrolladores > Lo que necesita saber de la factura electrónica en México para integrarla bien: CFDI, timbre, PAC, CSD, régimen, uso, PUE y PPD. Si es la primera vez que integra facturación en México, esta página le explica las piezas en cinco minutos. Fiscalisto se encarga de las reglas; usted solo necesita entender qué está pidiendo. ## Las piezas | Término | Qué es | | --- | --- | | **CFDI** | Comprobante Fiscal Digital por Internet: la factura electrónica. Es un XML con un formato que define el SAT (hoy la versión 4.0). El PDF es solo una representación impresa. | | **SAT** | El Servicio de Administración Tributaria, la autoridad fiscal. Recibe una copia de cada CFDI. | | **Timbre** | La firma con la que un PAC certifica el CFDI y le asigna su folio fiscal (UUID). Sin timbre no es una factura válida. | | **PAC** | Proveedor Autorizado de Certificación: la empresa que el SAT autoriza para timbrar. Fiscalisto usa a Finkok; usted no necesita contratarlo. | | **UUID** | El folio fiscal: el identificador único del CFDI ante el SAT, como `5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718`. | | **RFC** | Registro Federal de Contribuyentes: 12 caracteres una empresa (persona moral), 13 una persona física. | | **CSD** | Certificado de Sello Digital: el certificado (`.cer` y `.key`) con el que el emisor sella sus CFDI. Lo tramita el emisor en el SAT. | | **e.firma** | La firma electrónica del contribuyente (antes FIEL). No sirve para sellar facturas, pero sí para tramitar el CSD y para autorizar al PAC. | | **Constancia de situación fiscal** | El documento del SAT con el nombre, régimen y código postal oficiales de un RFC. Los datos del cliente deben coincidir con ella. | ## Lo que lleva una factura - **Emisor**: quien factura. En la API es su `issuer`. - **Receptor**: a quien se le factura, el `customer`. Desde CFDI 4.0 su nombre, régimen fiscal y código postal deben ser exactamente los de su constancia de situación fiscal; si no, el SAT la rechaza. Es la causa más común de rechazo. - **Conceptos**: lo que se vende, los `items`. Cada uno lleva una clave de producto o servicio (`product_key`, del catálogo c_ClaveProdServ) y una clave de unidad (`unit_key`). Búsquelas en [`GET /v1/sat_catalogs/product_keys?q=…`](https://fiscalisto.com/docs/api/referencia/catalogos-del-sat/get-sat-catalog). - **Impuestos**: IVA trasladado (16%, 8% en la frontera, 0% o exento), IEPS, y las retenciones de IVA e ISR cuando aplican. Fiscalisto calcula los importes. ## Régimen y uso del CFDI El **régimen fiscal** (`tax_system`) es cómo tributa cada contribuyente: 601 general de personas morales, 612 personas físicas con actividad empresarial, 626 RESICO, 616 sin obligaciones fiscales, entre otros. El **uso del CFDI** (`use`) es para qué usará el cliente la factura: G03 gastos en general, G01 adquisición de mercancías, I04 equipo de cómputo, S01 sin efectos fiscales, y otros. Cada uso solo vale con ciertos regímenes; Fiscalisto lo revisa antes de timbrar y le dice si no corresponde. Si no lo manda, usa G03 (o S01 si el cliente es 616). ## PUE y PPD El **método de pago** dice si la factura ya está pagada: - **PUE**, pago en una sola exhibición: se paga en el mes en que se emite. Lleva la forma de pago real (`payment_form`: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 tarjeta de débito…). - **PPD**, pago en parcialidades o diferido: se paga después o en partes. La forma de pago es 99 (por definir) y cada pago que reciba se documenta con un [complemento de pago](https://fiscalisto.com/docs/api/pagos-y-notas-de-credito). Si no sabe si se lo pagarán este mes, use PPD. ## Tipos de comprobante | `type` | Comprobante | En la API | | --- | --- | --- | | I | Ingreso: una venta. También la factura global. | `POST /v1/invoices` | | E | Egreso: nota de crédito, descuento o devolución. | `POST /v1/credit_notes` | | P | Pago: el complemento de pago de una factura PPD. | `POST /v1/payments` | | T | Traslado: ampara mercancía en tránsito, normalmente con Carta Porte. | `POST /v1/invoices` con `"type": "T"` | | N | Nómina: el recibo de un empleado. | `POST /v1/payroll_receipts` | Las constancias de retenciones son otro tipo de documento (Retenciones 2.0), en [`POST /v1/retentions`](https://fiscalisto.com/docs/api/referencia/retenciones/create-retention). ## Público en general y factura global Si vende a alguien que no pide factura, esa venta se factura después en una **factura global** junto con las demás del periodo, al RFC genérico `XAXX010101000` con el nombre `PUBLICO EN GENERAL`. Vea la [receta](https://fiscalisto.com/docs/api/facturas#global-a-público-en-general). Un cliente extranjero sin RFC mexicano lleva el RFC genérico `XEXX010101000`, con su país y su número de registro fiscal en `customer.foreign`. ## Plazos que conviene conocer - La fecha de un CFDI puede ser de hasta 72 horas antes del momento de timbrarlo. - Los complementos de pago se timbran a más tardar el quinto día natural del mes siguiente al del pago. - Una cancelación de una factura de más de $1,000 espera la aceptación del cliente (vea [Cancelar y sustituir](https://fiscalisto.com/docs/api/cancelacion)). --- # Emisores > Agregue los RFC que timbran desde su cuenta, con su CSD y su e.firma, y sepa en todo momento si ya pueden timbrar. Un emisor es un RFC por el que su cuenta timbra. Una plataforma que factura por sus clientes tiene un emisor por cada uno; una empresa con varias razones sociales, uno por cada RFC. Todos usan los timbres de la misma cuenta. ## 1. Crear el emisor ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "metadata": { "cliente": "42" } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.create({ tax_id: "TOL190312KT4", legal_name: "TOLPI", tax_system: "601", zip: "64000", metadata: { cliente: "42" }, }); ``` Escriba `legal_name`, `tax_system` y `zip` exactamente como en la constancia de situación fiscal del RFC: el SAT rechaza los comprobantes si no coinciden. La razón social va sin el régimen de capital (sin «S.A. de C.V.»). El código postal es también el lugar de expedición de sus comprobantes. La respuesta es el emisor con `status: "needs_certificate"` y, en `requirement`, lo que falta. ## 2. Subir el CSD El certificado de sello digital (CSD) es con el que se sella cada comprobante. Se tramita en el SAT con la e.firma y son dos archivos, `.cer` y `.key`, con su contraseña. Mándelos en base64: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/certificates \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"cer\": \"$(base64 < CSD.cer | tr -d '\n')\", \"key\": \"$(base64 < CSD.key | tr -d '\n')\", \"password\": \"$CSD_PASSWORD\" }" ``` ```ts tab="TypeScript" import { readFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.uploadCertificate("iss_OufpKqnrvx9p", { cer: (await readFile("CSD.cer")).toString("base64"), key: (await readFile("CSD.key")).toString("base64"), password: process.env.CSD_PASSWORD!, }); ``` Fiscalisto revisa que sea un CSD (no una e.firma), que sea de ese RFC, que no esté vencido, que el `.key` corresponda y que la contraseña lo abra; si algo no, responde `certificate_invalid` con el motivo. Los archivos se guardan cifrados y solo se usan para sellar. Cuando el CSD vence, suba el nuevo con la misma ruta: el emisor conserva sus comprobantes y sus folios. ## 3. Firmar con la e.firma (solo producción) Antes de su primer timbre en producción, cada RFC firma una vez el contrato de servicio con el PAC usando su e.firma (antes FIEL): ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/efirma \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"cer\": \"$(base64 < efirma.cer | tr -d '\n')\", \"key\": \"$(base64 < efirma.key | tr -d '\n')\", \"password\": \"$EFIRMA_PASSWORD\" }" ``` ```ts tab="TypeScript" import { readFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.signEfirma("iss_OufpKqnrvx9p", { cer: (await readFile("efirma.cer")).toString("base64"), key: (await readFile("efirma.key")).toString("base64"), password: process.env.EFIRMA_PASSWORD!, }); ``` La e.firma se usa en ese momento para firmar y no se guarda. Si manda el CSD en lugar de la e.firma, o la de otro RFC, responde `efirma_invalid`. Para pedirle al SAT las facturas del emisor, agregue `"keep_for_downloads": true` y se guarda cifrada (vea [Descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat)). ## Que su cliente suba sus archivos Si factura por sus clientes, lo más fácil es no pedirles sus archivos: mándelos a una página de Fiscalisto donde ellos suben su CSD y firman con su e.firma. Ni los archivos ni las contraseñas pasan por su plataforma. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/onboarding_links \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "return_url": "https://miplataforma.mx/configuracion/facturas" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuerOnboardingLink = await fiscalisto.issuers.createOnboardingLink("iss_OufpKqnrvx9p", { return_url: "https://miplataforma.mx/configuracion/facturas", }); ``` ```json { "object": "issuer_onboarding_link", "issuer": "iss_OufpKqnrvx9p", "url": "https://fiscalisto.com/alta/fko_…", "return_url": "https://miplataforma.mx/configuracion/facturas", "expires_at": "2026-10-25T10:15:00-06:00" } ``` Abra `url` en el navegador de su cliente (con un enlace o una redirección, no dentro de un iframe). La página le pide solo lo que falta: el CSD, la firma del contrato o los dos, los revisa antes de mandarlos (que sean de ese RFC, vigentes y que la contraseña abra la llave) y al terminar le muestra **Volver a** su plataforma, que lleva a `return_url`. En ese momento llega el evento `issuer.ready`. La liga sirve 24 horas (hasta 168 con `expires_in_hours`) y solo para ese emisor; cree otra cuando la necesite, por ejemplo cuando su CSD esté por vencer. Tras 20 intentos con archivos o contraseñas equivocados deja de servir. En pruebas la página revisa los archivos sin guardarlos y tiene **Completar en pruebas**, para que pruebe el flujo completo sin un CSD real. ## Estados | `status` | Qué significa | Qué hacer | | --- | --- | --- | | `active` | Puede timbrar. | Nada. | | `needs_certificate` | Falta su CSD. | Paso 2. | | `needs_efirma` | Falta firmar el contrato con el PAC. | Paso 3. | | `certificate_expired` | Su CSD venció. | Suba el nuevo (paso 2). | | `archived` | Ya no timbra. | Cree otro emisor si lo necesita. | `certificate.expires_at` dice cuándo vence el CSD: avise a su cliente antes de esa fecha. Cuando un emisor de producción pasa a `active` (por la API, el panel o una liga de alta) llega el evento `issuer.ready`. ## Pruebas En pruebas no hace falta el CSD ni la e.firma: un emisor de pruebas nace `active` y timbra como el emisor de pruebas del SAT (`stamps_as: "EKU9003173C9"`), con el RFC que usted le dé. Si sube un CSD en pruebas se revisa igual que en producción, para que pruebe su flujo, pero se sigue timbrando con el de pruebas. ## Nómina Para timbrar recibos de nómina, el emisor necesita su registro patronal (`employer_registration`) y, si es persona física, su `curp`: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_registration": "Y5412345109" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.update("iss_OufpKqnrvx9p", { employer_registration: "Y5412345109" }); ``` ## Archivar [`DELETE /v1/issuers/{id}`](https://fiscalisto.com/docs/api/referencia/emisores/archive-issuer) archiva el emisor: deja de timbrar, pero sus comprobantes siguen disponibles y se pueden cancelar. El RFC de un emisor no cambia; si se equivocó, archívelo y cree otro. --- # Sucursales > Facture desde otros lugares además del domicilio fiscal del emisor: cada sucursal con su código postal, su dirección en el PDF y sus series. Un emisor factura desde su domicilio fiscal (la **Matriz**) y, si tiene más lugares, desde sus sucursales. El CFDI solo lleva el código postal del lugar (`LugarExpedicion`), así que cada sucursal tiene el suyo, y de él dependen también la hora de la `Fecha` (Cancún, Tijuana y Hermosillo no tienen la de la Ciudad de México) y el IVA 8 % de la franja fronteriza. Su dirección va en el PDF y, si se las da, sus facturas llevan sus propias series. Son las mismas de **Ajustes › Sucursales** en la app. ## Crear una sucursal ```bash tab="cURL" curl https://api.fiscalisto.com/v1/branches \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "neighborhood": "Centro", "city": "Monterrey" }, "phone": "81 1234 5678", "series": { "invoice": "MTY" } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const branch = await fiscalisto.branches.create({ name: "Sucursal Monterrey", zip: "64000", address: { street: "Av. Constitución", exterior: "1500", neighborhood: "Centro", city: "Monterrey", }, phone: "81 1234 5678", series: { invoice: "MTY" }, }); ``` Contesta la sucursal (`brn_…`), con `state` tomado de su código postal si no lo manda. Cada código postal es de una sola sucursal (otro igual contesta `branch_exists`, y dice cuál) y el del domicilio fiscal del emisor no es sucursal: es la Matriz. Hasta 200 por emisor. `series` tiene una serie por tipo de CFDI: `invoice` (facturas, tipo I), `credit_note` (notas de crédito, tipo E), `payment` (complementos de pago, tipo P) y `transfer` (traslados, tipo T). La que no da usa la del emisor. ## Facturar desde ella Mande `branch` en la factura: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pedido-1042" \ -H "Content-Type: application/json" \ -d '{ "branch": "brn_OufpKqnrvx9p3", "customer": { "tax_id": "URE180429TM6", "legal_name": "UNIVERSIDAD ROBOTICA ESPAÑOLA", "tax_system": "601", "zip": "86991" }, "payment_form": "03", "items": [{ "description": "Consultoría", "product_key": "80101500", "unit_price": 1000 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ branch: "brn_OufpKqnrvx9p3", customer: { tax_id: "URE180429TM6", legal_name: "UNIVERSIDAD ROBOTICA ESPAÑOLA", tax_system: "601", zip: "86991", }, payment_form: "03", items: [ { description: "Consultoría", product_key: "80101500", unit_price: 1000, }, ], }, { idempotencyKey: "pedido-1042" }); ``` La factura sale con `LugarExpedicion="64000"`, la serie `MTY` y la dirección de la sucursal en el PDF, y su objeto dice `branch: "brn_OufpKqnrvx9p3"`. Sin `branch`, sale de la Matriz (`branch: null`). Sus complementos de pago y notas de crédito siguen solos a la sucursal de la factura: no hace falta mandarla otra vez. `branch` también va en cada factura de un lote y en las facturas recurrentes. La `Fecha` es la hora de la sucursal: una factura de su sucursal de Cancún a las 11:30 pm del 31 en la Ciudad de México ya es del día 1 a las 12:30 am. ## Cambiarla, archivarla o borrarla `POST /v1/branches/{id}` cambia solo lo que manda; una serie en `null` vuelve a la del emisor. Una sucursal que ya facturó no cambia de código postal (sus facturas se reconocen por él): si se mudó, archívela y cree otra. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/branches/brn_OufpKqnrvx9p3 \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "archived": true }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const branch = await fiscalisto.branches.update("brn_OufpKqnrvx9p3", { archived: true }); ``` Archivada, ya no se factura desde ella (una factura con su `branch` se rechaza) y sus facturas la siguen nombrando; `"archived": false` la restaura. `DELETE /v1/branches/{id}` borra solo una que nunca facturó; con facturas contesta `branch_has_invoices`. `GET /v1/branches` las lista, las más recientes primero, sin las archivadas salvo con `include_archived=true`; `zip=64000` deja la de ese código postal. --- # Inventario > Lleve existencias de sus productos: cada factura saca lo que vende, y usted ve lo que hay en cada almacén, el kárdex y sus ajustes. Un producto con `track_stock: true` lleva existencias. Cada factura que lo vende (con su `product`, o con su SKU o código de barras como `NoIdentificacion`) saca lo que vende del almacén de su sucursal en el momento en que se timbra, y una factura cancelada lo regresa. Es el mismo Inventario de la app: lo que haga ahí (compras, traspasos, conteos) se ve aquí, y al revés. ## Almacenes El emisor tiene un almacén por lugar desde donde factura: el de la Matriz y uno por cada [sucursal](https://fiscalisto.com/docs/api/sucursales), que se crean solos. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/warehouses \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.warehouses.list(); ``` Una factura vende del almacén de su sucursal (`branch`), o del de la Matriz sin ella. Para que salga de otro, mande `warehouse` (`whs_…`) en la factura. ## Las existencias iniciales Marque el producto con `track_stock: true` (al crearlo o con `POST /v1/products/{id}`) y meta lo que tiene con un ajuste: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/stock_adjustments \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "initial", "note": "Inventario de apertura", "lines": [{ "product": "prd_OufpKqnrvx9p2", "quantity": 50, "unit_cost": 90 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const stockAdjustment = await fiscalisto.stockAdjustments.create({ reason: "initial", note: "Inventario de apertura", lines: [ { product: "prd_OufpKqnrvx9p2", quantity: 50, unit_cost: 90, }, ], }); ``` Sin `warehouse` va al almacén de la Matriz. Una cantidad positiva entra y una negativa sale; las entradas valen su `unit_cost` (pesos sin IVA, o el costo promedio de hoy sin él) y las salidas el costo del emisor (promedio o PEPS, el que eligió en la app). El motivo (`reason`) es `initial`, `surplus`, `shrinkage` (merma), `theft`, `damage`, `expiration`, `internal_use`, `gift`, `count` u `other`. Un producto con lotes o números de serie pide `lot` en cada línea. Si el emisor eligió en la app no vender sin existencias, una factura que pide más de lo que hay se rechaza antes de timbrarse, y el mensaje dice de qué producto. Lo que Inventario no puede hacer en un ajuste (un lote que no es de ese producto, otro ajuste del mismo almacén en ese momento) contesta `inventory_refused`. ## Lo que hay ```bash tab="cURL" curl https://api.fiscalisto.com/v1/products/prd_OufpKqnrvx9p2/stock \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.products.stock("prd_OufpKqnrvx9p2"); ``` Una fila por almacén: `on_hand` (lo que hay), `reserved` (lo apartado por cotizaciones aceptadas), `available` (lo que se puede vender), `value` y `average_cost`. `GET /v1/warehouses/{id}/stock` da lo mismo por producto dentro de un almacén, de 100 en 100. ## El kárdex `GET /v1/stock_movements` lista cada entrada y salida, la más reciente primero: `type` dice qué fue (`sale`, `purchase`, `adjustment`, `transfer_out`, `reversal`…) y `source` qué la causó (el UUID de la factura, el `adj_…` del ajuste). Filtre por `product`, `warehouse`, `type` y por día con `date_gte` y `date_lte`, en hora de la Ciudad de México: una venta a las 11:30 pm del 31 es del 31. --- # Datos fiscales del cliente > Revise el RFC, nombre, régimen, código postal y uso del CFDI de un cliente antes de timbrar, o mándele una liga para que él mismo los dé con su constancia. Casi todos los rechazos del SAT vienen de los datos del cliente: un nombre con «S.A. de C.V.», un código postal que no es el de su constancia, un régimen que no le toca. Hay dos maneras de tenerlos bien antes de timbrar. ## Revisar los datos que ya tiene `POST /v1/tax_ids/validate` revisa los datos sin timbrar ni gastar timbres, y le dice qué está mal y con qué valor corregirlo: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/tax_ids/validate \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tax_id": "EKU9003173C9", "legal_name": "Escuela Kemper Urgate, S.A. de C.V.", "tax_system": "601", "zip": "42501", "use": "G03" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const taxIdValidation = await fiscalisto.taxIds.validate({ tax_id: "EKU9003173C9", legal_name: "Escuela Kemper Urgate, S.A. de C.V.", tax_system: "601", zip: "42501", use: "G03", }); ``` ```json { "object": "tax_id_validation", "tax_id": "EKU9003173C9", "kind": "company", "valid": false, "checked_with_sat": false, "errors": [ { "param": "legal_name", "code": "legal_name_capital_regime", "message": "Sin «S.A. de C.V.»: en CFDI 4.0 el nombre va sin régimen de capital, como en la constancia.", "suggestion": "ESCUELA KEMPER URGATE" } ], "warnings": [], "sat_record": null, "sat_error": null, "sat_lists": { "object": "sat_list_check", "listed": false, "risk": "none" } } ``` Sin más, revisa lo que se puede saber sin preguntarle al SAT: el formato y la fecha del RFC, el nombre, que el régimen sea de persona moral o física según el RFC, que el código postal exista, que el uso del CFDI vaya con el régimen, los RFC genéricos y si el RFC está en las [listas del SAT](https://fiscalisto.com/docs/api/listas-del-sat) (eso llega en `warnings`). Cada problema trae `param`, un `code` estable para su código, un `message` para mostrarle a su usuario y, cuando se puede, `suggestion` con el valor correcto. ### Compararlos con la constancia Si además manda `cif_id`, el idCIF que viene en la constancia de situación fiscal (arriba a la derecha, junto al código QR), Fiscalisto consulta el registro del SAT y compara nombre, código postal y régimen con lo que usted mandó. Entonces `checked_with_sat` es `true` y `sat_record` trae lo que dice el SAT: ```json "sat_record": { "legal_name": "ESCUELA KEMPER URGATE", "zip": "42501", "tax_systems": ["601"] } ``` Cada consulta al SAT cuenta en el [límite del día](https://fiscalisto.com/docs/api/limites); la misma constancia consultada dos veces en 24 horas cuenta una. Si el SAT tarda o no contesta, la revisión llega sin él, con el motivo en `sat_error`, y puede repetirla después. ## Pedirle sus datos al cliente Si no tiene los datos, o no confía en los que le dieron, pídaselos con una liga. Su cliente la abre en Fiscalisto, sube su constancia (o escribe sus datos) y antes de aceptarlos se revisan igual que arriba. A usted le llega un webhook. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/customer_data_requests \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference": "Pedido 1042", "description": "Para facturar su pedido 1042", "expires_in_days": 7 }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customerDataRequest = await fiscalisto.customerDataRequests.create({ reference: "Pedido 1042", description: "Para facturar su pedido 1042", expires_in_days: 7, }); ``` ```json { "id": "cdr_…", "object": "customer_data_request", "status": "open", "url": "https://fiscalisto.com/datos/…", "reference": "Pedido 1042", "expires_at": "2026-10-31T12:00:00-06:00", "submission": null } ``` Mándele `url` a su cliente como quiera: en su correo, en su página de pedido o por WhatsApp. También puede mostrarla dentro de su página, con `fiscalisto.js` o `@fiscalisto/react`, como la [autofactura](https://fiscalisto.com/docs/api/autofactura#en-su-página-con-fiscalistojs). `reference` es su id del cliente o del pedido y `description` una línea de para qué son los datos; el cliente ve las dos arriba del formulario, y `reference` le regresa en el evento. La liga sirve 60 días si no dice otra cosa. Cuando el cliente manda sus datos llega el evento `customer_data_request.completed` con la solicitud completa. En `submission` vienen los datos y `customer`, el cliente (`cus_…`) del emisor que ya puede usar al facturar: ```json "submission": { "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PÉREZ LÓPEZ", "tax_system": "612", "zip": "06600", "use": "G03", "email": "juan@correo.mx", "source": "constancia", "customer": "cus_…", "applied": true } ``` Un cliente nuevo se guarda directo en el catálogo del emisor (`applied: true`). Si el RFC ya era un cliente suyo y los datos cambian, no se sobrescribe solo: `applied` es `false` y usted decide si actualizarlo con `POST /v1/customers/{id}`. Así una liga no puede cambiar a quién se le mandan las facturas. | Ruta | Qué hace | | --- | --- | | `POST /v1/customer_data_requests` | Crea una liga | | `GET /v1/customer_data_requests` | Las ligas del emisor, de la más nueva a la más vieja | | `GET /v1/customer_data_requests/{id}` | Una liga, con lo que mandó el cliente | | `DELETE /v1/customer_data_requests/{id}` | La liga deja de servir (una completada no se cancela) | `status` es `open`, `completed`, `expired` o `canceled`; `url` solo viene mientras la liga está abierta. --- # Recetas de facturación > Las facturas más comunes listas para copiar: con catálogo, a crédito (PPD), en dólares, con retenciones, global a público en general y en lote. Todas van a [`POST /v1/invoices`](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice) con su llave y una `Idempotency-Key` nueva. Solo cambia el cuerpo; lo que no manda tiene su valor usual (PUE, MXN, uso G03 o S01 con régimen 616, unidad E48, objeto de impuesto 02, IVA 16%, cantidad 1). ## Con su catálogo de clientes y productos Guarde una vez al cliente y al producto, y facture con sus ids. El producto pone su clave, unidad, precio e impuestos; puede cambiar la cantidad o el precio en cada concepto. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/customers \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customer = await fiscalisto.customers.create({ tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }); ``` ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pedido-A-1043" \ -H "Content-Type: application/json" \ -d '{ "customer": "cus_OufpKqnrvx9p1", "payment_form": "04", "items": [{ "product": "prd_OufpKqnrvx9p1", "quantity": 3 }], "send_email": true }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: "cus_OufpKqnrvx9p1", payment_form: "04", items: [ { product: "prd_OufpKqnrvx9p1", quantity: 3, }, ], send_email: true, }, { idempotencyKey: "pedido-A-1043" }); ``` Con `send_email` le llega al cliente el PDF y el XML al correo de su ficha (o al de `customer.email`); sin ninguno contesta `400` `parameter_missing`, para que no se timbre una factura que no le llega a nadie. Un cliente nuevo también puede ir completo en `customer`, como en el [inicio rápido](https://fiscalisto.com/docs/api); no se guarda en el catálogo. ## A crédito (PPD) Si el cliente le paga después o en partes, la factura va con método PPD y forma de pago 99 (por definir), que Fiscalisto pone sola. Cada pago se registra después con un [complemento de pago](https://fiscalisto.com/docs/api/pagos-y-notas-de-credito). ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: contrato-88-oct" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_method": "PPD", "items": [{ "description": "Implementación del sistema", "product_key": "81111500", "unit_price": 45000 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_method: "PPD", items: [ { description: "Implementación del sistema", product_key: "81111500", unit_price: 45000, }, ], }, { idempotencyKey: "contrato-88-oct" }); ``` ## En dólares Mande la moneda y el tipo de cambio del día (el que publica el DOF el día hábil anterior). Los precios van en dólares. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pedido-USD-17" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "currency": "USD", "exchange_rate": 18.35, "items": [{ "description": "Licencia anual", "product_key": "43232400", "unit_price": 1200 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "03", currency: "USD", exchange_rate: 18.35, items: [ { description: "Licencia anual", product_key: "43232400", unit_price: 1200, }, ], }, { idempotencyKey: "pedido-USD-17" }); ``` ## Con retenciones Las retenciones van por concepto, como tasa. Por ejemplo, un flete a una persona moral lleva retención de IVA del 4%: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: flete-5521" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "items": [{ "description": "Flete Monterrey–Saltillo", "product_key": "78101800", "unit_price": 8000, "iva_retention": 0.04 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "03", items: [ { description: "Flete Monterrey–Saltillo", product_key: "78101800", unit_price: 8000, iva_retention: 0.04, }, ], }, { idempotencyKey: "flete-5521" }); ``` La respuesta trae lo retenido en `taxes.iva_withheld` y `taxes.isr_withheld`, y el `total` ya con ellas restadas. Otras comunes: honorarios o arrendamiento de una persona física a una moral (`isr_retention: 0.10`, `iva_retention: 0.106667`) y RESICO a una persona moral (`isr_retention: 0.0125`). ## Global a público en general Lo que vendió con ticket a quien no pidió factura va en una factura global por día, semana, mes o bimestre. El cliente es el público en general: RFC `XAXX010101000`, nombre `PUBLICO EN GENERAL`, régimen `616` y, como código postal, el del emisor (en pruebas, el del emisor de pruebas del SAT: `42501`). Cada concepto es una venta, con el número del ticket en `sku`. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: global-2026-10" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "XAXX010101000", "legal_name": "PUBLICO EN GENERAL", "tax_system": "616", "zip": "42501" }, "global": { "periodicity": "04", "months": "10", "year": 2026 }, "payment_form": "01", "items": [ { "description": "Venta", "product_key": "01010101", "unit_key": "ACT", "sku": "T-1001", "unit_price": 431.03 }, { "description": "Venta", "product_key": "01010101", "unit_key": "ACT", "sku": "T-1002", "unit_price": 862.07 } ] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "XAXX010101000", legal_name: "PUBLICO EN GENERAL", tax_system: "616", zip: "42501", }, global: { periodicity: "04", months: "10", year: 2026, }, payment_form: "01", items: [ { description: "Venta", product_key: "01010101", unit_key: "ACT", sku: "T-1001", unit_price: 431.03, }, { description: "Venta", product_key: "01010101", unit_key: "ACT", sku: "T-1002", unit_price: 862.07, }, ], }, { idempotencyKey: "global-2026-10" }); ``` ## Muchas a la vez Para cientos o miles de facturas (las cuotas del mes, la facturación de un marketplace), use un [lote](https://fiscalisto.com/docs/api/lotes): una sola solicitud con hasta 1,000. ## Para saber más - Cada campo, con su nombre en el XML del SAT y su catálogo, está en la [referencia de `POST /v1/invoices`](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice). - Busque claves de producto y unidad con [`GET /v1/sat_catalogs/product_keys?q=…`](https://fiscalisto.com/docs/api/referencia/catalogos-del-sat/get-sat-catalog). - [Complementos](https://fiscalisto.com/docs/api/complementos): Carta Porte, comercio exterior, instituciones educativas, impuestos locales y más. --- # Autofactura > Registre cada venta como ticket y deje que su comprador se facture solo, en su página o en el portal del emisor; lo que nadie factura entra en la factura global. Con la autofactura usted no le pide datos fiscales a cada comprador. Registra la venta como **ticket**, le da al comprador su código (impreso en el ticket, en el correo de la compra o en su página) y él se factura solo: escribe el código y el total, da sus datos fiscales (escritos o leídos de su constancia) y la factura se timbra en ese momento con los timbres de su cuenta, con su PDF y su XML para descargar. Lo que nadie factura dentro del plazo entra solo en la **factura global** del periodo (público en general), timbrada la mañana después de que termina. ## Registrar una venta ```bash tab="cURL" curl https://api.fiscalisto.com/v1/tickets \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "folio": "A1042", "total": 580, "payment_form": "04", "product_key": "90101501", "description": "Consumo de alimentos", "sold_at": "2026-10-24T14:32:00-06:00" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const ticket = await fiscalisto.tickets.create({ folio: "A1042", total: 580, payment_form: "04", product_key: "90101501", description: "Consumo de alimentos", sold_at: "2026-10-24T14:32:00-06:00", }); ``` El concepto viene de `product_key` y `description` (con `iva`, 0.16 por omisión) o de un producto de su catálogo con `product: "prd_…"`. El `total` lleva los impuestos incluidos; la propina va aparte en `tip` y no entra en la factura. Contesta `201` con el ticket: ```json { "id": "tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "object": "ticket", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "folio": "A1042", "code": "A1042-K7QX", "status": "pending", "url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c?c=A1042-K7QX", "total": 580, "tip": 0, "payment_form": "04", "description": "Consumo de alimentos", "product_key": "90101501", "sold_at": "2026-10-24T14:32:00-06:00", "invoiceable_until": "2026-10-31", "invoice": null, "global_invoice": null, "customer": null, "created_at": "2026-10-24T14:32:05-06:00" } ``` El folio es único por emisor: registrar otra vez el mismo folio con el mismo total contesta el mismo ticket, así que puede reintentar sin miedo. Con otro total contesta `409 ticket_folio_taken`. ## Que su comprador se facture Tiene tres formas, todas con el mismo portal del emisor: 1. **El código en el ticket.** Imprima `code` y el portal del emisor (`portal_url` de [`GET /v1/issuers/{id}/self_invoicing`](https://fiscalisto.com/docs/api/referencia/autofactura/get-self-invoicing)). El comprador escribe el código y el total. 2. **La liga.** Mande `url` por correo o WhatsApp: ya trae el código, el comprador solo pone el total. 3. **Dentro de su página**, sin que el comprador salga de ella, con `fiscalisto.js` o con React (abajo). Cuando el comprador se factura llega `ticket.invoiced` (y `invoice.stamped`), y el ticket trae la factura en `invoice`. Si entra en la factura global llega `ticket.included_in_global`, con la factura global en `global_invoice`. ## En su página con fiscalisto.js ```html ``` El botón abre el portal en una ventana encima de su página. Desde JavaScript: ```js Fiscalisto.open(ticket.url, { onComplete: (e) => console.log("Factura timbrada", e.uuid), onClose: () => console.log("Cerró la ventana"), }); // O dentro de un elemento, tan alto como su contenido: Fiscalisto.mount("#factura", ticket.url, { onComplete: (e) => {} }); ``` Funciona igual con la liga de una [solicitud de datos fiscales](https://fiscalisto.com/docs/api/datos-del-cliente) (`customer_data_request.url`): `onComplete` llega con `{ kind: "customer_data" }`. ## En React ```bash npm install @fiscalisto/react ``` ```tsx import { FiscalistoEmbed, useFiscalistoDialog } from "@fiscalisto/react"; // Dentro de su página: console.log(e)} />; // En una ventana encima: const { open, dialog } = useFiscalistoDialog({ heading: "Facture su compra" }); ; {dialog} ``` > **Qué llega a su página.** Solo que el comprador terminó, y el UUID de su factura. Sus datos fiscales nunca pasan por su página: léalos con `GET /v1/tickets/{id}` o con el webhook `ticket.invoiced`. Solo se pueden mostrar así las ligas de autofactura y de datos fiscales; la liga de alta de un emisor, que pide su e.firma, siempre se abre aparte. ## El plazo y la factura global Por omisión un ticket se factura hasta el fin del mes de la venta (lo usual ante el SAT) y la factura global es mensual. Para cambiarlo: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/self_invoicing \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "window": "days", "window_days": 7, "global_periodicity": "02" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const selfInvoicing = await fiscalisto.issuers.updateSelfInvoicing("iss_OufpKqnrvx9p", { window: "days", window_days: 7, global_periodicity: "02", }); ``` `window: "days"` da `window_days` días desde la venta; `global_periodicity` es la c_Periodicidad de la factura global (01 diaria, 02 semanal, 03 quincenal, 04 mensual). Cada ticket dice hasta cuándo se puede facturar en `invoiceable_until`. ## Cancelar un ticket Una venta que no se hizo (una devolución, un error de caja) se cancela con `DELETE /v1/tickets/{id}` mientras siga pendiente: ya no se puede facturar ni entra en la factura global. Uno que ya se facturó contesta `ticket_not_pending`; cancele su factura con [`POST /v1/invoices/{id}/cancel`](https://fiscalisto.com/docs/api/referencia/facturas/cancel-invoice). ## Listar `GET /v1/tickets` da los tickets del más reciente al más antiguo por fecha de venta, con `status` (pending, invoiced, global, cancelled) y `sold_at_gte` / `sold_at_lte` para filtrar. En pruebas todo funciona igual con su llave `fk_test_`: los tickets se facturan sin costo en el ambiente de pruebas del PAC y las facturas no son CFDI reales. --- # Tiendas en línea > Conecte Shopify, Mercado Libre o WooCommerce: cada pedido pagado se vuelve un ticket de autofactura, y lo que nadie factura entra en la factura global. Cada pedido pagado de la tienda se vuelve un [ticket de autofactura](https://fiscalisto.com/docs/api/autofactura) del emisor, con el folio del pedido (`SHOP-1001`, `ML-2000001234`). Su comprador lo factura en el portal del emisor, y lo que nadie factura entra solo en la factura global del periodo. Un pedido cancelado, o reembolsado completo, cancela su ticket mientras nadie lo haya facturado. | Tienda | Cómo se conecta | Dónde corre | | --- | --- | --- | | Shopify | `POST /v1/store_connections` con las llaves de la app de la tienda | En Fiscalisto: no instala nada | | Mercado Libre | `POST /v1/store_connections` y el vendedor autoriza en Mercado Libre | En Fiscalisto | | WooCommerce | El plugin **Fiscalisto Autofactura** en su WordPress | En su servidor, con `POST /v1/tickets` | Un pedido llega una sola vez aunque la tienda lo mande varias: el folio es el mismo ticket. Solo se registran pedidos en pesos con total mayor a 0; los demás quedan en `last_error` de la conexión. ## Shopify 1. En el [Dev Dashboard](https://shopify.dev/docs/apps/build/dev-dashboard) de Shopify, cree una app en la organización de la tienda con los permisos `read_orders` y `write_orders`, e instálela en la tienda. 2. Conéctela con su Client ID y Client secret: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/store_connections \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "shopify", "shop": "mi-tienda.myshopify.com", "client_id": "8c1f0e7a2b3d4e5f6a7b8c9d0e1f2a3b", "client_secret": "shpss_…", "settings": { "product_key": "53101600" } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeConnection = await fiscalisto.storeConnections.create({ platform: "shopify", shop: "mi-tienda.myshopify.com", client_id: "8c1f0e7a2b3d4e5f6a7b8c9d0e1f2a3b", client_secret: "shpss_…", settings: { product_key: "53101600" }, }); ``` Fiscalisto registra los webhooks de pedidos pagados, cancelados y reembolsos, y comprueba la firma de cada uno con el Client secret, que se guarda cifrado y nunca se muestra. Las llaves de la app solo sirven para tiendas de su misma organización de Shopify (`store_connection_failed` si no). Una app personalizada antigua, hecha en el admin de la tienda, se conecta con su token `shpat_…` en `access_token` y su API secret key en `client_secret`. Cada pedido lleva su liga de autofactura en el metafield `fiscalisto.autofactura_url`. Para mostrarla en la página de estado del pedido y en el correo de confirmación, agréguela a sus plantillas: ```liquid {% if order.metafields.fiscalisto.autofactura_url %} Facturar mi compra {% endif %} ``` ## Mercado Libre Cree la conexión sin llaves. Queda `pending` con `authorize_url`: mande ahí al vendedor para que autorice a Fiscalisto en su cuenta de Mercado Libre. Al volver queda `active` (y, si mandó `return_url`, regresa a su página con `store_connection` y `status`). ```bash tab="cURL" curl https://api.fiscalisto.com/v1/store_connections \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "mercadolibre", "return_url": "https://su-plataforma.com/tiendas", "settings": { "product_key": "43211500" } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeConnection = await fiscalisto.storeConnections.create({ platform: "mercadolibre", return_url: "https://su-plataforma.com/tiendas", settings: { product_key: "43211500" }, }); ``` - Si el comprador dejó sus datos fiscales en Mercado Libre (RFC, nombre, régimen y código postal), la factura se timbra al pagarse el pedido. Si faltan, el ticket espera en el portal. Con `"auto_invoice": false` siempre espera. - Cada factura de un pedido (la del comprador, timbrada aquí o en el portal) se sube a Mercado Libre como documento fiscal del pedido, con su XML y su PDF. - Una cuenta de Mercado Libre se conecta a un solo emisor a la vez. El total del ticket es el de los artículos (`total_amount`): el envío que cobra Mercado Libre no entra. ## Cómo se vuelve ticket cada pedido `settings` decide el concepto y las claves del ticket. Se cambia con `POST /v1/store_connections/{id}` y cuenta desde el siguiente pedido. | Campo | Qué hace | | --- | --- | | `product_key` | La c_ClaveProdServ de lo que vende. Obligatoria. | | `unit_key`, `iva` | La c_ClaveUnidad y la tasa de IVA incluida en los precios (16 % si no la indica). | | `description` | Un concepto fijo. Si no lo indica, el pedido y sus artículos: «Pedido 1001: Playera (2), Gorra». | | `folio_prefix` | Lo que va antes del número del pedido: `SHOP-` o `ML-` si no lo indica. | | `payment_forms` | La c_FormaPago de cada pasarela (`shopify_payments`, `paypal`…) o tipo de pago de Mercado Libre (`credit_card`, `debit_card`, `account_money`, `ticket`…). | | `payment_form` | La que se usa cuando la del pedido no está en `payment_forms`. | Sin `payment_forms`, una tarjeta de Shopify es 04, PayPal y Mercado Pago 31, el pago contra entrega 01 y el depósito 03. En Mercado Libre, crédito 04, débito 28, dinero en cuenta 06, efectivo 01 y transferencia 03; lo demás, 31 (intermediario de pagos). La propina de Shopify va en `tip` y no entra en la factura. ## Pedidos que no llegaron Si conectó la tienda después de vender, o un webhook se perdió, `POST /v1/store_connections/{id}/import_orders` trae los pedidos pagados desde `since` (hasta 31 días atrás, 200 a la vez). Un pedido que ya era ticket no se repite. ## Desconectar `DELETE /v1/store_connections/{id}` borra sus webhooks y sus llaves, y los pedidos dejan de llegar. Los tickets que ya hizo siguen y se pueden facturar. ## WooCommerce El plugin **Fiscalisto Autofactura** corre en su WordPress con una llave de su cuenta API: registra cada pedido pagado con `POST /v1/tickets`, muestra la liga de autofactura en la página de gracias, en **Mi cuenta** y en los correos del pedido, y cancela el ticket si el pedido se cancela o se reembolsa completo. Si Fiscalisto no responde, lo reintenta solo. Se configura en **WooCommerce › Ajustes › Fiscalisto**. El plugin está en revisión en WordPress.org; en cuanto lo aprueben se instala desde Plugins › Añadir nuevo plugin. --- # Facturas recurrentes > Rentas, suscripciones e igualas: una factura que el emisor timbra solo cada mes, semana o día, a la hora que elija, sin que usted programe nada. Una **factura recurrente** se timbra sola en cada fecha, con los timbres de su cuenta, y se le manda al cliente si así lo pide. Usted no necesita un cron ni guardar en qué mes va: Fiscalisto timbra cada periodo una sola vez, aunque su servidor reintente o esté caído. ## Crear una ```bash tab="cURL" curl https://api.fiscalisto.com/v1/recurring_invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Renta oficina 3", "schedule": { "frequency": "monthly", "day_of_month": 1, "hour": 7 }, "invoice": { "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "payment_form": "03", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "unit_price": 15000 } ] } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const recurringInvoice = await fiscalisto.recurringInvoices.create({ name: "Renta oficina 3", schedule: { frequency: "monthly", day_of_month: 1, hour: 7, }, invoice: { customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, payment_form: "03", items: [ { description: "Renta de oficina de {mes} {año}", product_key: "80131500", unit_price: 15000, }, ], }, }); ``` `invoice` es el mismo cuerpo de [POST /v1/invoices](https://fiscalisto.com/docs/api/facturas), sin `issuer`, `folio`, `date`, `global` ni `metadata`: cada factura toma el folio siguiente y la fecha del día en que se timbra. Se revisa al crearla con las mismas reglas, así que un RFC mal escrito o una clave que no existe se rechazan ahora y no el día que toca timbrar. También puede usar un cliente de su catálogo (`"customer": "cus_…"`) o productos (`"product": "prd_…"`): cada factura toma los datos que el cliente tenga en ese momento. Contesta `201` con la factura recurrente (`rin_…`), su siguiente fecha en `next_invoice_on` y el total de cada factura con los precios de hoy. ### Cada cuánto | `schedule` | Qué hace | | --- | --- | | `"frequency": "monthly", "day_of_month": 1` | El día 1 de cada mes | | `"frequency": "monthly", "day_of_month": "last"` | El último día de cada mes | | `"frequency": "monthly", "interval": 12` | Una vez al año | | `"frequency": "weekly", "interval": 2` | Cada dos semanas desde `start_date` | | `"frequency": "daily"` | Todos los días | `day_of_month` va del 1 al 28, o `"last"`. `start_date` (AAAA-MM-DD) es la primera factura; si no la manda, es el próximo `day_of_month` (mensual) o hoy (semanal y diaria). `hour` (0 a 23, por omisión 7) es la hora en que se timbra, en `time_zone` (por omisión `America/Mexico_City`; también `America/Cancun`, `America/Mazatlan`, `America/Hermosillo`, `America/Tijuana` y `America/Ciudad_Juarez`). ### Textos que cambian cada mes En `description` escriba `{mes}`, `{mes anterior}`, `{mes siguiente}`, `{año}`, `{año anterior}`, `{año siguiente}` o `{periodo}` y cada factura los llena con su fecha: `Renta de oficina de {mes} {año}` sale como `Renta de oficina de noviembre 2026`. `{periodo}` sigue la frecuencia: el mes completo en una mensual (`1 al 30 de noviembre`), la semana en una semanal (`2 al 8 de noviembre`) y todos sus meses en una de varios meses (`1 de noviembre al 31 de diciembre`). La factura recurrente los guarda tal cual en `items`. ## Lo que timbra Cada factura es una factura normal: llega `invoice.stamped` a sus [webhooks](https://fiscalisto.com/docs/api/webhooks), aparece en `GET /v1/invoices` y trae `recurring_invoice` con el id de la recurrente que la timbró. `last_invoice` y `last_invoiced_on` dicen cuál fue la última. Si un periodo no se puede timbrar (se acabaron los timbres, el cliente se borró del catálogo, el SAT la rechazó), la recurrente guarda el motivo en `last_error`, llega `recurring_invoice.failed` una vez, y lo intenta de nuevo cada día a su hora hasta que salga. Con `catch_up: "all"` (por omisión) timbra después cada periodo que quedó pendiente, cada uno con su mes; con `"latest"`, solo el más reciente. ## Cambiarla, pausarla o timbrar ya ```bash tab="cURL" # Pausar (active: true la reanuda desde hoy, sin timbrar lo que pasó en pausa) curl https://api.fiscalisto.com/v1/recurring_invoices/rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "active": false }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const recurringInvoice = await fiscalisto.recurringInvoices.update("rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", { active: false }); ``` - `POST /v1/recurring_invoices/{id}` cambia solo lo que mande: `name`, `schedule`, `send_email`, `catch_up`, `active` o `invoice` (la factura completa desde la siguiente vez). `schedule.start_date` mueve la siguiente fecha. - `POST /v1/recurring_invoices/{id}/skip` salta la siguiente fecha sin timbrar. - `POST /v1/recurring_invoices/{id}/run` timbra ya el periodo que toca, sin esperar su hora; contesta la factura. Si aún no toca, `409 recurring_not_due`. Si su hora llega mientras tanto no se timbra dos veces: es el mismo periodo. - `DELETE /v1/recurring_invoices/{id}` la borra; las facturas que timbró se quedan. ## Límites Un emisor tiene hasta 5,000 facturas recurrentes activas en producción y 25 en pruebas (las pausadas no cuentan). En pruebas se timbran con el PAC de pruebas, como las demás. --- # Cotizaciones > Mande una cotización con liga: su cliente la ve, la acepta, pide cambios o la rechaza, y la factura se timbra con los mismos conceptos. Una **cotización** lleva los conceptos y las condiciones de pago de una factura. Su cliente la recibe con una liga donde la ve, descarga el PDF, la **acepta**, **pide cambios** o la **rechaza**, y usted se entera por [webhooks](https://fiscalisto.com/docs/api/webhooks). Al aceptarla, la factura sale de los mismos conceptos: sola, con `auto_invoice`, o cuando usted la pida. ## Crear una ```bash tab="cURL" curl https://api.fiscalisto.com/v1/quotes \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "compras@cva.example" }, "payment_form": "03", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "unit_price": 45000 } ], "terms": "50% de anticipo, entrega en 4 semanas.", "valid_until": "2026-11-30" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "compras@cva.example", }, payment_form: "03", items: [ { description: "Desarrollo del sitio web", product_key: "81112103", unit_price: 45000, }, ], terms: "50% de anticipo, entrega en 4 semanas.", valid_until: "2026-11-30", }); ``` `items`, `payment_form`, `payment_method`, `use`, `currency` y `notes` son los de [POST /v1/invoices](https://fiscalisto.com/docs/api/facturas) y se revisan con las mismas reglas, así que una cotización siempre se puede facturar. `valid_until` es su último día (por omisión, en 15 días; hasta un año) y `terms` sus condiciones, impresas en el PDF. `customer` puede ser: - Un cliente de su catálogo: `"customer": "cus_…"`. - Sus datos fiscales, como en una factura: se agrega a sus clientes. - Solo `name` y `email`, si aún no le da sus datos fiscales: `{ "name": "Juan Pérez", "email": "juan@correo.example" }`. Puede agregárselos después, o mandarla con `auto_invoice` para que los dé él al aceptarla. Contesta `201` con la cotización (`quo_…`) en borrador: su número (`COT-12`), el total con impuestos y `url: null` hasta que la mande. ## Mandarla ```bash tab="cURL" curl https://api.fiscalisto.com/v1/quotes/quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx/send \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Le comparto la cotización del sitio.", "auto_invoice": true }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.send("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", { message: "Le comparto la cotización del sitio.", auto_invoice: true, }); ``` Llega al correo del cliente (o a `to`, hasta 5 correos) con el PDF y la liga, que también viene en `url`. Con `"send_email": false` solo se marca enviada y contesta `url`, para que usted la mande por WhatsApp o desde su sistema. En pruebas el correo solo se envía a las personas de su cuenta API, como el de una factura. Con `auto_invoice: true`, al aceptarla la factura se timbra sola con los timbres de su cuenta: de inmediato si ya tiene los datos fiscales del cliente, o en cuanto los dé en la misma página si no. Una cotización en otra moneda no se timbra sola: el tipo de cambio de ese día puede no ser el de hoy. ## Lo que hace su cliente | Evento | Cuándo | | --- | --- | | `quote.viewed` | Abre la liga por primera vez (`status: viewed`). | | `quote.changes_requested` | Pide cambios; lo que escribió viene en `customer_comment`. | | `quote.accepted` | La acepta (`decided_by: customer`). Con `auto_invoice`, después llega `invoice.stamped`. | | `quote.rejected` | La rechaza, con su motivo en `customer_comment` si lo dio. | Si le dijo que sí por teléfono, márquela con `POST /v1/quotes/{id}/accept` (`decided_by: user`). ## Cambiarla `POST /v1/quotes/{id}` cambia solo lo que mande. Un borrador cambia ahí mismo. Una que ya se envió se vuelve una **versión nueva** en borrador, con el mismo número y el mismo id (`version` sube); su cliente sigue viendo la anterior hasta que mande la nueva con `/send`. Una aceptada, rechazada o facturada ya no cambia (`400 quote_invalid`). Una cotización vencida (`status: expired`) se revive cambiándole `valid_until` y mandándola otra vez. ## Facturarla ```bash tab="cURL" curl -X POST https://api.fiscalisto.com/v1/quotes/quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx/invoice \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: cot-12-factura" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.quotes.invoice("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", { idempotencyKey: "cot-12-factura" }); ``` Timbra la factura con sus conceptos y los datos fiscales del cliente y contesta la factura (`201`). Si no estaba aceptada, la marca aceptada; la cotización queda `invoiced` con la factura en `invoice`. Si ya se facturó, contesta esa misma factura: nunca se timbran dos. Sin los datos fiscales del cliente contesta `400 quote_customer_missing`; rechazada o vencida, `400 quote_not_invoiceable`. ## Lo demás - `GET /v1/quotes` las lista, las más recientes primero, cada una en su última versión; filtre con `status` (`draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired` o `invoiced`). - `GET /v1/quotes/{id}/pdf` descarga el PDF, con el diseño de PDF del emisor. - `DELETE /v1/quotes/{id}` borra una que nunca se envió; si ya se envió, `400 quote_not_draft`. Una cuenta crea hasta 20,000 cotizaciones al día en producción y 200 en pruebas ([Límites](https://fiscalisto.com/docs/api/limites)). --- # Pagos y notas de crédito > Registre los pagos de sus facturas PPD con complementos de pago y los descuentos o devoluciones con notas de crédito. ## Complementos de pago Una factura PPD (a crédito o en parcialidades) se cobra con un complemento de pago (CFDI tipo P) por cada pago que recibe. El SAT pide timbrarlo a más tardar el quinto día natural del mes siguiente al del pago. Usted solo dice qué facturas paga y cuánto de cada una. Fiscalisto calcula el número de parcialidad, el saldo anterior y el insoluto, y los impuestos de cada factura en proporción. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/payments \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pago-SPEI-2026102400123" \ -H "Content-Type: application/json" \ -d '{ "payment_form": "03", "operation_number": "SPEI-2026102400123", "documents": [{ "invoice": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "amount": 2900 }], "send_email": true }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.payments.create({ payment_form: "03", operation_number: "SPEI-2026102400123", documents: [ { invoice: "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", amount: 2900, }, ], send_email: true, }, { idempotencyKey: "pago-SPEI-2026102400123" }); ``` - `documents` acepta hasta 100 facturas del mismo cliente, por su id o su UUID. `amount` es lo que se paga de cada una, en la moneda de la factura. Solo se pueden pagar facturas timbradas con la API. - Sin `date`, el pago es de hoy. Si se lo pagaron otro día, mande `date` (`2026-10-20`). - Si la factura es en dólares y le pagaron en pesos, el pago va en MXN y cada documento lleva `amount` en dólares (lo que se abona a la factura) y `received` en pesos (lo que le llegó por ella). Si le pagaron en dólares, mande `currency: "USD"` y el `exchange_rate` del día del pago. - Si `amount` es mayor que el saldo de la factura, no se timbra: el error dice el saldo que queda. - Si el pago es un depósito que mandó a [Conciliación bancaria](https://fiscalisto.com/docs/api/conciliacion-bancaria), mande `bank_transaction` (`btx_…`): el complemento lleva la fecha del depósito y lo deja conciliado. La respuesta es un objeto `invoice` de `type: "P"`, con `payment` (forma, fecha, monto y cada documento con su parcialidad y saldos). Cada factura pagada baja su `amount_due` y llega `invoice.partially_paid` o `invoice.paid` ([Cuentas por cobrar](https://fiscalisto.com/docs/api/cuentas-por-cobrar)). ## Notas de crédito Un descuento, una bonificación o una devolución sobre una factura ya timbrada va en una nota de crédito (CFDI tipo E) relacionada a ella. `amount` es antes de impuestos: Fiscalisto aplica el IVA y las retenciones de la factura en la misma proporción. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/credit_notes \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: descuento-A-1042" \ -H "Content-Type: application/json" \ -d '{ "invoice": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "amount": 500, "description": "Descuento por pronto pago", "payment_form": "03" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.creditNotes.create({ invoice: "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", amount: 500, description: "Descuento por pronto pago", payment_form: "03", }, { idempotencyKey: "descuento-A-1042" }); ``` Por omisión la relación es `01` (nota de crédito); para una devolución de mercancía mande `"relationship": "03"`. Si la factura es PPD y todavía no se paga, una nota de crédito reduce su saldo para los complementos de pago siguientes. ## Anticipos Si el cliente le paga antes de saber qué va a comprar, timbre la factura del anticipo con [`POST /v1/advances`](https://fiscalisto.com/docs/api/referencia/anticipos/create-advance) (`amount` es lo que pagó, con IVA). Cuando se entregue lo vendido, [`POST /v1/advances/{id}/apply`](https://fiscalisto.com/docs/api/referencia/anticipos/apply-advance) timbra la factura de la venta relacionada al anticipo, con cualquiera de las dos opciones del SAT: con `"method": "egreso"` además una nota de crédito por lo anticipado; con `"method": "remanente"` la factura solo por lo que falta pagar. --- # Nómina > Dé de alta a sus empleados y timbre sus recibos de nómina ordinaria (CFDI tipo N con Nómina 1.2): el ISR, el subsidio para el empleo y las cuotas del IMSS los calcula Fiscalisto. Un **recibo de nómina** es un CFDI de tipo N con el complemento Nómina 1.2. Usted manda el empleado y el periodo; Fiscalisto calcula el sueldo de los días pagados, el ISR con la tarifa del artículo 96 y el subsidio para el empleo vigente, y las cuotas obreras del IMSS, como **Nómina › Emitir recibos** en la app. Nunca timbra montos que usted escriba a mano, así que un recibo siempre cuadra con lo que pide el SAT. Cada recibo usa un timbre, como cualquier factura. En pruebas se timbra con los emisores de prueba del SAT, sin costo. ## Antes del primer recibo El emisor necesita su **registro patronal** del IMSS (11 caracteres) y, si es persona física, su CURP: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employer_registration": "Y5412345109" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.update("iss_OufpKqnrvx9p", { employer_registration: "Y5412345109" }); ``` Sin él, un recibo contesta `422` `payroll_invalid` y lo dice. ## Dar de alta un empleado ```bash tab="cURL" curl https://api.fiscalisto.com/v1/employees \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "number": "9", "name": "Ana Gómez Ruiz", "tax_id": "GORA800101AB1", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "email": "ana@example.com", "start_date": "2024-03-04", "daily_salary": 320, "integrated_daily_salary": 334.66, "periodicity": "04", "state": "CMX" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const employee = await fiscalisto.employees.create({ number: "9", name: "Ana Gómez Ruiz", tax_id: "GORA800101AB1", curp: "GORA800101MDFMZN09", social_security_number: "12345678903", zip: "06000", email: "ana@example.com", start_date: "2024-03-04", daily_salary: 320, integrated_daily_salary: 334.66, periodicity: "04", state: "CMX", }); ``` El nombre, el RFC y el código postal van como en la constancia de situación fiscal del empleado: el SAT los compara. Lo que no mande tiene el valor usual de **Nuevo empleado**: | Campo | Por omisión | Qué es | | --- | --- | --- | | `contract_type` | `01` | Contrato por tiempo indeterminado (c_TipoContrato). | | `workday_type` | `01` | Jornada diurna (c_TipoJornada). | | `periodicity` | `04` | Quincenal. También `02` semanal, `03` catorcenal y `05` mensual. | | `risk_class` | `1` | Clase de riesgo del puesto ante el IMSS, de 1 a 5. | | `border_zone` | `false` | Si trabaja en la zona libre de la frontera norte (salario mínimo de la zona). | La respuesta trae `warnings` con lo que el SAT podría rechazar antes de timbrar, por ejemplo un RFC o una CURP cuyo dígito verificador no corresponde. No impiden guardarlo: revíselos con el empleado. Un RFC o un número de empleado que ya existe en el emisor contesta `400` `employee_exists`. Para cambiar un salario o dar de baja (`end_date`), use [`POST /v1/employees/{id}`](https://fiscalisto.com/docs/api/referencia/nomina/update-employee) con solo lo que cambia; los recibos ya timbrados no cambian. ## Ver el recibo antes de timbrarlo ```bash tab="cURL" curl https://api.fiscalisto.com/v1/payroll_receipts/preview \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "employee": "emp_OufpKqnrvx9p4gtWHOcPMsq25QUp6Licjr", "period": { "start": "2026-10-01", "end": "2026-10-15" } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const payrollReceiptPreview = await fiscalisto.payrollReceipts.preview({ employee: "emp_OufpKqnrvx9p4gtWHOcPMsq25QUp6Licjr", period: { start: "2026-10-01", end: "2026-10-15" }, }); ``` Contesta el recibo como se timbraría, sin timbrar ni usar timbres: ```json { "object": "payroll_receipt_preview", "periodicity": "04", "period": { "start": "2026-10-01", "end": "2026-10-15", "payment_date": "2026-10-15" }, "days": 15, "perceptions": [{ "type": "001", "key": "001", "concept": "Sueldo", "taxed": 4800, "exempt": 0 }], "deductions": [ { "type": "001", "key": "001", "concept": "Cuotas IMSS", "amount": 119.21 }, { "type": "002", "key": "002", "concept": "ISR", "amount": 80.85 } ], "isr": { "tariff": 345.15, "subsidy": 264.3, "withheld": 80.85, "exempt_minimum": false }, "totals": { "perceptions": 4800, "deductions": 200.06, "net": 4599.94 } } ``` `isr.tariff` es el ISR de la tarifa, `isr.subsidy` el subsidio para el empleo que le toca y `isr.withheld` lo que se le retiene. El periodo va en hora de la Ciudad de México; `payment_date` (la fecha de pago) es su último día si no la manda, y `days` son todos los días pagables del periodo (sin los anteriores a su ingreso ni los posteriores a su baja). Mande `days` para pagar menos, por ejemplo con faltas. ## Timbrarlo ```bash tab="cURL" curl https://api.fiscalisto.com/v1/payroll_receipts \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: nomina-2026-10-q1-9" \ -d '{ "employee": "emp_OufpKqnrvx9p4gtWHOcPMsq25QUp6Licjr", "period": { "start": "2026-10-01", "end": "2026-10-15" }, "send_email": true, "metadata": { "corrida": "2026-19" } }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.payrollReceipts.create({ employee: "emp_OufpKqnrvx9p4gtWHOcPMsq25QUp6Licjr", period: { start: "2026-10-01", end: "2026-10-15" }, send_email: true, metadata: { corrida: "2026-19" }, }, { idempotencyKey: "nomina-2026-10-q1-9" }); ``` La respuesta es una [factura](https://fiscalisto.com/docs/api/facturas) de `type: "N"` con su `uuid`, su PDF y su XML, y con el recibo en `payroll`. Llega también el webhook `invoice.stamped`. Con `send_email` el empleado recibe el PDF y el XML en su `email`; si no tiene, contesta `400` `parameter_missing` antes de timbrar. Un empleado tiene un solo recibo por periodo: pedirlo otra vez (aun con otra `Idempotency-Key`) contesta el que ya tiene, sin timbrar otro. Un periodo que se cruza con otro recibo vigente del mismo empleado, o más largo que su periodicidad (un mes en una nómina semanal), contesta `payroll_invalid`, para no pagar dos veces los mismos días. ## Corregir un recibo Un recibo timbrado no se edita. Cancélelo con [`POST /v1/invoices/{id}/cancel`](https://fiscalisto.com/docs/api/cancelacion) (no pide aceptación del empleado) y vuelva a timbrar el periodo: un periodo con su recibo cancelado se puede timbrar de nuevo. ## Listarlos Los recibos son facturas de tipo N: [`GET /v1/invoices?type=N`](https://fiscalisto.com/docs/api/referencia/facturas/list-invoices) los da, los más recientes primero, y [`GET /v1/employees`](https://fiscalisto.com/docs/api/referencia/nomina/list-employees) a los empleados. ## Lo que no hace - Nómina extraordinaria (aguinaldo, finiquito, PTU): solo nómina ordinaria. - Percepciones o deducciones que usted escriba (bonos, préstamos, Infonavit): el recibo lleva el sueldo, el ISR, el subsidio y las cuotas del IMSS. --- # Constancias de retenciones > Timbre la constancia de retenciones e información de pagos (CFDI de Retenciones 2.0) de lo que retuvo a un proveedor: honorarios, arrendamiento, dividendos, intereses o pagos al extranjero. Cuando una empresa le paga a una persona física por honorarios o arrendamiento, le retiene ISR e IVA y debe darle una **constancia de retenciones**: un CFDI de Retenciones 2.0, distinto de una factura, con lo que pagó y lo que retuvo en el periodo. Con la API la timbra con los mismos folios (RET1, RET2…), revisiones y timbres que **Retenciones** en la app. Cada constancia usa un timbre; cancelarla no cuesta. ## Timbrar una Lo que le retuvo en septiembre a un abogado por $10,000 de honorarios: 10% de ISR y dos terceras partes del IVA. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/retentions \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: ret-2026-09-gora" \ -d '{ "code": "01", "recipient": { "tax_id": "GORA800101AB1", "legal_name": "ANA GÓMEZ RUIZ", "zip": "06000" }, "period": { "year": 2026, "month": 9 }, "totals": { "operation": 10000 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000 }, { "tax": "iva", "base": 10000, "amount": 1066.67 } ], "send_email": true, "email": "ana@example.com" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.create({ code: "01", recipient: { tax_id: "GORA800101AB1", legal_name: "ANA GÓMEZ RUIZ", zip: "06000", }, period: { year: 2026, month: 9 }, totals: { operation: 10000 }, taxes: [ { tax: "isr", base: 10000, amount: 1000 }, { tax: "iva", base: 10000, amount: 1066.67 }, ], send_email: true, email: "ana@example.com", }, { idempotencyKey: "ret-2026-09-gora" }); ``` La respuesta es la constancia (`ret_…`) con `status: "valid"`, su `uuid`, su folio en `internal_folio` y el total retenido en `totals.withheld`. Llega también el webhook `retention.stamped`. Con `send_email` el receptor recibe el PDF y el XML en `email`. | Campo | Qué es | | --- | --- | | `code` | La clave de la retención (c_CveRetenc): `01` servicios profesionales, `05` arrendamiento, `14` dividendos, `16` intereses, `18` pagos a residentes en el extranjero, `25` otro tipo (con `description`), y las demás del catálogo. Las que llevan complemento están abajo. | | `recipient` | A quién le retuvo: su RFC, su nombre y su código postal como en su constancia de situación fiscal (el SAT los compara). Un residente en el extranjero va con `foreign: true`, su número de identificación fiscal en `tax_id` y sin código postal. | | `period` | El mes (`month`) o los meses (`from_month` y `to_month`) del año que cubre, ya terminados o el que corre. Una constancia anual de un proveedor va con `from_month: 1` y `to_month: 12`. | | `totals` | El monto de la operación; `taxed` y `exempt` si una parte está exenta (sin ellos, todo es gravado). | | `taxes` | Cada impuesto retenido: `isr`, `iva` o `ieps`, su base y el monto. `payment_type` es el tipo de pago del SAT (c_TipoPagoRet); por omisión `04` para ISR (pago provisional), `01` para IVA y `02` para IEPS. | Las claves que el SAT pide con complemento llevan su objeto, con los nombres de la referencia: | `code` | Objeto | Complemento del SAT | | --- | --- | --- | | `14` | `dividends` | Dividendos 1.0 | | `16` | `interest` | Intereses 1.0 | | `17` | `trust_lease` | Arrendamiento en fideicomiso 1.0 | | `18` | `foreign_payment` | Pagos a extranjeros 1.0 | | `19` | `share_sale` | Enajenación de acciones 1.0 | | `20` | `prize` | Premios 1.0 (`state` y los montos de `totals`) | | `21` | `trust` | Fideicomiso no empresarial 1.0 | | `22` | `retirement_plan` | Planes de retiro 1.1 | | `23` | `mortgage_interest` | Intereses hipotecarios 1.0 | | `24` | `financial_derivatives` | Operaciones con derivados 1.0 | Cada objeto va solo con su clave; con otra contesta `400`. Sin él, la constancia contesta `422` `retention_invalid` y dice qué falta. La clave `26` (servicios mediante plataformas tecnológicas) aún no se puede emitir: contesta `422`. ```json { "code": "20", "recipient": { "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ", "zip": "06700" }, "period": { "year": 2026, "month": 9 }, "totals": { "operation": 50000 }, "taxes": [{ "tax": "isr", "base": 50000, "amount": 500, "payment_type": "03" }], "prize": { "state": "09" } } ``` La misma `Idempotency-Key` otra vez contesta la misma constancia, sin timbrar otra. Si el SAT la rechaza, contesta `422` `retention_rejected` con su código en `sat_code`, y llega `retention.failed`. ## Mandarla por correo después ```bash tab="cURL" curl https://api.fiscalisto.com/v1/retentions/ret_PhBIUaMpw23r7krC5Vs3pDT6dB7C2wuY0D/send \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": "ana@example.com, contabilidad@example.com" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.send("ret_PhBIUaMpw23r7krC5Vs3pDT6dB7C2wuY0D", { to: "ana@example.com, contabilidad@example.com", }); ``` Sin `to`, va al correo que tiene guardado el receptor. Si no se pudo mandar, contesta `email_failed` y dice por qué; dos envíos a la vez de la misma constancia también (el segundo no se manda). ## Descargarla [`GET /v1/retentions/{id}/pdf`](https://fiscalisto.com/docs/api/referencia/retenciones/get-retention-pdf) da el PDF y [`/xml`](https://fiscalisto.com/docs/api/referencia/retenciones/get-retention-xml) el XML timbrado. [`GET /v1/retentions`](https://fiscalisto.com/docs/api/referencia/retenciones/list-retentions) las lista, las más recientes primero, y filtra por `status` (`valid`, `canceled`, `rejected`, `processing`), por `recipient` (un RFC) y por `year`. ## Cancelarla ```bash tab="cURL" curl https://api.fiscalisto.com/v1/retentions/ret_PhBIUaMpw23r7krC5Vs3pDT6dB7C2wuY0D/cancel \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "motive": "02" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.cancel("ret_PhBIUaMpw23r7krC5Vs3pDT6dB7C2wuY0D", { motive: "02" }); ``` Una constancia se cancela de inmediato: el receptor no tiene que aceptarlo. Llega `retention.canceled`. Con el motivo `01` (tenía errores y otra la sustituye), timbre antes la correcta y mándela en `substitution` (su `ret_…` o su UUID): debe ser otra constancia vigente del mismo emisor. --- # Cuentas por cobrar > Sepa qué facturas ya le pagaron, cuáles van en parcialidades y cuánto le debe cada cliente, y entérese de cada pago por webhook. Cada factura de ingreso dice si ya se cobró. Fiscalisto lo lleva con lo que ya timbra: los [complementos de pago](https://fiscalisto.com/docs/api/pagos-y-notas-de-credito), las notas de crédito y, en la app, los depósitos conciliados del banco. Usted no guarda saldos. ## En cada factura | Campo | Qué es | | --- | --- | | `payment_status` | `paid`, `partial` (pagos o notas por una parte) o `unpaid`. `null` si no es de ingreso o no está vigente. | | `amount_due` | Lo que falta por cobrar, en la moneda de la factura. | | `amount_paid` | Lo cobrado: complementos de pago y notas de crédito vigentes. | Una factura **PPD** empieza `unpaid` y baja con cada complemento de pago; si se cancela un complemento, su saldo vuelve. Una **PUE** está pagada desde que se timbra, salvo que la timbre con `"paid_later": true` (el cliente le paga este mes): queda `unpaid` hasta que la marque pagada. ```bash tab="cURL" curl -X POST https://api.fiscalisto.com/v1/invoices/inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN/mark_paid \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.markPaid("inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN"); ``` `POST /v1/invoices/{id}/mark_unpaid` hace lo contrario con una PUE ya timbrada. Si el mes termina y una PUE sigue sin pagarse, la ley pide sustituirla por una PPD (regla 2.7.1.39 de la RMF). Una PPD no se marca: se paga con su complemento (`400 invoice_not_markable`). ## Lo que le deben ```bash tab="cURL" curl https://api.fiscalisto.com/v1/receivables \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.receivables.list()) { console.log(item.id); } ``` Contesta lo que debe cada cliente, por moneda: `amount_due`, cuántas facturas tiene abiertas (`open_invoices`) y la fecha de la más antigua (`oldest_invoice_date`), ordenado por RFC. Con `?customer=RFC` (o su id `cus_…`), solo ese cliente. Sus facturas: `GET /v1/invoices?customer=RFC&payment_status=open` (`unpaid`, `partial`, `open` o `paid`). ## Cada pago, por webhook | Evento | Cuándo | | --- | --- | | `invoice.partially_paid` | Llegó un pago por una parte de una factura PPD (un complemento de pago o una nota de crédito). | | `invoice.paid` | Ya no debe nada: el último complemento, una nota de crédito, un depósito conciliado o `mark_paid`. | En los dos, `data.object` es la factura con su `payment_status` y su `amount_due`. Así su sistema da de alta el pago o libera el pedido sin preguntar. En pruebas funciona igual, con los complementos y las notas que timbre con su llave `fk_test_`. --- # Conciliación bancaria > Mande los depósitos de la cuenta del emisor y Fiscalisto le dice qué factura pagó cada uno; al conciliarlos, sus facturas quedan pagadas. Mande los depósitos de la cuenta bancaria del emisor, de la API de su banco o de un estado de cuenta que ya leyó. Fiscalisto sugiere la factura que pagó cada uno (por monto, RFC, folio en el concepto y fecha), y al conciliarlo la factura queda pagada en [Cuentas por cobrar](https://fiscalisto.com/docs/api/cuentas-por-cobrar) y llega `invoice.paid`. ## Mandar depósitos ```bash tab="cURL" curl https://api.fiscalisto.com/v1/bank_transactions \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "012180001234567891", "transactions": [ { "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA", "reference": "F-1024", "payer_tax_id": "ICV060329BY0" } ] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const bankImport = await fiscalisto.bankTransactions.create({ account: "012180001234567891", transactions: [ { date: "2026-10-24", amount: 5800, description: "SPEI RECIBIDO INMOBILIARIA CVA", reference: "F-1024", payer_tax_id: "ICV060329BY0", }, ], }); ``` Hasta 1,000 depósitos por solicitud, solo abonos y en pesos. Uno que ya mandó (el mismo día, monto, referencia y concepto) se omite, así que puede mandar cada día los últimos 7 sin repetir nada: la respuesta dice cuántos se agregaron (`added`) y cuántos ya estaban (`duplicates`), con los nuevos en `data`. Cada depósito (`btx_…`) abierto trae `suggested_match`: las facturas que parece pagar, cuánto toca a cada una, si son PUE o PPD y qué tan segura es (`confidence`: `high` o `medium`), con sus razones. `payer_tax_id` ayuda mucho: el RFC del ordenante casi nunca viene en el concepto. ## Conciliar | Si el depósito… | Haga | | --- | --- | | Pagó facturas PUE | `POST /v1/bank_transactions/{id}/match`, sin cuerpo para aceptar la sugerencia o con `invoices` (`inv_…` o UUID, de un mismo cliente). | | Pagó facturas PPD | [POST /v1/payments](https://fiscalisto.com/docs/api/pagos-y-notas-de-credito) con `"bank_transaction": "btx_…"`: timbra el complemento de pago, con la fecha del depósito, y lo concilia. Repetir la solicitud con la misma `Idempotency-Key` contesta el mismo complemento; otro con el mismo depósito se rechaza. | | No es de una factura | `POST /v1/bank_transactions/{id}/classify` con `classification`: `venta` (aún sin facturar), `prestamo`, `traspaso` o `devolucion`. | Un depósito sin explicar puede tomarse como ingreso en una revisión del SAT (artículo 59 fracción III del CFF): clasifique los que no vienen de una factura. `POST /v1/bank_transactions/{id}/reopen` deshace la conciliación o la clasificación, y sus facturas vuelven a deber. Si se pagó con un complemento vigente, cancele antes el complemento (`400 bank_transaction_invalid`). ## Consultar `GET /v1/bank_transactions` lista los depósitos, los más recientes primero; con `status=open` solo los que faltan. Cada uno conciliado dice qué facturas pagó (`invoices`) y, si se pagó con un complemento, cuál (`payment`). Una cuenta manda hasta 100,000 depósitos al día en producción y 5,000 en pruebas ([Límites](https://fiscalisto.com/docs/api/limites)). --- # Contabilidad > Las pólizas, la balanza de comprobación, los estados financieros, el catálogo de cuentas, la contabilidad electrónica para el SAT y la DIOT de cada emisor, armadas de sus CFDI. Con las facturas que el emisor timbró y las que recibió (de las [descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat)), Fiscalisto arma su contabilidad de cada mes: las pólizas de sus CFDI, sus pagos, su nómina y la depreciación de sus activos; la balanza; el estado de resultados y el balance general; el catálogo de cuentas con el código agrupador del SAT; los XML de la contabilidad electrónica y la DIOT. No incluye movimientos de banco sin CFDI ni provisiones. ## Pólizas ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/accounting/journal_entries?month=2026-10" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.accounting.journalEntries({ month: "2026-10" })) { console.log(item.id); } ``` Las pólizas de diario del mes (`D-202610-00001`…), en orden de fecha, con sus cargos y abonos en pesos, que siempre cuadran. `type` dice de qué es cada una: | `type` | De qué | Cargo | Abono | | --- | --- | --- | --- | | `invoice` | Factura emitida | Clientes (o Caja o Bancos si es PUE), retenciones que le hicieron | Ventas, IVA trasladado (cobrado si es PUE, no cobrado si es PPD) | | `invoice` | Factura recibida | El gasto de su categoría (o el activo fijo, usos I01 a I08), IVA acreditable | Proveedores (o Caja o Bancos si es PUE), retenciones por pagar | | `invoice` | Nota de crédito | Al revés que la factura | | | `payment_received` | Pago de un complemento que emitió | Bancos, pérdida cambiaria | Clientes, utilidad cambiaria; el IVA pasa de no cobrado a cobrado | | `payment_made` | Pago de un complemento que recibió | Proveedores | Bancos; el IVA pasa de pendiente a pagado | | `payroll` | Recibo de nómina | Sueldos y otros pagos | ISR retenido, otras retenciones, Bancos por el neto | | `depreciation` | La depreciación del mes | Depreciación de cada tipo de activo | Su depreciación acumulada | Un PUE se registra cobrado o pagado según su forma de pago: efectivo en Caja, transferencia, cheque o tarjeta en Bancos, y una recibida pagada con tarjeta de crédito en Tarjetas de crédito por pagar. La depreciación es en línea recta con las tasas del artículo 34 de la LISR, desde el mes siguiente al inicio de uso, sin INPC. Cada póliza dice de qué documento sale (`uuid`, e `invoice` si es un CFDI que timbró este emisor); la de depreciación no tiene `uuid` ni `tax_id`. Un CFDI en otra moneda se registra con su tipo de cambio; sin él, no se registra. Hasta 1,000 pólizas por página con `limit`, y la siguiente con `starting_after`. El número de una póliza es su lugar en el mes: un CFDI de ese mes que llega después (por ejemplo, en una descarga del SAT) recorre los que siguen, así que para relacionar una póliza con su documento use `uuid`, no `id`. ## Balanza y catálogo `GET /v1/accounting/trial_balance?month=2026-10` da el saldo inicial, los cargos, los abonos y el saldo final de cada cuenta. Los saldos iniciales se acumulan desde el primer CFDI, o desde los saldos iniciales que capture en **Estados financieros** de la app (las cuentas de resultados, desde enero), y los resultados de cada año anterior se cierran a capital, así que cargos y abonos siempre suman lo mismo. ## Estados financieros ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/accounting/financial_statements?month=2026-10" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const financialStatements = await fiscalisto.accounting.financialStatements({ month: "2026-10" }); ``` El estado de resultados del mes y del año hasta ese mes (`income_statement`), y el balance general al cierre del mes (`balance_sheet`), leídos de la misma balanza. Cada sección del estado de resultados (`ingresos`, `costos`, `gastos`, `financiamiento`, `otros`) suma en positivo lo que aumenta la utilidad y en negativo lo que la reduce, y se agrupa por el primer nivel del código agrupador con sus cuentas. En el balance, `difference` es activo menos pasivo y capital: 0 cuando los libros cuadran. `missing_invoices` cuenta los pagos de facturas que no están en Fiscalisto y no se registraron. ## Cruce con lo declarado ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/accounting/cross_checks?year=2026" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const crossChecks = await fiscalisto.accounting.crossChecks({ year: 2026 }); ``` Cada mes del año cuya declaración mensual ya venció (el día 17 del mes siguiente) compara lo que el emisor declaró con lo que dicen sus CFDI: `vat_charged` (IVA trasladado), `vat_creditable` (IVA acreditable), `vat_withheld` (IVA que le retuvieron, de sus facturas emitidas y de las constancias de retenciones que recibió) e `isr_income` (ingresos para ISR). En los meses en que el emisor retuvo, también sus retenciones: `isr_withheld_salaries` (ISR por salarios y asimilados contra sus recibos de nómina, por fecha de pago), `isr_withheld_suppliers` (ISR por honorarios y arrendamiento contra las facturas recibidas que pagó) y `vat_withheld_suppliers` (IVA retenido a proveedores). Si la declaración del mes no trae una retención que sus CFDI sí tienen, `declared` es 0 y el cruce `differs`. Lo declarado sale de sus [declaraciones del SAT](https://fiscalisto.com/docs/api/declaraciones-del-sat), así que pida antes `POST /v1/sat_declarations/refresh`; este cruce no consulta al SAT y no cuesta timbres. Cada cruce dice `matches` (hasta $2 de diferencia, el redondeo de las declaraciones en pesos enteros), `differs`, `not_filed` (no hay declaración de ese mes) o `unreadable` (la declaración no trae la cifra en un renglón reconocible: `declared` es `null`, nunca 0). Un mes con complementaria toma, por impuesto, la más reciente que lo trae. Cuando la declaración dice aparte lo que llegó sin CFDI (en `isr_income`, los ingresos nominales adicionales; en `vat_charged`, el IVA al 16% por encima del que el SAT precargó de sus facturas), eso viene en `declared_without_cfdi` y solo el resto se compara: `difference` es `declared` menos `declared_without_cfdi` menos `invoiced`. `vat_balance_in_favor` suma el IVA a favor del año y lo que se acreditó después, y `tax_regimes` dice con qué régimen facturó y si está en su constancia. ### Los CFDI de una cifra ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/accounting/cross_checks/cfdi?month=2026-09&key=vat_creditable" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const crossCheckCfdi = await fiscalisto.accounting.crossCheckCfdi({ month: "2026-09", key: "vat_creditable" }); ``` Para ver por qué una cifra `differs`, `GET /v1/accounting/cross_checks/cfdi` con el mes (`month`, cuya declaración ya venció) y la cifra (`key`: `vat_charged`, `vat_creditable`, `vat_withheld`, `isr_income`, `isr_withheld_salaries`, `isr_withheld_suppliers` o `vat_withheld_suppliers`) da los CFDI que forman lo facturado (`invoiced`), con los mismos cálculos de Impuestos y de mayor a menor. Cada uno trae su `uuid`, la contraparte (`tax_id`, `name`), la fecha y lo que aporta (`amount`). En `isr_withheld_salaries` son los recibos de nómina del mes (por fecha de pago) con el ISR retenido a cada empleado, y en las retenciones a proveedores las facturas recibidas que pagó en el mes. `type` es `invoice` (factura PUE, en su mes), `credit_note` (nota de crédito: resta), `payment` (lo que se pagó en el mes de una factura PPD; `uuid` es el de la factura) o `withholding_certificate` (en `vat_withheld`, una constancia de retenciones que recibió, como las de plataformas de hospedaje, en el mes en que se emitió; `uuid` es el de la constancia). La suma de `cfdi` más `adjustment` es `invoiced`. `adjustment` es lo que Impuestos agrega sobre los CFDI: en `vat_creditable`, la proporción de gastos comunes, el IVA retenido enterado del mes anterior y las importaciones. Trae también `status`, `declared` y `difference` de esa cifra, como el cruce del año. Tampoco consulta al SAT ni cuesta timbres. ## Catálogo `GET /v1/accounting/accounts` es el catálogo de cuentas, cada una con su código agrupador del Anexo 24. ## Contabilidad electrónica ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/accounting/electronic_accounting?month=2026-10" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" -o contabilidad.zip ``` ```ts tab="TypeScript" import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("contabilidad.zip", await fiscalisto.accounting.electronicAccounting({ month: "2026-10" })); ``` Un ZIP con el catálogo (`CT`) y la balanza (`BN`) en el formato 1.3 del Anexo 24, cada uno en su ZIP con el nombre que espera el Buzón Tributario (`RFC` + año + mes + tipo). - Una balanza que corrige otra ya enviada: `type=complementary` y el día en que se modificó en `modified_on` (sale `BC`). - Las pólizas (`PL`) se entregan cuando el SAT las pide: agregue `journal_request` con el motivo (`AF` acto de fiscalización o `FC` fiscalización compulsa, con `order_number`; `DE` devolución o `CO` compensación, con `procedure_number`). ## DIOT `GET /v1/accounting/diot?month=2026-09` dice lo efectivamente pagado a cada proveedor en el mes (facturas PUE y complementos de pago que recibió), con sus notas de crédito como devoluciones, una línea por proveedor y tipo de operación. `blacklisted` marca a los proveedores en la lista 69-B definitiva, y `warnings` lo que conviene revisar. `GET /v1/accounting/diot/file?month=2026-09` es el archivo TXT de carga masiva de la DIOT 2025, listo para la plataforma del SAT. Con `group_small_suppliers=true` junta en una línea global a los proveedores de hasta $50,000 en el mes, mientras no pasen del 10 % de lo pagado (regla 4.5.2 de la RMF). Un mes sin pagos a proveedores responde `diot_empty`: esa DIOT se presenta sin operaciones desde el portal del SAT. La DIOT sale de las facturas recibidas: pida antes las del mes con [POST /v1/sat_downloads](https://fiscalisto.com/docs/api/descargas-del-sat). Se arma desde enero de 2025, con el formato de ese año. --- # Cancelar y sustituir > Cómo se cancela un CFDI ante el SAT, cuándo debe aceptarlo el cliente y cómo corregir una factura con otra que la sustituye. Un CFDI timbrado no se edita: si tiene un error se cancela y, si hace falta, se emite otro que lo sustituye. La cancelación la pide Fiscalisto al SAT con el CSD del emisor. ## Cancelar ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices/inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg/cancel \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "motive": "02" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.cancel("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { motive: "02" }); ``` El SAT pide uno de cuatro motivos (`motive`): | Motivo | Cuándo | | --- | --- | | `01` | Tiene errores y ya emitió la que la sustituye: mande `substitution` con su id o UUID. Mejor use [sustituir](#sustituir). | | `02` | Tiene errores y no se va a sustituir. Es el que se usa si no manda `motive`. | | `03` | La operación no se llevó a cabo. | | `04` | Solo en una factura global: una venta incluida en ella se facturó después a nombre del cliente, así que se cancela la global (y se emite otra sin esa venta). En otra factura contesta `cancellation_failed`. | ## Cuándo debe aceptarlo el cliente Si la factura pasa de $1,000 y no es de las excepciones del SAT, el cliente tiene que aceptar la cancelación en su buzón tributario. Mientras tanto la factura queda `status: "pending"`, con `cancellation.status: "pending"`, y sigue vigente. Después le llega uno de estos eventos: - `invoice.canceled`: la aceptó, o no respondió en 3 días hábiles y el SAT la dio por aceptada. - `invoice.cancellation_rejected`: la rechazó, o la solicitud venció. Puede pedirla otra vez. Se cancelan en ese momento, sin aceptación, las facturas de hasta $1,000, las notas de crédito, los traslados, los recibos de nómina, las facturas a público en general o a un extranjero, y cualquier factura dentro de las 24 horas siguientes a su emisión. Las constancias de retenciones también se cancelan en ese momento. Si pide la cancelación otra vez mientras espera al cliente, la API contesta el estado actual sin mandar otra solicitud. Si consulta la factura con `?refresh=true`, Fiscalisto pregunta al SAT su estado en ese momento. ## Lo que cuesta Cancelar un comprobante no usa timbres, ni la primera solicitud ni las que repita después de un rechazo. El timbre que usó al timbrarlo no regresa. ## Sustituir Para corregir una factura (otro precio, otro régimen del cliente, otro concepto), mande la factura corregida completa, con el mismo cuerpo que [`POST /v1/invoices`](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice): ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices/inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg/substitute \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: pedido-A-1042-corregida" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "items": [{ "description": "Mensualidad", "product_key": "86121700", "quantity": 2, "unit_price": 2400 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.substitute("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "03", items: [ { description: "Mensualidad", product_key: "86121700", quantity: 2, unit_price: 2400, }, ], }, { idempotencyKey: "pedido-A-1042-corregida" }); ``` Fiscalisto timbra la nueva relacionada a la anterior (relación `04`) y cancela la anterior con el motivo `01`, en una sola llamada. La respuesta es la factura nueva; su `substitutes` dice cuál sustituyó y si ya quedó cancelada. Si no se pudo cancelar, `substitutes.cancel_error` dice por qué; corríjalo y cancélela con [`POST /v1/invoices/{id}/cancel`](https://fiscalisto.com/docs/api/referencia/facturas/cancel-invoice) con el motivo `01` y la nueva en `substitution`. Si la anterior necesita aceptación del cliente, la nueva ya está timbrada y la anterior queda `pending` hasta que responda. Los complementos de pago y las notas de crédito no se sustituyen: se cancelan y se emite otro. --- # Lotes de facturas > Timbre hasta 1,000 facturas de un emisor con una sola solicitud, revisadas todas antes de empezar. Un lote sirve para lo que se factura de golpe: las cuotas del mes de un condominio, las colegiaturas, las ventas del día de un marketplace. En lugar de mil solicitudes y sus límites, manda una. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoice_batches \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: cuotas-2026-10" \ -H "Content-Type: application/json" \ -d '{ "name": "Cuotas de octubre", "invoices": [ { "reference": "depto-101", "customer": "cus_OufpKqnrvx9p1", "payment_form": "03", "items": [{ "product": "prd_OufpKqnrvx9p1" }] }, { "reference": "depto-102", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "items": [{ "description": "Cuota de mantenimiento", "product_key": "80131500", "unit_price": 1500 }] } ] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoiceBatch = await fiscalisto.invoiceBatches.create({ name: "Cuotas de octubre", invoices: [ { reference: "depto-101", customer: "cus_OufpKqnrvx9p1", payment_form: "03", items: [{ product: "prd_OufpKqnrvx9p1" }], }, { reference: "depto-102", customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "03", items: [ { description: "Cuota de mantenimiento", product_key: "80131500", unit_price: 1500, }, ], }, ], }, { idempotencyKey: "cuotas-2026-10" }); ``` Cada factura del lote es el mismo cuerpo de [`POST /v1/invoices`](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice), sin `issuer` (el lote es de un emisor), sin `type` ni `global` (son facturas de ingreso) y con `reference` en lugar de `metadata`: su identificador de esa factura, que vuelve en el resultado. ## Cómo se timbra 1. **Se revisan todas antes de empezar.** Si una no pasa la revisión, la API responde `422` `batch_invalid` con `param` en la factura y el campo (`invoices.3.items.0.unit_price`) y no se timbra ninguna. Corríjala y mande el lote otra vez. 2. **Se responde `202` de inmediato**, con el lote en `status: "processing"`. 3. **Se timbran en segundo plano**, a la velocidad que permite el PAC. Cada factura timbrada manda su `invoice.stamped`, como cualquier otra. 4. **Al terminar** llega `invoice_batch.completed` y el lote queda `completed`, con cuántas se timbraron (`stamped`) y cuántas no (`failed`). ```json { "id": "bat_OufpKqnrvx9p4hMdcoapAo5aaGJEme7mXj", "object": "invoice_batch", "status": "completed", "total": 2, "stamped": 1, "failed": 1, "processing": 0, "invoices": [ { "index": 0, "reference": "depto-101", "status": "valid", "invoice": "inv_OufpKqnrvx9p0mn7VjQ6JlfipKAkHWstOK", "uuid": "DB494B07-D6DE-4F70-8ED0-C8B864FF8717", "total": 1740, "error": null }, { "index": 1, "reference": "depto-102", "status": "failed", "invoice": null, "uuid": null, "total": 1740, "error": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital." } ] } ``` Una factura puede fallar aunque pasó la revisión, si el SAT la rechaza (casi siempre por los datos del cliente). Esas no usan timbre: corríjalas y timbre cada una con `POST /v1/invoices`. ## Consultar el avance [`GET /v1/invoice_batches/{id}`](https://fiscalisto.com/docs/api/referencia/lotes/get-invoice-batch) dice cuántas van y el estado de cada una. Si no usa webhooks, consúltelo cada pocos segundos hasta que diga `completed`. ## Timbres Cada factura timbrada usa un timbre, al timbrarse. Antes de mandar un lote grande revise que le alcancen con [`GET /v1/balance`](https://fiscalisto.com/docs/api/referencia/cuenta/get-balance): si se acaban a la mitad, las que faltan quedan `failed` con el motivo en `error`. Después de comprar, mándelas en otro lote, con otra `Idempotency-Key`. ## Repetirlo Mandar el mismo lote con la misma `Idempotency-Key` contesta el lote que ya existe (`202`, como la primera vez, con `idempotent-replayed: true`), sin timbrar nada otra vez. --- # Complementos > Carta Porte, comercio exterior, impuestos locales, instituciones educativas y los demás complementos del CFDI, como un campo más de la factura. Un complemento agrega al CFDI la información que el SAT pide para ciertas operaciones. En la API cada uno es un campo de [`POST /v1/invoices`](https://fiscalisto.com/docs/api/referencia/facturas/create-invoice), de la factura o de cada concepto, y Fiscalisto lo revisa con las mismas reglas que la app antes de timbrar. | Complemento | Campo | Para qué | | --- | --- | --- | | Carta Porte 3.1 | `carta_porte` | Traslado de mercancías por carretera, mar, aire o tren: origen, destinos, mercancías, vehículo y operadores. Va en un traslado (`"type": "T"`) o en una factura de flete. | | Comercio exterior 2.0 | `comercio_exterior` | Exportación definitiva de mercancías, con `"export": "02"`. | | Impuestos locales | `local_taxes` | Impuestos estatales trasladados o retenidos, como el ISH de hospedaje. | | Instituciones educativas | `items[].iedu` | Colegiaturas: alumno, CURP, nivel y RVOE, para que los padres las deduzcan. | | A cuenta de terceros | `items[].third_party` | Un concepto que se cobra por cuenta de otro contribuyente. | | Vehículo usado | `items[].used_vehicle` | Venta de un auto usado por una persona física. | | Donatarias | `donation` | Recibos de donativos de una donataria autorizada. | | INE | `ine` | Operaciones con partidos políticos y candidatos. | | Leyendas fiscales | `leyendas` | Textos que una disposición fiscal pide incluir. | | Addenda | `addenda` (y `items[].addenda`) | Lo que piden algunos receptores, como Walmart o Soriana. | Las facturas también llevan sin complemento la cuenta predial (`items[].predial`) en un arrendamiento y los pedimentos (`items[].pedimentos`) de la mercancía importada. La referencia de `POST /v1/invoices` describe cada campo de cada complemento, con su nombre en el XML del SAT. ## Ejemplo: hospedaje con ISH ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: reserva-77812" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "04", "items": [{ "description": "Hospedaje, 2 noches", "product_key": "90111800", "unit_name": "Noche", "quantity": 2, "unit_price": 1500 }], "local_taxes": [{ "retention": false, "name": "ISH", "rate": 3, "amount": 90 }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "04", items: [ { description: "Hospedaje, 2 noches", product_key: "90111800", unit_name: "Noche", quantity: 2, unit_price: 1500, }, ], local_taxes: [ { retention: false, name: "ISH", rate: 3, amount: 90, }, ], }, { idempotencyKey: "reserva-77812" }); ``` `rate` va en por ciento y `amount` es el importe del impuesto local; Fiscalisto lo suma (o, si es una retención, lo resta) al total. Si el producto del catálogo ya lleva el impuesto (`local_taxes` en [`POST /v1/products`](https://fiscalisto.com/docs/api/referencia/productos/create-product)) y la factura no manda `local_taxes`, Fiscalisto lo agrega solo, calculado sobre los conceptos de ese producto: en una factura con hospedaje y cena, el ISH va sobre el hospedaje. Si la factura manda `local_taxes`, se usan los suyos tal cual. Al público en general solo se le trasladan. ## Ejemplo: a cuenta de terceros ```bash tab="cURL" curl https://api.fiscalisto.com/v1/invoices \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Idempotency-Key: comision-3391" \ -H "Content-Type: application/json" \ -d '{ "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "payment_form": "03", "items": [{ "description": "Consultoría", "product_key": "80101500", "unit_price": 1000, "third_party": { "tax_id": "CACX7605101P8", "name": "XOCHILT CASAS CHAVEZ", "tax_system": "612", "zip": "36257" } }] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", }, payment_form: "03", items: [ { description: "Consultoría", product_key: "80101500", unit_price: 1000, third_party: { tax_id: "CACX7605101P8", name: "XOCHILT CASAS CHAVEZ", tax_system: "612", zip: "36257", }, }, ], }, { idempotencyKey: "comision-3391" }); ``` --- # Listas negras del SAT > Revise si un RFC está en la lista 69-B (operaciones simuladas), 69-B Bis o en las del artículo 69, uno por uno o hasta 1,000 a la vez. El SAT publica quién simula operaciones (artículo 69-B del CFF), quién transmitió pérdidas fiscales indebidamente (69-B Bis) y quién no está localizado, tiene créditos firmes o exigibles, o tiene su sello cancelado (artículo 69). Fiscalisto descarga esas listas cada día y usted las consulta con su llave, de pruebas o de producción. En producción, cada 10 RFC revisados usan un timbre; en pruebas no cuesta. ## Un RFC ```bash tab="cURL" curl https://api.fiscalisto.com/v1/sat_lists/AAA080808HL8 \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satListCheck = await fiscalisto.satLists.get("AAA080808HL8"); ``` ```json { "object": "sat_list_check", "tax_id": "AAA080808HL8", "name": "EMPRESA SIMULADORA SA DE CV", "listed": true, "risk": "high", "lists": [ { "list": "69b", "source": "69b", "source_name": "69-B (operaciones simuladas)", "situation": "Definitivo", "published_on": null, "details": { "Publicación página SAT definitivos": "15/03/2024", "Número y fecha de oficio global de definitivos": "500-05-2024-1234 de fecha 01/03/2024" } } ], "lists_updated_at": "2026-10-24T00:12:40-06:00" } ``` `details` trae las columnas que el SAT publica para ese RFC, con los nombres del SAT. En la 69-B las fechas y oficios vienen ahí; en las del artículo 69, `published_on` es la fecha del archivo (primera publicación, cancelación o «sin efectos desde»). Si el RFC no aparece en ninguna, `listed` es `false`, `risk` es `none` y `lists` viene vacía. ## Varios a la vez Mande hasta 1,000 RFC en una solicitud; la respuesta trae uno por RFC, en el orden en que los mandó (los repetidos, una vez): ```bash tab="cURL" curl https://api.fiscalisto.com/v1/sat_lists/check \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tax_ids": ["AAA080808HL8", "ICV060329BY0"] }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.satLists.check({ tax_ids: ["AAA080808HL8", "ICV060329BY0"] }); ``` Úselo para revisar su catálogo de proveedores o clientes de una vez, por ejemplo cada mes. Un RFC mal escrito contesta `400` con `param` igual a `tax_ids.N`, su posición. ## Qué significa risk | `risk` | Cuándo | Qué hacer | | --- | --- | --- | | `high` | 69-B o 69-B Bis como **Presunto** o **Definitivo**. | Las facturas de un definitivo no se pueden deducir. No le compre; si ya le compró, revise con su contador. | | `medium` | Alguna lista del artículo 69: no localizado, créditos firmes o exigibles, cancelados, sentencias o CSD sin efectos. | Puede seguir facturándole, pero confirme sus datos y su situación. | | `none` | No aparece, o ya **desvirtuó** o tiene **sentencia favorable**. | Nada. | `listed` es `true` siempre que aparezca, aunque ya haya desvirtuado: así sabe que tuvo antecedentes. ## Al facturar Cuando timbra una factura a un cliente que aparece en una lista, la factura se timbra igual (facturarle no está prohibido) y su respuesta trae un aviso en `warnings`, como «EMPRESA SIMULADORA SA DE CV está en la lista 69-B del SAT como «Definitivo»…». Muéstrelo a quien factura o regístrelo, según su sistema. Las listas se actualizan cada día a la medianoche de la Ciudad de México; `lists_updated_at` dice cuándo fue la última vez. Las mismas listas se consultan en la app, en **Listas del SAT**. ## Cuánto cuesta En producción, cada 10 RFC revisados usan un timbre de su cuenta; el aviso al facturar no cuenta. En pruebas no cuesta. Vea [Límites](https://fiscalisto.com/docs/api/limites#lo-que-usa-timbres-sin-timbrar). --- # Descargas del SAT > Las facturas que un emisor recibió o emitió, directo del SAT con su XML: la descarga masiva con la e.firma del emisor, con un evento al terminar. Con la descarga masiva del SAT usted trae todas las facturas que un emisor **recibió** (sus gastos, de cualquier proveedor) o **emitió** (aunque no las haya hecho con Fiscalisto), con su XML. Sirve para la contabilidad, para conciliar pagos o para revisar a los proveedores contra las [listas del SAT](https://fiscalisto.com/docs/api/listas-del-sat). En producción usan timbres según cuántas facturas traen; en pruebas no cuestan. El SAT tarda de unos minutos a unas horas en preparar cada descarga. Usted la pide, sigue con lo suyo y le llega `sat_download.completed` cuando las facturas ya están. ## En pruebas y en producción - **En producción** se le pregunta al SAT con la e.firma del emisor, que usted guarda una vez (abajo). - **En pruebas** el SAT no tiene un ambiente de pruebas para esto, así que no se le pregunta: la descarga termina al instante con facturas de ejemplo entre el emisor y los RFC de prueba del SAT, con su XML y su evento. Pedir lo mismo otra vez trae las mismas facturas, sin duplicarlas. No necesita e.firma. ## Guardar la e.firma del emisor En producción, mande la e.firma (el `.cer` y el `.key` en base64 y su contraseña) con `keep_for_downloads: true`. Se revisa que sea una e.firma vigente de ese RFC y se guarda cifrada, como en la app. Si el emisor aún no firmó el contrato con el PAC y ya tiene su CSD, la misma llamada lo firma. ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/efirma \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"cer\": \"$(base64 < efirma.cer | tr -d '\n')\", \"key\": \"$(base64 < efirma.key | tr -d '\n')\", \"password\": \"$EFIRMA_PASSWORD\", \"keep_for_downloads\": true }" ``` ```ts tab="TypeScript" import { readFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.signEfirma("iss_OufpKqnrvx9p", { cer: (await readFile("efirma.cer")).toString("base64"), key: (await readFile("efirma.key")).toString("base64"), password: process.env.EFIRMA_PASSWORD!, keep_for_downloads: true, }); ``` El emisor trae `sat_downloads.available: true` y cuándo vence su e.firma en `sat_downloads.efirma_expires_at`. Para borrarla: `DELETE /v1/issuers/{id}/efirma`. El emisor sigue timbrando; solo deja de poder pedir descargas. > **Con permiso del dueño.** Pida la e.firma solo con el permiso del dueño del RFC y dígale para qué es. Con ella se consultan todas sus facturas en el SAT. ## Pedir una descarga ```bash tab="cURL" curl https://api.fiscalisto.com/v1/sat_downloads \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "direction": "received", "from": "2026-09-01", "to": "2026-09-30" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDownload = await fiscalisto.satDownloads.create({ direction: "received", from: "2026-09-01", to: "2026-09-30", }); ``` Contesta `202` con la descarga (`sdl_…`) en `status: "processing"` (en pruebas ya viene `completed`): ```json { "id": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "object": "sat_download", "livemode": true, "issuer": "iss_OufpKqnrvx9p", "direction": "received", "from": "2026-09-01", "to": "2026-09-30", "type": null, "counterparty_tax_id": null, "uuid": null, "status": "processing", "new_invoices": null, "error": null, "created_at": "2026-10-24T10:20:00-06:00", "completed_at": null } ``` | Campo | Qué pide | | --- | --- | | `direction` | `received` (por omisión) o `issued`. | | `from`, `to` | Los días, incluidos. Hasta un año por descarga, desde el 1 de enero de hace 5 años (lo que da el SAT) y hasta hoy. | | `type` | Solo un tipo de comprobante: `I`, `E`, `P`, `T` o `N`. | | `counterparty_tax_id` | Solo las de un proveedor (recibidas) o un cliente (emitidas). | | `uuid` | Una sola factura, por su folio fiscal. | Las recibidas llegan **vigentes**: el SAT solo entrega el XML de una factura recibida mientras no esté cancelada. ## Cuando termina Llega el evento `sat_download.completed` con la descarga y `new_invoices`, cuántas facturas nuevas trajo (las que ya tenía no se cuentan dos veces). Si el SAT no pudo, llega `sat_download.failed` con el motivo en `error`, por ejemplo una e.firma vencida. También puede consultarla con `GET /v1/sat_downloads/{id}` o listarlas con `GET /v1/sat_downloads`. El SAT limita cuántas veces se puede pedir exactamente lo mismo (el mismo periodo con los mismos filtros). Si se agotan, la respuesta es `sat_request_rejected`: pida un periodo un poco distinto. ## Las facturas ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/sat_invoices?direction=received&date_gte=2026-09-01&limit=20" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satInvoices.list({ direction: "received", date_gte: "2026-09-01", limit: 20, })) { console.log(item.id); } ``` Cada factura (`sat_…`) trae su folio fiscal, quién la emitió (`issued_by`) y a quién (`issued_to`), los importes, los impuestos trasladados y retenidos, y su estado en el SAT (`sat_status`). Se filtran por `direction`, `type`, `counterparty_tax_id`, `date_gte` y `date_lte`, de la más reciente a la más antigua, con la [paginación](https://fiscalisto.com/docs/api/paginacion) de siempre. El XML, tal como lo entregó el SAT, está en `GET /v1/sat_invoices/{id}/xml`. ## Límites Hasta 300 descargas al día por cuenta en producción y 50 en pruebas. En producción, pedir una usa un timbre, que alcanza para los primeros 100 CFDI (si el SAT la rechaza, el timbre regresa). Cuando el SAT dice cuántos tiene, cada 100 más, o los que sobren, usan otro timbre antes de descargarlos: 1,250 CFDI son 13 timbres. Si a su cuenta no le alcanzan, la descarga termina en `failed` (evento `sat_download.failed`), sin descargar nada, y el timbre de la solicitud no regresa. En pruebas no cuestan. Vea [Límites](https://fiscalisto.com/docs/api/limites#lo-que-usa-timbres-sin-timbrar). Pedir otra vez una descarga igual (mismo emisor, fechas y filtros) mientras el SAT todavía trabaja en ella contesta esa misma descarga, sin otro timbre: así un reintento después de un timeout no gasta las pocas solicitudes idénticas que acepta el SAT. Cuando ya terminó, una igual es una descarga nueva. --- # Declaraciones del SAT > Las declaraciones que un emisor presentó ante el SAT, con su PDF, su acuse y sus cifras: provisionales, la anual de persona moral y las del servicio anterior Declaraciones y pagos. Con la e.firma del emisor usted trae del SAT todas las declaraciones que presentó: las **provisionales o definitivas** de cada mes (desde 2022), la **anual** de persona moral (desde 2019) y las del servicio anterior **Declaraciones y pagos** (2021 y antes). Cada una llega con su PDF, su acuse, el número de operación, la línea de captura y sus cifras por impuesto, incluido el **coeficiente de utilidad** que aplicó. Sirve para revisar lo que ya se pagó, armar la contabilidad o calcular el siguiente pago provisional con el coeficiente del SAT. Solo se consulta: nunca se presenta, firma ni cambia nada en el SAT. ## Lo que cuesta | Qué | En producción | En pruebas | | --- | --- | --- | | Consultar al SAT (`POST /v1/sat_declarations/refresh`) | 1 timbre por consulta | Gratis | | Listar, ver y descargar lo que ya trajo | Gratis | Gratis | Si el SAT no contesta o no deja entrar con la e.firma, el timbre regresa. Cada emisor se puede consultar una vez cada 10 minutos (cada consulta es una entrada al SAT con su e.firma); antes, la API contesta `429` `sat_refresh_too_soon` con `Retry-After`. Fiscalisto no consulta solo: las declaraciones se actualizan cuando usted lo pide. ## En pruebas y en producción - **En producción** se entra al SAT con la e.firma que el emisor guardó con `keep_for_downloads: true`, la misma de las [descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat#guardar-la-efirma-del-emisor). - **En pruebas** el SAT no tiene un ambiente de pruebas, así que no se le pregunta: la consulta trae al instante las declaraciones de ejemplo de este año (hasta el mes pasado) y la anual del año anterior, con su PDF y su acuse. No necesita e.firma. ## Consultar al SAT ```bash tab="cURL" curl https://api.fiscalisto.com/v1/sat_declarations/refresh \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "issuer": "iss_OufpKqnrvx9p" }' ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDeclarationsRefresh = await fiscalisto.satDeclarations.refresh({ issuer: "iss_OufpKqnrvx9p" }); ``` La primera consulta trae todos los años que el SAT conserva; las siguientes, este año y el anterior, y vuelven a pedir los archivos que el SAT no pudo dar. La respuesta dice cuántas encontró (`found`) y cuántos archivos bajó (`downloaded`): ```json { "object": "sat_declarations_refresh", "livemode": true, "issuer": "iss_OufpKqnrvx9p", "status": "completed", "found": 58, "downloaded": 112, "pending": 0, "warning": null, "refreshed_at": "2026-10-26T09:12:00-06:00", "next_refresh_at": "2026-10-26T09:22:00-06:00" } ``` La consulta contesta en menos de un minuto. Un RFC con muchos años puede no alcanzar a bajar todo: entonces llega `status: "processing"` con lo que falta en `pending`, y lo demás se baja solo en la siguiente hora, sin otro timbre. Si una parte del SAT falló (por ejemplo, la anual) y lo demás sí llegó, `warning` trae lo que dijo el SAT. ## Listar las declaraciones ```bash tab="cURL" curl -G https://api.fiscalisto.com/v1/sat_declarations \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -d issuer=iss_OufpKqnrvx9p \ -d year=2026 ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satDeclarations.list({ issuer: "iss_OufpKqnrvx9p", year: 2026 })) { console.log(item.id); } ``` Salen del ejercicio y mes más recientes a los más antiguos, la anual antes de los meses de su año. Filtre por `year`, `month` (1 a 12), `type` (`normal` o `complementary`) y `source`: `monthly`, `annual` o `dyp`. Cada declaración trae: - `period`, `type` (`normal` o `complementary`), `operation_number`, `filed_on` y `payment_line`. - `isr_due`, `vat_due` y `total_due`, del acuse, y `profit_coefficient` en el ISR de personas morales. - `sections`: cada impuesto con sus renglones como los imprime el SAT (`label` y `value`). En las de Declaraciones y pagos va vacío: su PDF trae dos columnas que no se leen con certeza, así que use el PDF. - `summary`: las mismas cifras con nombres fijos, por obligación (ver abajo). - `receipt`: los conceptos del acuse con lo que quedó a cargo y a pagar. - `files`: qué archivos ya tiene, y en `files_error` por qué falta alguno, como lo dijo el SAT. ## Las cifras con nombres fijos El SAT cambia el formato y los nombres de sus renglones de un año a otro, y repite algunos («CANTIDAD A CARGO» sale varias veces). `summary` los lee por usted: cada obligación de la declaración con sus cifras bajo nombres que no cambian, para comparar un mes con otro o con sus facturas sin conocer el formato del SAT. ```json { "obligations": [ { "obligation": "vat", "name": "Impuesto al Valor Agregado. Personas morales", "read": true, "amount_due": 186287, "amount_to_pay": 186287, "figures": { "taxable_16": 13622057, "charged": 2179529, "creditable": 1993242, "withheld": 0, "balance_applied": 0, "due": 186287, "in_favor": null, "to_pay": 186287 } } ], "total_to_pay": 186287 } ``` | `obligation` | Qué es | Cifras además de las de todas | | --- | --- | --- | | `isr` | ISR propio (pago provisional) | `income`, `invoiced_income`, `income_ytd`, `deductions`, `profit_coefficient`, `taxable_profit`, `losses_applied`, `tax_base`, `tax_caused`, `prior_payments`, `withheld` | | `vat` | IVA | `taxable_16`, `taxable_0`, `exempt`, `charged`, `paid_16`, `creditable`, `withheld`, `balance_applied` | | `isr_salaries` | ISR retenido por salarios | `workers`, `payments`, `exempt`, `withheld` | | `isr_assimilated_salaries` | ISR retenido por asimilados a salarios | `payments`, `withheld` | | `isr_professional_services`, `isr_rent` | ISR retenido por servicios profesionales o arrendamiento | `withheld` | | `vat_withholdings` | IVA retenido a terceros | `withheld` | | `ieps`, `other` | IEPS y lo demás | `charged` en IEPS | Todas traen `surcharges` (recargos), `inflation` (actualización), `paid_before` (lo pagado antes, en una complementaria), `due`, `in_favor` y `to_pay`. Una cifra que la declaración no trae o que no se pudo leer es `null`, nunca `0`. Si una obligación solo viene en el acuse (`read: false`), trae sus montos `amount_due` y `amount_to_pay` y sus cifras en `null`; así llegan siempre las de Declaraciones y pagos. ## El año mes por mes `GET /v1/sat_declarations/calendar?year=2026` arma el ejercicio con lo que trajeron sus consultas: cada mes con sus obligaciones y si están presentadas (`filed`, con sus declaraciones), si faltan (`missing`: el plazo venció y no la encontramos) o si aún no vencen (`due`), con el día en que vencen en `due_on`. En persona moral trae también la anual (`month: null`). No usa timbres. ```bash tab="cURL" curl -G https://api.fiscalisto.com/v1/sat_declarations/calendar \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -d issuer=iss_OufpKqnrvx9p \ -d year=2026 ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDeclarationCalendar = await fiscalisto.satDeclarations.calendar({ issuer: "iss_OufpKqnrvx9p", year: 2026 }); ``` ```json { "object": "sat_declaration_calendar", "livemode": true, "issuer": "iss_OufpKqnrvx9p", "year": 2026, "missing": 1, "months": [ { "month": 8, "due_on": "2026-09-18", "obligations": [ { "obligation": "isr", "status": "filed", "declarations": ["dcl_OufpKqnrvx9p1E"] }, { "obligation": "vat", "status": "filed", "declarations": ["dcl_OufpKqnrvx9p1E"] }, { "obligation": "isr_salaries", "status": "missing", "declarations": [] } ] } ] } ``` El SAT no dice qué obligaciones tiene un RFC, así que se deducen de lo que presenta: ISR e IVA cada mes desde el primero que presentó, y una retención si la presentó en 2 de los 3 meses anteriores, o antes y después del mes. Una retención que el emisor dejó de presentar (ya no tiene empleados) deja de pedirse a los dos meses. La opinión de cumplimiento del SAT es la que manda; use `missing` para avisarle a su cliente, no como dictamen. ## Webhooks Cada consulta avisa a sus [webhooks](https://fiscalisto.com/docs/api/webhooks): - `sat_declaration.created` cuando encuentra una declaración nueva. Sus archivos y cifras pueden llegar un momento después. - `sat_declaration.updated` cuando llega su PDF, su acuse, los estados financieros o sus cifras. En ambos, `data.object` es la declaración como queda. ## Descargar el PDF y el acuse ```bash tab="cURL" curl https://api.fiscalisto.com/v1/sat_declarations/dcl_OufpKqnrvx9p1F/pdf \ -H "Authorization: Bearer $FISCALISTO_API_KEY" \ -o declaracion.pdf ``` ```ts tab="TypeScript" import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.satDeclarations.pdf("dcl_OufpKqnrvx9p1F")); ``` El acuse está en `GET /v1/sat_declarations/{id}/receipt` y, en la anual de persona moral, el Excel de estados financieros en `GET /v1/sat_declarations/{id}/financial_statements`. Un archivo que el SAT aún no da contesta `404` con su motivo; se vuelve a pedir en la siguiente consulta. > **Con permiso del dueño.** Pida la e.firma solo con el permiso del dueño del RFC y dígale para qué es. Con ella se consultan sus declaraciones y sus facturas en el SAT. --- # Cumplimiento SAT > La opinión de cumplimiento (32-D), la constancia de situación fiscal y el buzón tributario de un emisor, traídos del SAT con su e.firma, y los hallazgos que Fiscalisto encontró en sus CFDI. Con la e.firma del emisor usted trae del SAT tres papeles que sus clientes, bancos y proveedores le piden seguido: - La **opinión de cumplimiento** (artículo 32-D del CFF): positiva, negativa (con lo que el SAT dice que falta), sin obligaciones o en suspensión, con su folio y su PDF. - La **constancia de situación fiscal**: el PDF del SAT de hoy, con su idCIF, sus regímenes, su código postal y su estatus en el padrón ya leídos. - Los avisos del **buzón tributario**: cada uno con su asunto, si no se ha leído y qué tan urgente es (un requerimiento o una multa piden atención; un boletín no). Y sin ir al SAT, los **hallazgos** que Fiscalisto encontró en las facturas del emisor (las que timbró aquí y las que trajo con las [descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat)): proveedores en la lista 69-B, pagos en efectivo de más de $2,000, PPD sin complemento de pago y los demás. Solo se consulta: nunca se presenta, firma ni abre nada en el SAT. Un aviso del buzón se lee en Fiscalisto sin abrirlo en el SAT, así que no firma el acuse ni empieza los plazos. ## Lo que cuesta | Qué | En producción | En pruebas | | --- | --- | --- | | Pedir al SAT la opinión, la constancia o el buzón (`…/refresh`) | 1 timbre por consulta | Gratis | | Ver lo que ya trajo, bajar sus PDF y leer los hallazgos | Gratis | Gratis | Si el SAT no contesta o no deja entrar con la e.firma, contesta `502` `sat_unavailable` y el timbre regresa. Cada papel de cada emisor se puede pedir una vez cada 10 minutos; antes, la API contesta `429` `sat_refresh_too_soon` con `Retry-After`. Dos solicitudes a la vez entran al SAT una sola vez: la otra recibe el `429`. Fiscalisto no consulta solo: estos papeles se actualizan cuando usted lo pide. ## En pruebas y en producción - **En producción** se entra al SAT con la e.firma que el emisor guardó con `keep_for_downloads: true`, la misma de las [descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat#guardar-la-efirma-del-emisor). Sin ella, o vencida, contesta `400` `efirma_required`. - **En pruebas** el SAT no tiene un ambiente de pruebas, así que no se le pregunta: la opinión sale positiva, la constancia con los datos del emisor y el buzón con dos avisos de ejemplo (un requerimiento sin leer y un boletín). Cada uno con su PDF de ejemplo. No necesita e.firma. ## La opinión de cumplimiento Pídala al SAT; la solicitud no lleva cuerpo. ```bash tab="cURL" curl -X POST https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion/refresh \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satRefresh = await fiscalisto.issuers.refreshComplianceOpinion("iss_OufpKqnrvx9p"); ``` La respuesta dice si terminó (`status: completed`) o si el SAT sigue trabajando (`processing`, cuando tarda más de 50 segundos): en ese caso el resultado llega solo a `GET /v1/issuers/{id}/compliance_opinion` en uno o dos minutos. Ahí está la última que trajo: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const complianceOpinion = await fiscalisto.issuers.complianceOpinion("iss_OufpKqnrvx9p"); ``` ```json { "object": "compliance_opinion", "livemode": true, "issuer": "iss_OufpKqnrvx9p", "result": "negative", "date": "2026-10-27", "folio": "26NA1234567", "reasons": ["No se encuentra presentada la declaración anual de 2025."], "pdf": true, "checked_at": "2026-10-27T09:12:00-06:00", "last_error": null } ``` `result` es `positive`, `negative`, `no_obligations` o `suspended` (`null` hasta la primera consulta), y `reasons` lo que el SAT lista como pendiente en una negativa. El PDF, como lo da el SAT: ```bash tab="cURL" curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion/pdf \ -H "Authorization: Bearer $FISCALISTO_API_KEY" -o opinion.pdf ``` ```ts tab="TypeScript" import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.issuers.complianceOpinionPdf("iss_OufpKqnrvx9p")); ``` ## La constancia de situación fiscal ```bash tab="cURL" curl -X POST https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/tax_status_certificate/refresh \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satRefresh = await fiscalisto.issuers.refreshTaxStatusCertificate("iss_OufpKqnrvx9p"); ``` `GET /v1/issuers/{id}/tax_status_certificate` trae lo que dice la última: `issued_on`, `id_cif`, `legal_name`, `tax_systems` (cada régimen con su clave y su nombre), `zip` y `status` (`ACTIVO` en el padrón). Su PDF en `GET /v1/issuers/{id}/tax_status_certificate/pdf`. Si el emisor cambió de régimen o de domicilio fiscal, pídala otra vez: la factura sale con los datos del emisor en Fiscalisto, no con los de la constancia. ## El buzón tributario ```bash tab="cURL" curl -X POST https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/sat_mailbox/refresh \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satRefresh = await fiscalisto.issuers.refreshSatMailbox("iss_OufpKqnrvx9p"); ``` Los avisos quedan en `GET /v1/sat_mailbox_messages`, del más reciente al más antiguo y [paginados](https://fiscalisto.com/docs/api/paginacion); `unread=true` deja solo los que no se han leído en el SAT: ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/sat_mailbox_messages?issuer=iss_OufpKqnrvx9p&unread=true" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satMailboxMessages.list({ issuer: "iss_OufpKqnrvx9p", unread: true })) { console.log(item.id); } ``` Cada aviso trae `urgency` (`urgent`, `attention` o `informational`), `kind` (qué parece: requerimiento, multa, invitación…) y `action_required`, para que su sistema avise a quien deba contestarlo. Para leerlo completo y contestar, el emisor entra a su buzón en el SAT. ## Los hallazgos ```bash tab="cURL" curl "https://api.fiscalisto.com/v1/sat_findings?issuer=iss_OufpKqnrvx9p&direction=received" \ -H "Authorization: Bearer $FISCALISTO_API_KEY" ``` ```ts tab="TypeScript" import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.satFindings.list({ issuer: "iss_OufpKqnrvx9p", direction: "received", }); ``` Uno por tipo, de lo más grave a lo menos: `code` (estable, como `efos_definitivo` o `efectivo_2000`), `severity` (`critical`, `warning` o `info`), `title`, `explanation`, cuántas facturas lo tienen (`invoices`) y la suma de sus totales (`amount`). Filtra por `direction` (`issued` o `received`) y por fecha de la factura con `date_gte` y `date_lte` (días de la Ciudad de México). Los hallazgos que alguien marcó como revisados en Fiscalisto no aparecen. No usa timbres. --- # Ver la cuenta `GET https://api.fiscalisto.com/v1/account` La cuenta API y la llave con que se hizo la solicitud. Útil para probar la llave. ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const account = await fiscalisto.account.get(); ``` ## Respuesta 200 Correcto. Un objeto `Account`. Sus campos están en «Objetos» al final de este archivo (Account). ```json { "id": "org_39c5a6b2-02b3-8453-9c35-aba6b43c876a", "object": "account", "livemode": false, "name": "Mi plataforma", "api_version": "2026-10-24", "latest_api_version": "2026-10-24", "key": { "id": "key_vFQbeTxgHVoTlowHaTLR", "name": "Pruebas", "issuers": null, "read_only": false, "expires_at": null, "sandbox": null } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cambiar la versión de la API `POST https://api.fiscalisto.com/v1/account` Mueve la versión de la API de la cuenta: la que reciben sus solicitudes sin el encabezado Fiscalisto-Version y sus webhooks sin api_version propio. Antes, pruebe la nueva mandando Fiscalisto-Version en sus solicitudes. Con una llave de la cuenta con todos los permisos (no de un entorno de prueba). Queda en Desarrolladores › Actividad. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `api_version` | string | sí | La versión de la API de la cuenta: la que reciben sus solicitudes sin Fiscalisto-Version y sus webhooks sin api_version propio. Pruebe antes la nueva con el encabezado. | Ejemplo: ```json { "api_version": "2026-10-24" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const account = await fiscalisto.account.update({ api_version: "2026-10-24" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Account`. Sus campos están en «Objetos» al final de este archivo (Account). ```json { "id": "org_39c5a6b2-02b3-8453-9c35-aba6b43c876a", "object": "account", "livemode": false, "name": "Mi plataforma", "api_version": "2026-10-24", "latest_api_version": "2026-10-24", "key": { "id": "key_vFQbeTxgHVoTlowHaTLR", "name": "Pruebas", "issuers": null, "read_only": false, "expires_at": null, "sandbox": null } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Reiniciar los datos de prueba `POST https://api.fiscalisto.com/v1/test_data/reset` Deja el modo de pruebas como nuevo: cada emisor de pruebas conserva su RFC y su CSD de pruebas pero pierde sus facturas, clientes, productos, lotes, ligas y descargas, y se borran los eventos y el registro de solicitudes de pruebas. Las llaves y los webhooks se quedan. Con seed (por omisión) deja 3 clientes y 3 productos de ejemplo. Solo con una llave fk_test_; producción nunca se toca. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `seed` | boolean | | true (por omisión) para dejarle a cada emisor de pruebas 3 clientes y 3 productos de ejemplo. false para dejarlo vacío. (por omisión true) | Ejemplo: ```json { "seed": true } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testDataReset = await fiscalisto.testData.reset({ seed: true }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `TestDataReset`. Sus campos están en «Objetos» al final de este archivo (TestDataReset). ```json { "object": "test_data_reset", "livemode": false, "issuers": 1, "deleted_rows": 214, "deleted_events": 37, "customers_created": 3, "products_created": 3 } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un entorno de prueba `POST https://api.fiscalisto.com/v1/sandboxes` Un modo de pruebas propio para una persona o un proyecto: nace con el emisor de pruebas del SAT y una llave fk_test_ (key.secret, una sola vez) que solo ve los emisores, webhooks y eventos del entorno, y que reinicia solo los suyos. Se crea con una llave de pruebas de la cuenta (no de otro entorno ni limitada a emisores). Hasta 25 por cuenta. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | string | sí | Para quién o para qué es, por ejemplo el nombre de quien lo usa. (hasta 60 caracteres) | Ejemplo: ```json { "name": "Ana" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const sandbox = await fiscalisto.sandboxes.create({ name: "Ana" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Sandbox`. Sus campos están en «Objetos» al final de este archivo (Sandbox). ```json { "id": "sbx_4kQm8ZtR2wLp9XbN", "object": "sandbox", "livemode": false, "name": "Ana", "issuers": 1, "created_at": "2026-10-25T09:40:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar entornos de prueba `GET https://api.fiscalisto.com/v1/sandboxes` Los entornos de prueba de la cuenta, cada uno con su emisor de prueba y su llave fk_test_. Hasta 25 por cuenta. ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.sandboxes.list(); ``` ## Respuesta 200 Correcto. Un objeto `SandboxList`. Sus campos están en «Objetos» al final de este archivo (SandboxList). ```json { "object": "list", "data": [ { "id": "sbx_4kQm8ZtR2wLp9XbN", "object": "sandbox", "livemode": false, "name": "Ana", "issuers": 1, "created_at": "2026-10-25T09:40:00-06:00" } ], "has_more": false, "url": "/v1/sandboxes" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un entorno de prueba `GET https://api.fiscalisto.com/v1/sandboxes/{id}` Un entorno de prueba: su nombre, su emisor y lo que lleva usado hoy de sus reinicios. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const sandbox = await fiscalisto.sandboxes.get("sbx_4kQm8ZtR2wLp9XbN"); ``` ## Respuesta 200 Correcto. Un objeto `Sandbox`. Sus campos están en «Objetos» al final de este archivo (Sandbox). ```json { "id": "sbx_4kQm8ZtR2wLp9XbN", "object": "sandbox", "livemode": false, "name": "Ana", "issuers": 1, "created_at": "2026-10-25T09:40:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar un entorno de prueba `DELETE https://api.fiscalisto.com/v1/sandboxes/{id}` Sus llaves dejan de funcionar al momento, sus emisores se archivan y sus webhooks se borran. No toca nada de la cuenta ni de otros entornos. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.sandboxes.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un reloj de prueba `POST https://api.fiscalisto.com/v1/issuers/{id}/test_clock` Le da a un emisor de pruebas su propia hora, congelada en frozen_time (ahora, si no la manda): sus facturas recurrentes, cotizaciones, cuentas por cobrar y tickets cuentan los días con ella, y nada de eso corre solo hasta que usted lo adelanta. Para ver en segundos lo que pasa en meses. Solo emisores de prueba; uno por emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `frozen_time` | string | | Dónde empieza el reloj; si no la manda, ahora. | Ejemplo: ```json { "frozen_time": "2026-10-25T09:00:00-06:00" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testClock = await fiscalisto.testClocks.create("clk_0q1Vmmw52sbD", { frozen_time: "2026-10-25T09:00:00-06:00" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `TestClock`. Sus campos están en «Objetos» al final de este archivo (TestClock). ```json { "id": "clk_0q1Vmmw52sbD", "object": "test_clock", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "frozen_time": "2026-12-01T09:00:00-06:00", "status": "ready", "created_at": "2026-10-25T09:40:00-06:00", "advanced": { "recurring_invoices": 2, "quotes_expired": 1, "global_invoices": 1 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver el reloj de prueba `GET https://api.fiscalisto.com/v1/issuers/{id}/test_clock` La hora que es «hoy» para un emisor de prueba y hasta dónde se ha adelantado. Solo emisores de prueba. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testClock = await fiscalisto.testClocks.get("clk_0q1Vmmw52sbD"); ``` ## Respuesta 200 Correcto. Un objeto `TestClock`. Sus campos están en «Objetos» al final de este archivo (TestClock). ```json { "id": "clk_0q1Vmmw52sbD", "object": "test_clock", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "frozen_time": "2026-12-01T09:00:00-06:00", "status": "ready", "created_at": "2026-10-25T09:40:00-06:00", "advanced": { "recurring_invoices": 2, "quotes_expired": 1, "global_invoices": 1 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar el reloj de prueba `DELETE https://api.fiscalisto.com/v1/issuers/{id}/test_clock` El emisor vuelve a la hora real; lo que el reloj dejó pendiente lo hacen los procesos diarios. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testClock = await fiscalisto.testClocks.delete("clk_0q1Vmmw52sbD"); ``` ## Respuesta 200 Correcto. Un objeto `TestClock`. Sus campos están en «Objetos» al final de este archivo (TestClock). ```json { "id": "clk_0q1Vmmw52sbD", "object": "test_clock", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "frozen_time": "2026-12-01T09:00:00-06:00", "status": "ready", "created_at": "2026-10-25T09:40:00-06:00", "advanced": { "recurring_invoices": 2, "quotes_expired": 1, "global_invoices": 1 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Adelantar el reloj de prueba `POST https://api.fiscalisto.com/v1/issuers/{id}/test_clock/advance` Mueve el reloj hasta frozen_time haciendo, día por día, lo que habría pasado: timbra las facturas recurrentes de cada periodo, vence las cotizaciones y hace las facturas globales de tickets, con sus eventos. Contesta cuando terminó, con lo que hizo en advanced, y llega test_clock.ready. Hasta 25 comprobantes por solicitud: si se detiene antes, stopped_early y otra llamada sigue. Los CFDI se timbran con la fecha real (el SAT solo acepta la de hoy); el periodo, los vencimientos y los eventos siguen al reloj. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `frozen_time` | string | sí | A dónde adelantarlo: después de la hora actual del reloj. Lo que toca en el camino se hace día por día. | Ejemplo: ```json { "frozen_time": "2026-12-01T09:00:00-06:00" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const testClock = await fiscalisto.testClocks.advance("clk_0q1Vmmw52sbD", { frozen_time: "2026-12-01T09:00:00-06:00" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `TestClock`. Sus campos están en «Objetos» al final de este archivo (TestClock). ```json { "id": "clk_0q1Vmmw52sbD", "object": "test_clock", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "frozen_time": "2026-12-01T09:00:00-06:00", "status": "ready", "created_at": "2026-10-25T09:40:00-06:00", "advanced": { "recurring_invoices": 2, "quotes_expired": 1, "global_invoices": 1 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver los timbres `GET https://api.fiscalisto.com/v1/balance` Los timbres de producción que le quedan, por paquete. En pruebas se timbra gratis. ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const balance = await fiscalisto.balance.get(); ``` ## Respuesta 200 Correcto. Un objeto `Balance`. Sus campos están en «Objetos» al final de este archivo (Balance). ```json { "object": "balance", "livemode": true, "available": 20, "unlimited": false, "low_balance_at": 100, "auto_recharge": null, "packs": [ { "timbres": 20, "remaining": 20, "purchased_at": "2026-10-24T10:15:42-06:00", "expires_at": null } ] } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un emisor `POST https://api.fiscalisto.com/v1/issuers` Agrega un RFC que va a timbrar. Después suba su CSD (POST /v1/issuers/{id}/certificates) y, solo en producción, firme una vez el contrato con el PAC con su e.firma (POST /v1/issuers/{id}/efirma). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `employer_registration` | string o null | | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `curp` | string o null | | CURP, 18 caracteres. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "metadata": { "cliente": "42" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.create({ tax_id: "TOL190312KT4", legal_name: "TOLPI", tax_system: "601", zip: "64000", metadata: { cliente: "42" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar emisores `GET https://api.fiscalisto.com/v1/issuers` Los emisores de la cuenta en el modo de la llave, los más recientes primero. Una llave limitada a algunos emisores solo ve los suyos. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `tax_id` | consulta | | Solo el de este RFC. | | `include_archived` | consulta | | true para incluir los archivados. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.issuers.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `IssuerList`. Sus campos están en «Objetos» al final de este archivo (IssuerList). ```json { "object": "list", "data": [ { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ], "has_more": false, "url": "/v1/issuers" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un emisor `GET https://api.fiscalisto.com/v1/issuers/{id}` Un emisor: sus datos fiscales, si ya puede timbrar (status, CSD y contrato con el PAC) y su registro patronal. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.get("iss_OufpKqnrvx9p"); ``` ## Respuesta 200 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Actualizar un emisor `POST https://api.fiscalisto.com/v1/issuers/{id}` Solo cambia lo que mande. El RFC no cambia: cree otro emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `legal_name` | string | | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `tax_system` | string | | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `zip` | string | | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `employer_registration` | string o null | | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `curp` | string o null | | CURP, 18 caracteres. | | `metadata` | objeto o null | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "zip": "64010", "employer_registration": "Y5412345109" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.update("iss_OufpKqnrvx9p", { zip: "64010", employer_registration: "Y5412345109", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Archivar un emisor `DELETE https://api.fiscalisto.com/v1/issuers/{id}` Deja de timbrar; sus comprobantes se quedan. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.archive("iss_OufpKqnrvx9p"); ``` ## Respuesta 200 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Subir el CSD `POST https://api.fiscalisto.com/v1/issuers/{id}/certificates` El certificado de sello digital (.cer y .key en base64) y la contraseña de la llave. Se guarda cifrado y solo se usa para sellar. En pruebas se revisa pero se timbra con el CSD de pruebas del SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `cer` | string | sí | El archivo .cer en base64 (puede traer el prefijo data:). (hasta 87386 caracteres) | | `key` | string | sí | El archivo .key en base64 (puede traer el prefijo data:). (hasta 87386 caracteres) | | `password` | string | sí | La contraseña de la llave privada (.key). Se usa para abrirla y no se guarda en texto. (hasta 256 caracteres) | Ejemplo: ```json { "cer": "MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTY=", "key": "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI", "password": "12345678a" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.uploadCertificate("iss_OufpKqnrvx9p", { cer: "MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTY=", key: "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI", password: "12345678a", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Firmar el contrato con el PAC `POST https://api.fiscalisto.com/v1/issuers/{id}/efirma` Una sola vez por RFC antes de timbrar en producción: firma el contrato de servicio del PAC (Finkok) con la e.firma del emisor. La e.firma se usa en memoria para esa firma y no se guarda, salvo que mande keep_for_downloads: true para pedirle al SAT las facturas del emisor (POST /v1/sat_downloads); entonces se guarda cifrada y puede borrarla con DELETE. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `cer` | string | sí | El archivo .cer en base64 (puede traer el prefijo data:). (hasta 87386 caracteres) | | `key` | string | sí | El archivo .key en base64 (puede traer el prefijo data:). (hasta 87386 caracteres) | | `password` | string | sí | La contraseña de la llave privada (.key). Se usa para abrirla y no se guarda en texto. (hasta 256 caracteres) | | `keep_for_downloads` | boolean | | true para guardarla (cifrada) y poder pedir al SAT las facturas del emisor con POST /v1/sat_downloads. Sin esto solo firma el contrato con el PAC y no se guarda. | Ejemplo: ```json { "cer": "MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTY=", "key": "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI", "password": "12345678a", "keep_for_downloads": true } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.signEfirma("iss_OufpKqnrvx9p", { cer: "MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTY=", key: "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI", password: "12345678a", keep_for_downloads: true, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar la e.firma guardada `DELETE https://api.fiscalisto.com/v1/issuers/{id}/efirma` Borra la e.firma que guardó con keep_for_downloads. El emisor sigue timbrando; ya no puede pedir descargas al SAT hasta que la mande de nuevo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuer = await fiscalisto.issuers.deleteEfirma("iss_OufpKqnrvx9p"); ``` ## Respuesta 200 Correcto. Un objeto `Issuer`. Sus campos están en «Objetos» al final de este archivo (Issuer). ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear una liga de alta `POST https://api.fiscalisto.com/v1/issuers/{id}/onboarding_links` Una página en fiscalisto.com donde el dueño del emisor sube su CSD y firma el contrato con el PAC con su e.firma, sin que esos archivos ni sus contraseñas pasen por su plataforma. Mande a su cliente a url; al terminar vuelve a return_url y llega issuer.ready. Cada liga sirve para un emisor, hasta que vence. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `return_url` | string (uri) | | A dónde regresa el botón al terminar, por ejemplo la página de su plataforma donde su cliente sigue. Sin ella, la página solo dice que ya terminó. (hasta 2000 caracteres) | | `expires_in_hours` | integer | | Horas que la liga sirve, de 1 a 168 (7 días). Por omisión 24. (de 1 a 168) | Ejemplo: ```json { "return_url": "https://miplataforma.mx/configuracion/facturas" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const issuerOnboardingLink = await fiscalisto.issuers.createOnboardingLink("id", { return_url: "https://miplataforma.mx/configuracion/facturas", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `IssuerOnboardingLink`. Sus campos están en «Objetos» al final de este archivo (IssuerOnboardingLink). ```json { "object": "issuer_onboarding_link", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "url": "https://fiscalisto.com/alta/fko_8Xc1nQ2VbT0aLm3Rk7Yp9WdZs4Hf6Jg5Ue1Oi2Ky", "return_url": "https://miplataforma.mx/configuracion/facturas", "expires_at": "2026-10-25T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar una factura `POST https://api.fiscalisto.com/v1/invoices` Revisa la factura con las reglas del SAT, la sella y la timbra en una sola llamada. Responde con la factura timbrada (status valid) o, si el PAC tarda, en processing (escuche invoice.stamped). Lo que puede omitir tiene el valor usual: PUE, MXN, uso G03 (S01 con régimen 616), objeto de impuesto 02, IVA 16%, cantidad 1, unidad E48. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo Un objeto `InvoiceCreate`: sus campos en «Objetos». Ejemplo: ```json { "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "payment_form": "03", "items": [ { "description": "Mensualidad", "product_key": "86121700", "quantity": 2, "unit_price": 2500 } ], "send_email": true, "metadata": { "pedido": "A-1042" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, payment_form: "03", items: [ { description: "Mensualidad", product_key: "86121700", quantity: 2, unit_price: 2500, }, ], send_email: true, metadata: { pedido: "A-1042" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar facturas `GET https://api.fiscalisto.com/v1/invoices` Las más recientes primero. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `status` | consulta | | valid, pending, canceled, rejected o processing. | | `type` | consulta | | I, E, P, T o N. | | `customer` | consulta | | RFC del cliente o su id (cus_…). | | `payment_status` | consulta | | Facturas de ingreso por lo que deben: unpaid, partial, open (unpaid o partial) o paid. | | `created_gte` | consulta | | Creadas desde, en ISO 8601. Un día solo (2026-10-01) cuenta desde su inicio en hora de la Ciudad de México. | | `created_lte` | consulta | | Creadas hasta, en ISO 8601. Un día solo (2026-10-31) incluye el día completo en hora de la Ciudad de México. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.invoices.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `InvoiceList`. Sus campos están en «Objetos» al final de este archivo (InvoiceList). ```json { "object": "list", "data": [ { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ], "has_more": false, "url": "/v1/invoices" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una factura `GET https://api.fiscalisto.com/v1/invoices/{id}` Por su id (inv_…) o por su folio fiscal (UUID). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `issuer` | consulta | | Con un UUID en vez del id, el emisor (iss_…) si la cuenta tiene varios. | | `refresh` | consulta | | true para preguntar su estado al SAT ahora (cancelaciones en espera). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.get("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg"); ``` ## Respuesta 200 Correcto. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el PDF `GET https://api.fiscalisto.com/v1/invoices/{id}/pdf` La representación impresa del CFDI, como la recibe su cliente. En vez del id puede usar el UUID (con issuer si la cuenta tiene varios emisores). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `issuer` | consulta | | Con un UUID en vez del id, el emisor (iss_…) si la cuenta tiene varios. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.invoices.pdf("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el XML `GET https://api.fiscalisto.com/v1/invoices/{id}/xml` El CFDI timbrado, tal como lo tiene el SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `issuer` | consulta | | Con un UUID en vez del id, el emisor (iss_…) si la cuenta tiene varios. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.xml", await fiscalisto.invoices.xml("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Enviar por correo `POST https://api.fiscalisto.com/v1/invoices/{id}/send` Manda el PDF y el XML con el diseño de Fiscalisto. Sin `to`, al correo del cliente. Con la misma Idempotency-Key no se manda dos veces. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `to` | string | | A quién (correos separados por comas). (hasta 1000 caracteres) | | `cc` | string o null | | Con copia (correos separados por comas). | Ejemplo: ```json { "to": "pagos@cva.example", "cc": "contabilidad@cva.example" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.send("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { to: "pagos@cva.example", cc: "contabilidad@cva.example", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cancelar una factura `POST https://api.fiscalisto.com/v1/invoices/{id}/cancel` Pide la cancelación al SAT. Si el cliente debe aceptarla queda en pending (escuche invoice.canceled o invoice.cancellation_rejected). No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `motive` | `01`, `02`, `03`, `04` | | Motivo de cancelación (c_MotivoCancelacion): 01 con errores con relación (lleva substitution), 02 con errores sin relación (por omisión), 03 no se llevó a cabo, 04 solo en una factura global, cuando una venta incluida se facturó a nombre del cliente. (por omisión "02") | | `substitution` | string | | El comprobante que sustituye a este (su id o su UUID), con el motivo 01. | Ejemplo: ```json { "motive": "02" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.cancel("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { motive: "02" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 403, 404, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Sustituir una factura `POST https://api.fiscalisto.com/v1/invoices/{id}/substitute` Timbra la factura corregida (el mismo cuerpo que POST /v1/invoices, relacionada 04) y cancela la anterior con motivo 01. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo Un objeto `InvoiceCreate`: sus campos en «Objetos». Ejemplo: ```json { "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "payment_form": "03", "items": [ { "description": "Mensualidad", "product_key": "86121700", "quantity": 2, "unit_price": 2400 } ], "send_email": true, "metadata": { "pedido": "A-1042" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.substitute("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, payment_form: "03", items: [ { description: "Mensualidad", product_key: "86121700", quantity: 2, unit_price: 2400, }, ], send_email: true, metadata: { pedido: "A-1042" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Marcar pagada `POST https://api.fiscalisto.com/v1/invoices/{id}/mark_paid` Una factura PUE timbrada con paid_later (o marcada por cobrar) ya se pagó: queda paid y llega invoice.paid. Una PPD se paga con su complemento de pago (POST /v1/payments). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.markPaid("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Marcar por cobrar `POST https://api.fiscalisto.com/v1/invoices/{id}/mark_unpaid` Una factura PUE que el cliente aún no paga (le paga este mes): queda unpaid hasta POST /v1/invoices/{id}/mark_paid. Si el mes termina sin pago, la ley pide sustituirla por una PPD (regla 2.7.1.39 de la RMF). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.invoices.markUnpaid("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver lo que le deben `GET https://api.fiscalisto.com/v1/receivables` Lo que debe cada cliente, por moneda, de sus facturas abiertas (PPD con saldo y PUE por cobrar), ordenado por RFC. Las facturas de cada uno: GET /v1/invoices?customer=RFC&payment_status=open. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `customer` | consulta | | Solo este cliente: su RFC o su id (cus_…). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.receivables.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `ReceivableList`. Sus campos están en «Objetos» al final de este archivo (ReceivableList). ```json { "object": "list", "data": [ { "id": "ICV060329BY0:MXN", "object": "receivable", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "amount_due": 34800, "open_invoices": 2, "oldest_invoice_date": "2026-09-01" } ], "has_more": false, "url": "/v1/receivables" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Mandar depósitos `POST https://api.fiscalisto.com/v1/bank_transactions` Los depósitos de la cuenta del emisor (de la API de su banco o de un estado de cuenta), hasta 1,000 por solicitud. Uno que ya mandó (mismo día, monto, referencia y concepto) se omite, así que puede mandar periodos que se enciman. Cada uno trae suggested_match: las facturas que parece pagar. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `account` | string | | La cuenta (CLABE o número; se guardan sus últimos 4 dígitos). (hasta 40 caracteres) | | `transactions` | arreglo de objeto | sí | Los depósitos, hasta 1,000 por solicitud. (hasta 1000) | | `transactions[].date` | string | sí | El día del depósito (AAAA-MM-DD). | | `transactions[].amount` | number | sí | Lo depositado, en pesos. Los retiros no se mandan. (de -∞ a 1000000000000) | | `transactions[].description` | string | sí | El concepto del banco, como viene (SPEI, nombre del ordenante…). (hasta 500 caracteres) | | `transactions[].reference` | string | | La referencia o clave de rastreo. (hasta 100 caracteres) | | `transactions[].payer_tax_id` | string | | El RFC de quien depositó, si lo sabe (ayuda a sugerir la factura). | Ejemplo: ```json { "account": "012180001234567891", "transactions": [ { "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA", "reference": "F-1024", "payer_tax_id": "ICV060329BY0" } ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const bankImport = await fiscalisto.bankTransactions.create({ account: "012180001234567891", transactions: [ { date: "2026-10-24", amount: 5800, description: "SPEI RECIBIDO INMOBILIARIA CVA", reference: "F-1024", payer_tax_id: "ICV060329BY0", }, ], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `BankImport`. Sus campos están en «Objetos» al final de este archivo (BankImport). ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar depósitos `GET https://api.fiscalisto.com/v1/bank_transactions` Los más recientes primero; los abiertos con su suggested_match. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `status` | consulta | | open (sin conciliar), matched o classified. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.bankTransactions.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `BankTransactionList`. Sus campos están en «Objetos» al final de este archivo (BankTransactionList). ```json { "object": "list", "data": [ { "id": "btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", "object": "bank_transaction", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA · ICV060329BY0", "reference": "F-1024", "account": "4821", "payer_tax_id": "ICV060329BY0", "status": "open", "classification": null, "note": null, "invoices": [], "payment": null, "suggested_match": { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ], "amounts": { "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN": 5800 }, "method": "PUE", "confidence": "high", "reasons": [ "Mismo monto", "RFC del cliente en el concepto" ] }, "created_at": "2026-10-25T09:00:00-06:00" } ], "has_more": false, "url": "/v1/bank_transactions" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un depósito `GET https://api.fiscalisto.com/v1/bank_transactions/{id}` Un depósito mandado a conciliación, con las facturas que se le encontraron o el pago que se registró con él. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const bankTransaction = await fiscalisto.bankTransactions.get("btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx"); ``` ## Respuesta 200 Correcto. Un objeto `BankTransaction`. Sus campos están en «Objetos» al final de este archivo (BankTransaction). ```json { "id": "btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", "object": "bank_transaction", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA · ICV060329BY0", "reference": "F-1024", "account": "4821", "payer_tax_id": "ICV060329BY0", "status": "open", "classification": null, "note": null, "invoices": [], "payment": null, "suggested_match": { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ], "amounts": { "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN": 5800 }, "method": "PUE", "confidence": "high", "reasons": [ "Mismo monto", "RFC del cliente en el concepto" ] }, "created_at": "2026-10-25T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Conciliar un depósito `POST https://api.fiscalisto.com/v1/bank_transactions/{id}/match` El depósito pagó estas facturas (de un mismo cliente), o sin invoices, las de suggested_match. Sus facturas PUE por cobrar quedan pagadas (invoice.paid). No timbra: para pagar una PPD con este depósito, POST /v1/payments con bank_transaction. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `invoices` | arreglo de string | | Las facturas (inv_… o UUID) que pagó este depósito, de un mismo cliente. Sin ellas, las de suggested_match. (hasta 50) | Ejemplo: ```json { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const bankTransaction = await fiscalisto.bankTransactions.match("btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", { invoices: ["inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN"], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `BankTransaction`. Sus campos están en «Objetos» al final de este archivo (BankTransaction). ```json { "id": "btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", "object": "bank_transaction", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA · ICV060329BY0", "reference": "F-1024", "account": "4821", "payer_tax_id": "ICV060329BY0", "status": "open", "classification": null, "note": null, "invoices": [], "payment": null, "suggested_match": { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ], "amounts": { "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN": 5800 }, "method": "PUE", "confidence": "high", "reasons": [ "Mismo monto", "RFC del cliente en el concepto" ] }, "created_at": "2026-10-25T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Explicar un depósito sin factura `POST https://api.fiscalisto.com/v1/bank_transactions/{id}/classify` Un depósito que ninguna factura explica: venta (aún sin facturar), prestamo, traspaso o devolucion. El SAT puede presumir ingreso un depósito sin explicar (CFF art. 59 fr. III). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `classification` | `venta`, `prestamo`, `traspaso`, `devolucion` | sí | venta (una venta que aún no se factura), prestamo, traspaso (entre cuentas propias) o devolucion. | | `note` | string | | Una nota para su equipo (de dónde vino, por qué). (hasta 500 caracteres) | Ejemplo: ```json { "classification": "traspaso", "note": "De la cuenta de ahorro" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const bankTransaction = await fiscalisto.bankTransactions.classify("btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", { classification: "traspaso", note: "De la cuenta de ahorro", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `BankTransaction`. Sus campos están en «Objetos» al final de este archivo (BankTransaction). ```json { "id": "btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", "object": "bank_transaction", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA · ICV060329BY0", "reference": "F-1024", "account": "4821", "payer_tax_id": "ICV060329BY0", "status": "open", "classification": null, "note": null, "invoices": [], "payment": null, "suggested_match": { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ], "amounts": { "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN": 5800 }, "method": "PUE", "confidence": "high", "reasons": [ "Mismo monto", "RFC del cliente en el concepto" ] }, "created_at": "2026-10-25T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Deshacer la conciliación `POST https://api.fiscalisto.com/v1/bank_transactions/{id}/reopen` Vuelve a open, y sus facturas vuelven a deber. Si se pagó con un complemento vigente, cancélelo antes. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const bankTransaction = await fiscalisto.bankTransactions.reopen("btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `BankTransaction`. Sus campos están en «Objetos» al final de este archivo (BankTransaction). ```json { "id": "btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", "object": "bank_transaction", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA · ICV060329BY0", "reference": "F-1024", "account": "4821", "payer_tax_id": "ICV060329BY0", "status": "open", "classification": null, "note": null, "invoices": [], "payment": null, "suggested_match": { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ], "amounts": { "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN": 5800 }, "method": "PUE", "confidence": "high", "reasons": [ "Mismo monto", "RFC del cliente en el concepto" ] }, "created_at": "2026-10-25T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar un complemento de pago `POST https://api.fiscalisto.com/v1/payments` El recibo de pago (CFDI tipo P, Pagos 2.0) de facturas PPD: parcialidad, saldo anterior e insoluto e impuestos de cada una los calcula Fiscalisto. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `date` | string | | Fecha en que le pagaron (FechaPago), AAAA-MM-DD. Por omisión hoy. | | `payment_form` | string | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `operation_number` | string o null | | Número de operación del pago (NumOperacion), como el rastreo SPEI. | | `currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `exchange_rate` | number o null | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. | | `series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `documents` | arreglo de objeto | sí | Las facturas que paga (DoctoRelacionado). (hasta 100) | | `documents[].invoice` | string | sí | La factura (inv_… o su UUID). | | `documents[].amount` | number | sí | Lo que se paga de esa factura (ImpPagado), en la moneda de la factura. | | `documents[].received` | number o null | | Solo si la factura es de otra moneda que el pago: lo que recibió por ella en la moneda del pago. Fiscalisto calcula la equivalencia (EquivalenciaDR). | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `email` | string (email) | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `email_cc` | string o null | | Correos con copia, separados por comas. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `bank_transaction` | string | | El depósito (btx_…) con que le pagaron: queda conciliado con este complemento, y date es su día si no manda otra. | Ejemplo: ```json { "payment_form": "03", "operation_number": "SPEI-2026102400123", "documents": [ { "invoice": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "amount": 2900 } ], "send_email": true } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.payments.create({ payment_form: "03", operation_number: "SPEI-2026102400123", documents: [ { invoice: "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", amount: 2900, }, ], send_email: true, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar una nota de crédito `POST https://api.fiscalisto.com/v1/credit_notes` Un egreso (tipo E) relacionado 01 (o 03, devolución) a una factura, con su IVA y retenciones en la misma proporción. amount es antes de impuestos. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `invoice` | string | sí | La factura (inv_… o su UUID). | | `amount` | number | sí | Importe de la nota antes de impuestos: Fiscalisto agrega el IVA y las retenciones de la factura en la misma proporción. | | `description` | string | | Descripción del concepto de la nota (Descripcion), como «Descuento por pronto pago». (hasta 1000 caracteres) | | `payment_form` | string | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `relationship` | `01`, `03` | | Tipo de relación (TipoRelacion): 01 nota de crédito, 03 devolución, 04 sustitución, 07 anticipo… | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `email` | string (email) | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `email_cc` | string o null | | Correos con copia, separados por comas. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "invoice": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "amount": 500, "description": "Descuento por pronto pago", "payment_form": "03" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.creditNotes.create({ invoice: "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", amount: 500, description: "Descuento por pronto pago", payment_form: "03", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar un anticipo `POST https://api.fiscalisto.com/v1/advances` La factura del anticipo: un concepto «Anticipo del bien o servicio» (84111506, ACT). amount es lo que pagó el cliente con IVA. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `customer` | string (cus_…) o objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `customer.email` | string o null | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `amount` | number | sí | Lo que pagó el cliente, con IVA incluido. | | `iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. (por omisión "0.16") | | `payment_form` | string | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. | | `series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `email_cc` | string o null | | Correos con copia, separados por comas. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "amount": 1160, "payment_form": "03" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.advances.create({ customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, amount: 1160, payment_form: "03", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar anticipos sin aplicar `GET https://api.fiscalisto.com/v1/advances` Los anticipos timbrados que todavía no se aplican a una factura, con lo que queda de cada uno. Filtre por customer para los de un cliente. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `customer` | consulta | | RFC del cliente o su id (cus_…). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.advances.list(); ``` ## Respuesta 200 Correcto. Un objeto `AdvanceList`. Sus campos están en «Objetos» al final de este archivo (AdvanceList). ```json { "object": "list", "data": [ { "object": "advance", "uuid": "D7BDBADC-7E6E-41E0-8D94-7E5FC4BB796E", "date": "2026-10-24", "series": null, "folio": "1", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "amount": 1000, "total": 1160, "iva": "0.16", "currency": "MXN" } ], "has_more": false, "url": "/v1/advances" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Aplicar un anticipo `POST https://api.fiscalisto.com/v1/advances/{id}/apply` Timbra la factura de lo vendido (el cuerpo de POST /v1/invoices) relacionada 07 al anticipo. Con method egreso, también la nota de aplicación. La misma Idempotency-Key continúa donde se quedó. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `method` | `egreso`, `remanente` | | egreso (la factura completa y después una nota de crédito por el anticipo, opción A) o remanente (la factura solo por lo que falta, opción B). (por omisión "egreso") | | `remainder` | objeto | | Cómo se paga lo que falta del anticipo: ahora con su forma de pago, o después (PPD). | | `remainder.paid_now` | boolean | sí | true si lo que falta se paga ahora (PUE); false si después (PPD). | | `remainder.payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `branch` | string (brn_…) | | Sucursal (brn_…) desde donde se factura: su código postal va en LugarExpedicion, su dirección en el PDF y, si las tiene, sus series. Sin ella, el domicilio fiscal del emisor (la Matriz). | | `warehouse` | string (whs_…) | | Almacén (whs_…) de donde salen sus productos con track_stock. Por omisión, el de su sucursal (o el de la Matriz). | | `customer` | string (cus_…) o objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `customer.email` | string o null | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. (hasta 200) | | `items[].product` | string (prd_…) | | Un producto de su catálogo (prd_…): toma su clave, unidad, precio e impuestos. | | `items[].description` | string | | Descripción del concepto (Descripcion), hasta 1,000 caracteres. (hasta 1000 caracteres) | | `items[].product_key` | string | | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `items[].unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… (hasta 5 caracteres) | | `items[].unit_name` | string o null | | Nombre de la unidad (Unidad), hasta 20 caracteres. | | `items[].sku` | string o null | | Número de identificación o SKU (NoIdentificacion). | | `items[].quantity` | number | | Cantidad (Cantidad). Por omisión 1. (de -∞ a 1000000000) | | `items[].unit_price` | number | | Precio unitario antes de impuestos (ValorUnitario). (de 0 a 1000000000000) | | `items[].discount` | number | | Descuento (Descuento). (de 0 a 1000000000000) | | `items[].taxability` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` | | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… | | `items[].iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `items[].ieps` | number | | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. (de 0 a 72.1605) | | `items[].ieps_factor` | `Tasa`, `Cuota` | | Tasa o Cuota (TipoFactor del IEPS). | | `items[].ieps_units` | number | | Unidades de IEPS a cuota (litros, cigarros…), si no son la cantidad. (de -∞ a 1000000000) | | `items[].iva_retention` | number | | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). (de 0 a 0.16) | | `items[].isr_retention` | number | | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). (de 0 a 0.35) | | `items[].predial` | arreglo de string | | Números de cuenta predial (CuentaPredial) en arrendamiento. (hasta 10) | | `items[].pedimentos` | arreglo de string | | Números de pedimento (InformacionAduanera) de mercancía importada. (hasta 50) | | `items[].third_party` | objeto | | A cuenta de terceros (ACuentaTerceros): RFC, nombre, régimen y código postal. | | `items[].third_party.tax_id` | string | sí | RFC de a cuenta de quién (RfcACuentaTerceros). (hasta 13 caracteres) | | `items[].third_party.name` | string | sí | Nombre (NombreACuentaTerceros). (hasta 300 caracteres) | | `items[].third_party.tax_system` | string | sí | Régimen fiscal (RegimenFiscalACuentaTerceros), c_RegimenFiscal. | | `items[].third_party.zip` | string | sí | Código postal de su domicilio fiscal (DomicilioFiscalACuentaTerceros). | | `items[].iedu` | objeto | | Complemento de instituciones educativas privadas (IEDU): alumno, CURP, nivel y RVOE. | | `items[].iedu.student_name` | string | sí | Nombre del alumno (nombreAlumno). (hasta 300 caracteres) | | `items[].iedu.curp` | string | sí | CURP, 18 caracteres. (18 caracteres) | | `items[].iedu.level` | `Preescolar`, `Primaria`, `Secundaria`, `Profesional técnico`, `Bachillerato o su equivalente` | sí | Nivel educativo (nivelEducativo). | | `items[].iedu.rvoe` | string | sí | Reconocimiento de validez oficial de estudios (autRVOE). (hasta 30 caracteres) | | `items[].iedu.payer_tax_id` | string o null | | RFC de quien paga, si no es el receptor (rfcPago). | | `items[].used_vehicle` | objeto | | Complemento de vehículo usado. | | `items[].used_vehicle.purchase_amount` | number | sí | Monto de adquisición (montoAdquisicion). (de -∞ a 1000000000000) | | `items[].used_vehicle.sale_amount` | number | sí | Monto de enajenación (montoEnajenacion). (de -∞ a 1000000000000) | | `items[].used_vehicle.vehicle_key` | string | sí | Clave vehicular (claveVehicular). (hasta 7 caracteres) | | `items[].used_vehicle.make` | string | sí | Marca (marca). (hasta 50 caracteres) | | `items[].used_vehicle.type` | string | sí | Tipo o versión (tipo). (hasta 50 caracteres) | | `items[].used_vehicle.model` | string | sí | Año del modelo (modelo), 4 dígitos. | | `items[].used_vehicle.engine_number` | string | | Número de motor (numeroMotor). (hasta 17 caracteres) | | `items[].used_vehicle.serial_number` | string | | Número de serie (numeroSerie). (hasta 17 caracteres) | | `items[].used_vehicle.niv` | string | | Número de identificación vehicular (NIV). (hasta 17 caracteres) | | `items[].used_vehicle.guide_value` | number | sí | Valor según la guía EBC o Libro Azul (valor). (de -∞ a 1000000000000) | | `items[].used_vehicle.customs` | arreglo de objeto | | Información aduanera de la importación del vehículo (InformacionAduanera). (hasta 10) | | `items[].used_vehicle.customs[].number` | string | sí | Número del documento de importación (numero). (hasta 100 caracteres) | | `items[].used_vehicle.customs[].date` | string | sí | Fecha del documento (fecha), AAAA-MM-DD. | | `items[].used_vehicle.customs[].office` | string | | Aduana (aduana). (hasta 100 caracteres) | | `items[].addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `type` | `I`, `T` | | I ingreso (por omisión; con global, factura global) o T traslado. Notas de crédito: POST /v1/credit_notes. Pagos: POST /v1/payments. (por omisión "I") | | `carta_porte` | objeto | | Complemento Carta Porte 3.1 (traslado de mercancías). | | `carta_porte.modes` | arreglo de `01`, `02`, `03`, `04` | | Cómo viajan las mercancías (c_CveTransporte): 01 autotransporte, 02 marítimo, 03 aéreo, 04 ferroviario; varios si es intermodal. Por omisión 01. (hasta 4) | | `carta_porte.international` | objeto | | Transporte internacional (TranspInternac Sí): entrada o salida, país, vía y regímenes aduaneros. | | `carta_porte.international.direction` | `Entrada`, `Salida` | sí | EntradaSalidaMerc: Entrada o Salida. | | `carta_porte.international.country` | string | sí | País de origen o destino (PaisOrigenDestino), c_Pais. (3 caracteres) | | `carta_porte.international.via` | string | sí | Vía de entrada o salida (ViaEntradaSalida), c_CveTransporte (05 ducto incluido). (hasta 2 caracteres) | | `carta_porte.international.regimes` | arreglo de string | sí | Regímenes aduaneros (RegimenAduanero), c_RegimenAduanero. (hasta 10) | | `carta_porte.istmo` | objeto | | Corredor Interoceánico del Istmo de Tehuantepec: polos de origen y destino (c_RegistroISTMO). | | `carta_porte.istmo.origin` | string | sí | Polo de origen (UbicacionPoloOrigen). (hasta 2 caracteres) | | `carta_porte.istmo.destination` | string | sí | Polo de destino (UbicacionPoloDestino). (hasta 2 caracteres) | | `carta_porte.reverse_logistics` | boolean | | Autotransporte: true si es logística inversa, recolección o devolución. | | `carta_porte.origin` | objeto | sí | Ubicación de origen (Ubicacion TipoUbicacion Origen): remitente, fecha y hora de salida y domicilio. | | `carta_porte.origin.tax_id` | string | sí | RFC del remitente (RFCRemitenteDestinatario); XEXX010101000 si es extranjero. | | `carta_porte.origin.name` | string | | Nombre del remitente (NombreRemitenteDestinatario). (hasta 254 caracteres) | | `carta_porte.origin.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.origin.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.origin.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.origin.date_time` | string | sí | Fecha y hora de salida (origen) o llegada (destino), AAAA-MM-DDTHH:MM, hora local. | | `carta_porte.origin.distance_km` | number | | Distancia recorrida desde la ubicación anterior, en km (DistanciaRecorrida). Autotransporte y ferroviario. (de -∞ a 99999) | | `carta_porte.origin.station` | objeto | | Estación, puerto o aeropuerto (TipoEstacion y NumEstacion de c_Estaciones). Solo sin autotransporte. | | `carta_porte.origin.station.type` | string | | Tipo de estación (TipoEstacion): 01 origen, 02 intermedia, 03 destino final. (hasta 2 caracteres) | | `carta_porte.origin.station.number` | string | | Clave de la estación, puerto o aeropuerto (NumEstacion), c_Estaciones. (hasta 12 caracteres) | | `carta_porte.origin.station.name` | string | | Nombre de la estación (NombreEstacion). (hasta 50 caracteres) | | `carta_porte.origin.station.foreign` | boolean | | true si la estación está en el extranjero (va sin TipoEstacion). | | `carta_porte.origin.navigation` | `Altura`, `Cabotaje` | | Marítimo: tipo de tráfico (NavegacionTrafico), Altura o Cabotaje. | | `carta_porte.origin.address` | objeto | sí | Domicilio (Domicilio). En México basta el código postal: estado, municipio y localidad se toman de c_CodigoPostal. | | `carta_porte.origin.address.street` | string | | Calle (Calle). (hasta 100 caracteres) | | `carta_porte.origin.address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `carta_porte.origin.address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `carta_porte.origin.address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `carta_porte.origin.address.country` | string | | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `carta_porte.origin.address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `carta_porte.origin.address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.origin.address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.origin.address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.destinations` | arreglo de objeto | sí | Ubicaciones de destino (TipoUbicacion Destino), en orden: destinatario, llegada, distancia y domicilio. (hasta 20) | | `carta_porte.destinations[].tax_id` | string | sí | RFC del destinatario (RFCRemitenteDestinatario); XEXX010101000 si es extranjero. | | `carta_porte.destinations[].name` | string | | Nombre del destinatario (NombreRemitenteDestinatario). (hasta 254 caracteres) | | `carta_porte.destinations[].foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.destinations[].foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.destinations[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.destinations[].date_time` | string | sí | Fecha y hora de salida (origen) o llegada (destino), AAAA-MM-DDTHH:MM, hora local. | | `carta_porte.destinations[].distance_km` | number | | Distancia recorrida desde la ubicación anterior, en km (DistanciaRecorrida). Autotransporte y ferroviario. (de -∞ a 99999) | | `carta_porte.destinations[].station` | objeto | | Estación, puerto o aeropuerto (TipoEstacion y NumEstacion de c_Estaciones). Solo sin autotransporte. | | `carta_porte.destinations[].station.type` | string | | Tipo de estación (TipoEstacion): 01 origen, 02 intermedia, 03 destino final. (hasta 2 caracteres) | | `carta_porte.destinations[].station.number` | string | | Clave de la estación, puerto o aeropuerto (NumEstacion), c_Estaciones. (hasta 12 caracteres) | | `carta_porte.destinations[].station.name` | string | | Nombre de la estación (NombreEstacion). (hasta 50 caracteres) | | `carta_porte.destinations[].station.foreign` | boolean | | true si la estación está en el extranjero (va sin TipoEstacion). | | `carta_porte.destinations[].navigation` | `Altura`, `Cabotaje` | | Marítimo: tipo de tráfico (NavegacionTrafico), Altura o Cabotaje. | | `carta_porte.destinations[].address` | objeto | sí | Domicilio (Domicilio). En México basta el código postal: estado, municipio y localidad se toman de c_CodigoPostal. | | `carta_porte.destinations[].address.street` | string | | Calle (Calle). (hasta 100 caracteres) | | `carta_porte.destinations[].address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `carta_porte.destinations[].address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `carta_porte.destinations[].address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `carta_porte.destinations[].address.country` | string | | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `carta_porte.destinations[].address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `carta_porte.destinations[].address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.destinations[].address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.destinations[].address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.goods` | arreglo de objeto | sí | Mercancías (Mercancia) que se transportan. (hasta 200) | | `carta_porte.goods[].product_key` | string | sí | Clave del bien transportado (BienesTransp), c_ClaveProdServCP. | | `carta_porte.goods[].description` | string | sí | Descripción de la mercancía (Descripcion). (hasta 1000 caracteres) | | `carta_porte.goods[].quantity` | number | sí | Cantidad (Cantidad). (de -∞ a 1000000000) | | `carta_porte.goods[].unit_key` | string | sí | Clave de unidad (ClaveUnidad), c_ClaveUnidad. (hasta 5 caracteres) | | `carta_porte.goods[].weight_kg` | number | sí | Peso en kilogramos (PesoEnKg). (de 0.001 a 100000000) | | `carta_porte.goods[].dimensions` | string | | Dimensiones (Dimensiones), como 30/40/30cm o en plg. (hasta 20 caracteres) | | `carta_porte.goods[].hazmat` | objeto | | Material peligroso (MaterialPeligroso Sí): clave UN y embalaje. | | `carta_porte.goods[].hazmat.key` | string | sí | Clave del material peligroso (CveMaterialPeligroso), c_MaterialPeligroso (número UN). (hasta 4 caracteres) | | `carta_porte.goods[].hazmat.packaging` | string | sí | Tipo de embalaje (Embalaje), c_TipoEmbalaje. (hasta 4 caracteres) | | `carta_porte.goods[].hazmat.packaging_description` | string | | Descripción del embalaje (DescripEmbalaje). (hasta 100 caracteres) | | `carta_porte.goods[].cofepris` | objeto | | Datos COFEPRIS de la mercancía: sector y lo que pide cada uno (medicamentos, precursores, psicotrópicos, sustancias tóxicas, plaguicidas). | | `carta_porte.goods[].cofepris.sector` | string | sí | Sector COFEPRIS (SectorCOFEPRIS): 01 medicamento, 02 precursores y químicos de uso dual, 03 psicotrópicos y estupefacientes, 04 sustancias tóxicas, 05 plaguicidas y fertilizantes. (hasta 2 caracteres) | | `carta_porte.goods[].cofepris.active_ingredient` | string | | Nombre del ingrediente activo (NombreIngredienteActivo). (hasta 1000 caracteres) | | `carta_porte.goods[].cofepris.chemical_name` | string | | Nombre químico (NomQuimico). (hasta 150 caracteres) | | `carta_porte.goods[].cofepris.generic_name` | string | | Denominación genérica del producto (DenominacionGenericaProd). (hasta 50 caracteres) | | `carta_porte.goods[].cofepris.brand_name` | string | | Denominación distintiva o marca (DenominacionDistintivaProd). (hasta 50 caracteres) | | `carta_porte.goods[].cofepris.manufacturer` | string | | Fabricante (Fabricante). (hasta 240 caracteres) | | `carta_porte.goods[].cofepris.expiry` | string | | Fecha de caducidad (FechaCaducidad), AAAA-MM-DD. (hasta 10 caracteres) | | `carta_porte.goods[].cofepris.lot` | string | | Lote del medicamento (LoteMedicamento). (hasta 10 caracteres) | | `carta_porte.goods[].cofepris.form` | string | | Forma farmacéutica (FormaFarmaceutica), c_FormaFarmaceutica. (hasta 2 caracteres) | | `carta_porte.goods[].cofepris.conditions` | string | | Condiciones especiales de transporte (CondicionesEspTransp), c_CondicionesEspeciales. (hasta 2 caracteres) | | `carta_porte.goods[].cofepris.sanitary_registry` | string | | Registro sanitario o folio de autorización (RegistroSanitarioFolioAutorizacion). (hasta 15 caracteres) | | `carta_porte.goods[].cofepris.import_permit` | string | | Permiso de importación (PermisoImportacion). (hasta 6 caracteres) | | `carta_porte.goods[].cofepris.vucem_folio` | string | | Folio de importación VUCEM (FolioImpoVUCEM). (hasta 25 caracteres) | | `carta_porte.goods[].cofepris.cas` | string | | Número CAS de la sustancia (NumCAS). (hasta 15 caracteres) | | `carta_porte.goods[].cofepris.importer` | string | | Razón social de la empresa importadora (RazonSocialEmpImp). (hasta 80 caracteres) | | `carta_porte.goods[].cofepris.pesticide_registry` | string | | Registro sanitario de plaguicidas COFEPRIS (NumRegSanPlagCOFEPRIS). (hasta 60 caracteres) | | `carta_porte.goods[].cofepris.manufacturer_data` | string | | Datos del fabricante (DatosFabricante). (hasta 600 caracteres) | | `carta_porte.goods[].cofepris.formulator_data` | string | | Datos del formulador (DatosFormulador). (hasta 600 caracteres) | | `carta_porte.goods[].cofepris.maquilador_data` | string | | Datos del maquilador (DatosMaquilador). (hasta 600 caracteres) | | `carta_porte.goods[].cofepris.authorized_use` | string | | Uso autorizado (UsoAutorizado). (hasta 1000 caracteres) | | `carta_porte.goods[].value` | number | | Valor de la mercancía (ValorMercancia); obligatorio en transporte aéreo. (de 0 a 1000000000000000) | | `carta_porte.goods[].currency` | string | | Moneda del valor de la mercancía (Moneda), c_Moneda. (3 caracteres) | | `carta_porte.goods[].tariff` | string | | Fracción arancelaria (FraccionArancelaria), 10 dígitos. Transporte internacional. (hasta 10 caracteres) | | `carta_porte.goods[].cce_uuid` | string | | UUID del CFDI con complemento de comercio exterior (UUIDComercioExt). (hasta 36 caracteres) | | `carta_porte.goods[].material` | objeto | | Tipo de materia (TipoMateria, c_TipoMateria) y su descripción si es «otra». | | `carta_porte.goods[].material.type` | string | sí | Tipo de materia (TipoMateria), c_TipoMateria. (hasta 2 caracteres) | | `carta_porte.goods[].material.description` | string | | Descripción de la materia (DescripcionMateria), con el tipo 05 otra. (hasta 50 caracteres) | | `carta_porte.goods[].customs_docs` | arreglo de objeto | | Documentación aduanera (DocumentacionAduanera) de la mercancía en transporte internacional. (hasta 20) | | `carta_porte.goods[].customs_docs[].type` | string | sí | Tipo de documento (TipoDocumento), c_DocumentoAduanero: 01 pedimento… (hasta 2 caracteres) | | `carta_porte.goods[].customs_docs[].pedimento` | string | | Número de pedimento (NumPedimento), con el formato «NN NN NNNN NNNNNNN». Solo con el tipo 01. (hasta 21 caracteres) | | `carta_porte.goods[].customs_docs[].doc_id` | string | | Identificador del documento aduanero (IdentDocAduanero), con un tipo distinto de 01. (hasta 150 caracteres) | | `carta_porte.goods[].customs_docs[].importer_tax_id` | string | | RFC del importador (RFCImpo), con un pedimento. (hasta 13 caracteres) | | `carta_porte.goods[].guides` | arreglo de objeto | | Paquetería: guías de los paquetes o tarimas (GuiasIdentificacion). (hasta 100) | | `carta_porte.goods[].guides[].number` | string | sí | Número de guía (NumeroGuiaIdentificacion). (hasta 30 caracteres) | | `carta_porte.goods[].guides[].description` | string | sí | Descripción del contenido (DescripGuiaIdentificacion). (hasta 1000 caracteres) | | `carta_porte.goods[].guides[].weight_kg` | number | sí | Peso en kilogramos (PesoEnKg). (de 0 a 100000000) | | `carta_porte.goods[].quantities` | arreglo de objeto | | Cuánto va a cada destino (CantidadTransporta), por destino. (hasta 40) | | `carta_porte.goods[].quantities[].quantity` | number | sí | Cantidad que va a ese destino. (de -∞ a 1000000000) | | `carta_porte.goods[].quantities[].destination` | integer | sí | El destino, por su posición en destinations (desde 0). (de 0 a 19) | | `carta_porte.goods[].quantities[].mode` | `01`, `02`, `03`, `04` | | Medio en que va esa cantidad (CvesTransporte), c_CveTransporte. | | `carta_porte.goods[].detail` | objeto | | Marítimo: detalle de la mercancía (DetalleMercancia): kilos netos, tara y piezas. | | `carta_porte.goods[].detail.net_kg` | number | sí | Peso neto en kg (PesoNeto). (de 0 a 100000000) | | `carta_porte.goods[].detail.tare_kg` | number | sí | Tara en kg (PesoTara). (de 0 a 100000000) | | `carta_porte.goods[].detail.pieces` | integer | | Número de piezas (NumPiezas). (de 0 a 1000000000) | | `carta_porte.goods[].stcc` | string | | Ferroviario: clave STCC (ClaveSTCC). (hasta 7 caracteres) | | `carta_porte.vehicle` | objeto | | Autotransporte (Autotransporte): vehículo, permiso SCT, seguros y remolques. | | `carta_porte.vehicle.config` | string | sí | Configuración vehicular (ConfigVehicular), c_ConfigAutotransporte: C2, T3S2… (hasta 10 caracteres) | | `carta_porte.vehicle.plate` | string | sí | Placa (PlacaVM), sin guiones ni espacios. (hasta 10 caracteres) | | `carta_porte.vehicle.year` | integer | sí | Año del modelo (AnioModeloVM). (de 0 a 2100) | | `carta_porte.vehicle.gross_weight` | number | sí | Peso bruto vehicular en toneladas (PesoBrutoVehicular). (de 0 a 999) | | `carta_porte.vehicle.permit_type` | string | sí | Tipo de permiso SCT (PermSCT), c_TipoPermiso. (hasta 50 caracteres) | | `carta_porte.vehicle.permit_number` | string | sí | Número de permiso SCT (NumPermisoSCT). (hasta 50 caracteres) | | `carta_porte.vehicle.insurer` | string | sí | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.vehicle.policy` | string | sí | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.vehicle.env_insurer` | string | | Aseguradora de daños al medio ambiente (AseguraMedAmbiente); obligatoria con material peligroso. (hasta 50 caracteres) | | `carta_porte.vehicle.env_policy` | string | | Póliza de daños al medio ambiente (PolizaMedAmbiente). (hasta 30 caracteres) | | `carta_porte.vehicle.cargo_insurer` | string | | Aseguradora de la carga (AseguraCarga). (hasta 50 caracteres) | | `carta_porte.vehicle.cargo_policy` | string | | Póliza de la carga (PolizaCarga). (hasta 30 caracteres) | | `carta_porte.vehicle.premium` | number | | Prima del seguro (PrimaSeguro). (de 0 a 1000000000000) | | `carta_porte.vehicle.trailers` | arreglo de objeto | | Remolques o semirremolques (Remolque): tipo (c_SubTipoRem) y placa. (hasta 2) | | `carta_porte.vehicle.trailers[].type` | string | sí | Subtipo de remolque (SubTipoRem), c_SubTipoRem. (hasta 10 caracteres) | | `carta_porte.vehicle.trailers[].plate` | string | sí | Placa del remolque (Placa). (hasta 10 caracteres) | | `carta_porte.sea` | objeto | | Transporte marítimo (TransporteMaritimo): embarcación, naviero y contenedores. | | `carta_porte.sea.permit_type` | string | | Tipo de permiso SCT (PermSCT), c_TipoPermiso. (hasta 10 caracteres) | | `carta_porte.sea.permit_number` | string | | Número de permiso SCT (NumPermisoSCT). (hasta 30 caracteres) | | `carta_porte.sea.insurer` | string | | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.sea.policy` | string | | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.sea.vessel_type` | string | sí | Tipo de embarcación (TipoEmbarcacion), c_ConfigMaritima. (hasta 4 caracteres) | | `carta_porte.sea.registration` | string | sí | Matrícula (Matricula). (hasta 30 caracteres) | | `carta_porte.sea.imo` | string | sí | Número IMO (NumeroOMI): IMO y 7 dígitos. (hasta 10 caracteres) | | `carta_porte.sea.year` | integer | | Año de la embarcación (AnioEmbarcacion). (de 0 a 2100) | | `carta_porte.sea.vessel_name` | string | | Nombre de la embarcación (NombreEmbarc). (hasta 50 caracteres) | | `carta_porte.sea.flag` | string | sí | Nacionalidad de la embarcación (NacionalidadEmbarc), c_Pais. (3 caracteres) | | `carta_porte.sea.gross_tonnage` | number | sí | Unidades de arqueo bruto (UnidadesDeArqBruto). (de 0 a 1000000000) | | `carta_porte.sea.cargo_type` | string | sí | Tipo de carga (TipoCarga), c_ClaveTipoCarga. (hasta 4 caracteres) | | `carta_porte.sea.length` | number | | Eslora en metros (Eslora). (de 0 a 100000) | | `carta_porte.sea.beam` | number | | Manga en metros (Manga). (de 0 a 100000) | | `carta_porte.sea.draft` | number | | Calado en metros (Calado). (de 0 a 100000) | | `carta_porte.sea.depth` | number | | Puntal en metros (Puntal). (de 0 a 100000) | | `carta_porte.sea.line` | string | | Línea naviera (LineaNaviera). (hasta 50 caracteres) | | `carta_porte.sea.agent_name` | string | sí | Nombre del agente naviero consignatario (NombreAgenteNaviero). (hasta 100 caracteres) | | `carta_porte.sea.agent_authorization` | string | sí | Autorización del agente naviero (NumAutorizacionNaviero), c_NumAutorizacionNaviero. (hasta 20 caracteres) | | `carta_porte.sea.voyage` | string | | Número de viaje (NumViaje). (hasta 30 caracteres) | | `carta_porte.sea.bill_of_lading` | string | | Número de conocimiento de embarque (NumConocEmbarc). (hasta 30 caracteres) | | `carta_porte.sea.temp_permit` | string | | Permiso temporal de navegación (PermisoTempNavegacion), para una embarcación extranjera. (hasta 19 caracteres) | | `carta_porte.sea.containers` | arreglo de objeto | | Contenedores (Contenedor). (hasta 100) | | `carta_porte.sea.containers[].type` | string | sí | Tipo de contenedor (TipoContenedor), c_ContenedorMaritimo o c_Contenedor en ferroviario. (hasta 6 caracteres) | | `carta_porte.sea.containers[].registration` | string | | Matrícula (Matricula). (hasta 15 caracteres) | | `carta_porte.sea.containers[].seal` | string | | Número de precinto (NumPrecinto). (hasta 20 caracteres) | | `carta_porte.sea.containers[].related_id_ccp` | string | | Ferri: el IdCCP de la Carta Porte del autotransporte a bordo (IdCCPRelacionado). (hasta 36 caracteres) | | `carta_porte.sea.containers[].truck_plate` | string | | Ferri: placa del vehículo a bordo (PlacaVMCCP). (hasta 10 caracteres) | | `carta_porte.sea.containers[].certified_at` | string | | Ferri: fecha de certificación de la Carta Porte del vehículo (FechaCertificacionCCP). (hasta 19 caracteres) | | `carta_porte.sea.containers[].trailers` | arreglo de objeto | | Remolques o semirremolques (Remolque): tipo (c_SubTipoRem) y placa. (hasta 2) | | `carta_porte.sea.containers[].trailers[].type` | string | sí | Subtipo de remolque (SubTipoRem), c_SubTipoRem. (hasta 10 caracteres) | | `carta_porte.sea.containers[].trailers[].plate` | string | sí | Placa del remolque (Placa). (hasta 10 caracteres) | | `carta_porte.air` | objeto | | Transporte aéreo (TransporteAereo): permiso, guía y transportista. | | `carta_porte.air.permit_type` | string | sí | Tipo de permiso SCT (PermSCT), c_TipoPermiso. (hasta 10 caracteres) | | `carta_porte.air.permit_number` | string | sí | Número de permiso SCT (NumPermisoSCT). (hasta 50 caracteres) | | `carta_porte.air.aircraft` | string | | Matrícula de la aeronave (MatriculaAeronave). (hasta 11 caracteres) | | `carta_porte.air.insurer` | string | | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.air.policy` | string | | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.air.waybill` | string | sí | Número de guía aérea (NumeroGuia), de 12 a 15 caracteres. (hasta 15 caracteres) | | `carta_porte.air.contract_place` | string | | Lugar del contrato (LugarContrato). (hasta 120 caracteres) | | `carta_porte.air.carrier` | string | sí | Código del transportista (CodigoTransportista), c_CodigoTransporteAereo. (hasta 6 caracteres) | | `carta_porte.air.shipper` | objeto | | Embarcador (RFCEmbarcador o NumRegIdTribEmbarc y ResidenciaFiscalEmbarc, NombreEmbarcador). | | `carta_porte.air.shipper.tax_id` | string | | RFC del embarcador (RFCEmbarcador). (hasta 13 caracteres) | | `carta_porte.air.shipper.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.air.shipper.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.air.shipper.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.air.shipper.name` | string | | Nombre del embarcador (NombreEmbarcador). (hasta 254 caracteres) | | `carta_porte.air.rate_charge` | number | | Cargo por tasación (CargoPorTasacion). (de 0 a 1000000000000000) | | `carta_porte.rail` | objeto | | Transporte ferroviario (TransporteFerroviario): servicio, tráfico, derechos de paso y carros. | | `carta_porte.rail.service` | string | sí | Tipo de servicio (TipoDeServicio), c_TipoDeServicio. (hasta 4 caracteres) | | `carta_porte.rail.traffic` | string | sí | Tipo de tráfico (TipoDeTrafico), c_TipoDeTrafico. (hasta 4 caracteres) | | `carta_porte.rail.insurer` | string | | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.rail.policy` | string | | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.rail.rights` | arreglo de objeto | | Derechos de paso (DerechosDePaso): tipo y kilómetros. (hasta 50) | | `carta_porte.rail.rights[].type` | string | sí | Tipo de derecho de paso (TipoDerechoDePaso), c_DerechosDePaso. (hasta 6 caracteres) | | `carta_porte.rail.rights[].km` | number | sí | Kilometraje pagado (KilometrajePagado). (de 0 a 100000) | | `carta_porte.rail.cars` | arreglo de objeto | sí | Carros (Carro). (hasta 200) | | `carta_porte.rail.cars[].type` | string | sí | Tipo de carro (TipoCarro), c_TipoCarro. (hasta 4 caracteres) | | `carta_porte.rail.cars[].registration` | string | sí | Matrícula (Matricula). (hasta 15 caracteres) | | `carta_porte.rail.cars[].guide` | string | sí | Guía del carro (GuiaCarro). (hasta 15 caracteres) | | `carta_porte.rail.cars[].net_tons` | number | sí | Toneladas netas del carro (ToneladasNetasCarro). (de 0 a 1000000) | | `carta_porte.rail.cars[].containers` | arreglo de objeto | | Contenedores (Contenedor). (hasta 10) | | `carta_porte.rail.cars[].containers[].type` | string | sí | Tipo de contenedor (TipoContenedor), c_ContenedorMaritimo o c_Contenedor en ferroviario. (hasta 4 caracteres) | | `carta_porte.rail.cars[].containers[].empty_kg` | number | sí | Peso del contenedor vacío en kg (PesoContenedorVacio). (de 0 a 100000000) | | `carta_porte.rail.cars[].containers[].net_kg` | number | sí | Peso neto en kg (PesoNeto). (de 0 a 100000000) | | `carta_porte.drivers` | arreglo de objeto | sí | Operadores (FiguraTransporte tipo 01): RFC, licencia y nombre. (hasta 5) | | `carta_porte.drivers[].tax_id` | string o `""` | sí | RFC del operador (RFCFigura); vacío si es extranjero (con foreign). | | `carta_porte.drivers[].license` | string | sí | Número de licencia (NumLicencia). (hasta 16 caracteres) | | `carta_porte.drivers[].name` | string | sí | Nombre del operador (NombreFigura). (hasta 254 caracteres) | | `carta_porte.drivers[].foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.drivers[].foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.drivers[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.figures` | arreglo de objeto | | Otras figuras de transporte: 02 propietario, 03 arrendador, 04 notificado, 05 integrante de coordinados. (hasta 10) | | `carta_porte.figures[].type` | `02`, `03`, `04`, `05` | sí | Tipo de figura (TipoFigura): 02 propietario, 03 arrendador, 04 notificado, 05 integrante. | | `carta_porte.figures[].tax_id` | string | | RFC de la figura (RFCFigura). | | `carta_porte.figures[].foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.figures[].foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.figures[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.figures[].name` | string | sí | Nombre de la figura (NombreFigura). (hasta 254 caracteres) | | `carta_porte.figures[].parts` | arreglo de string | | Partes del transporte que posee o arrienda (ParteTransporte), c_ParteTransporte. (hasta 20) | | `carta_porte.figures[].address` | objeto | | Domicilio (Domicilio). En México basta el código postal: estado, municipio y localidad se toman de c_CodigoPostal. | | `carta_porte.figures[].address.street` | string | | Calle (Calle). (hasta 100 caracteres) | | `carta_porte.figures[].address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `carta_porte.figures[].address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `carta_porte.figures[].address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `carta_porte.figures[].address.country` | string | | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `carta_porte.figures[].address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `carta_porte.figures[].address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.figures[].address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.figures[].address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `ine` | objeto | | Complemento INE (procesos electorales). | | `ine.process` | `Ordinario`, `Precampaña`, `Campaña` | sí | Tipo de proceso (TipoProceso): Ordinario, Precampaña o Campaña. | | `ine.committee` | `Ejecutivo Nacional`, `Ejecutivo Estatal`, `Directivo Estatal` | | Tipo de comité (TipoComite), con el proceso Ordinario. | | `ine.accounting_id` | string | | Clave de contabilidad (IdContabilidad), hasta 6 dígitos. | | `ine.entities` | arreglo de objeto | | Entidades (Entidad): clave del estado, ámbito y claves de contabilidad. (hasta 64) | | `ine.entities[].key` | string | sí | Clave de la entidad (ClaveEntidad), 3 letras. (3 caracteres) | | `ine.entities[].scope` | `Local`, `Federal` | | Ámbito (Ambito): Local o Federal. | | `ine.entities[].accounting_ids` | arreglo de string | | Claves de contabilidad (IdContabilidad). (hasta 50) | | `leyendas` | arreglo de objeto | | Complemento de leyendas fiscales. (hasta 10) | | `leyendas[].text` | string | sí | Texto de la leyenda (textoLeyenda). (hasta 1000 caracteres) | | `leyendas[].disposition` | string | | Disposición fiscal (disposicionFiscal), por ejemplo LISR. (hasta 30 caracteres) | | `leyendas[].norm` | string | | Norma o artículo (norma). (hasta 100 caracteres) | | `donation` | objeto | | Complemento de donatarias. | | `donation.in_kind` | boolean | | true si el donativo es en especie (va sin forma de pago en efectivo). | | `donation.deducted` | boolean | | true si el donativo es deducible para quien lo da. | | `local_taxes` | arreglo de objeto | | Impuestos locales (complemento implocal): traslados y retenciones estatales. (hasta 20) | | `local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de 0 a 100) | | `local_taxes[].amount` | number | sí | Importe del impuesto local. (de 0 a 1000000000000) | | `export` | `01`, `02`, `03`, `04` | | Exportación (Exportacion), c_Exportacion: 01 no aplica (por omisión), 02 definitiva… | | `comercio_exterior` | objeto | | Complemento de comercio exterior 2.0. | | `comercio_exterior.motive` | `"05"` | | Motivo de traslado (MotivoTraslado): 05 envío de mercancías propias. | | `comercio_exterior.owners` | arreglo de objeto | | Propietarios de la mercancía si no es el emisor (Propietario), con motive 05. (hasta 10) | | `comercio_exterior.owners[].tax_id` | string | sí | Número de registro tributario del propietario (NumRegIdTrib). (hasta 40 caracteres) | | `comercio_exterior.owners[].country` | string | sí | País de residencia del propietario (ResidenciaFiscal), c_Pais. (3 caracteres) | | `comercio_exterior.incoterm` | string | | Incoterm (Incoterm), c_INCOTERM: EXW, FOB, CIF, DAP, DDP… (3 caracteres) | | `comercio_exterior.certificate` | string | | Número de certificado de origen (NumCertificadoOrigen). (hasta 40 caracteres) | | `comercio_exterior.usd_rate` | number | sí | Tipo de cambio a dólares (TipoCambioUSD). (de -∞ a 1000000) | | `comercio_exterior.issuer_curp` | string | | CURP del emisor persona física (Emisor Curp). (18 caracteres) | | `comercio_exterior.issuer_address` | objeto | | Domicilio del emisor (Emisor Domicilio): calle y números; lo demás sale de su código postal. | | `comercio_exterior.issuer_address.street` | string | sí | Calle (Calle). (hasta 100 caracteres) | | `comercio_exterior.issuer_address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `comercio_exterior.issuer_address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `comercio_exterior.issuer_address.zip` | string | | Código postal del emisor (CodigoPostal). | | `comercio_exterior.receiver_address` | objeto | | Domicilio del receptor en el extranjero (Receptor Domicilio). | | `comercio_exterior.receiver_address.street` | string | sí | Calle (Calle). (hasta 100 caracteres) | | `comercio_exterior.receiver_address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `comercio_exterior.receiver_address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `comercio_exterior.receiver_address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.receiver_address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.receiver_address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.receiver_address.state` | string | | Estado (Estado), c_Estado para USA y CAN. (hasta 30 caracteres) | | `comercio_exterior.receiver_address.country` | string | sí | País (Pais), c_Pais. (3 caracteres) | | `comercio_exterior.receiver_address.zip` | string | sí | Código postal en su país (CodigoPostal). (hasta 12 caracteres) | | `comercio_exterior.recipient` | objeto | | Destinatario, si no es el receptor (Destinatario). | | `comercio_exterior.recipient.name` | string | | Nombre del destinatario (Nombre). (hasta 300 caracteres) | | `comercio_exterior.recipient.tax_id` | string | | Número de registro tributario del destinatario (NumRegIdTrib). (hasta 40 caracteres) | | `comercio_exterior.recipient.address` | objeto | sí | Domicilio del destinatario (Domicilio). | | `comercio_exterior.recipient.address.street` | string | sí | Calle (Calle). (hasta 100 caracteres) | | `comercio_exterior.recipient.address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `comercio_exterior.recipient.address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `comercio_exterior.recipient.address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.recipient.address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.recipient.address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.recipient.address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `comercio_exterior.recipient.address.country` | string | sí | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `comercio_exterior.recipient.address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `comercio_exterior.goods` | arreglo de objeto | sí | Mercancías (Mercancia), una por concepto exportado. (hasta 200) | | `comercio_exterior.goods[].id` | string | | Número de identificación de la mercancía (NoIdentificacion): el sku de su concepto. (hasta 100 caracteres) | | `comercio_exterior.goods[].tariff` | string | | Fracción arancelaria (FraccionArancelaria), 10 dígitos. Transporte internacional. | | `comercio_exterior.goods[].unit` | string | sí | Unidad aduana (UnidadAduana), c_UnidadAduana. | | `comercio_exterior.goods[].quantity` | number | | Cantidad en la unidad aduana (CantidadAduana). (de -∞ a 100000000000000) | | `comercio_exterior.goods[].unit_value_usd` | number | | Valor unitario en dólares (ValorUnitarioAduana). (de 0 a 10000000000000000) | | `comercio_exterior.notes` | string | | Observaciones del complemento (Observaciones). (hasta 300 caracteres) | | `global` | objeto | | Factura global a público en general (InformacionGlobal): periodicidad, meses y año. | | `global.periodicity` | string | sí | Periodicidad (Periodicidad), c_Periodicidad: 01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral. | | `global.months` | string | sí | Meses (Meses), c_Meses: 01 a 12, o 13 a 18 bimestres. | | `global.year` | integer | sí | Año (Año). (de 2022 a 2100) | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `paid_later` | boolean | | Con PUE: true si aún no le pagan («Por cobrar» en la app) aunque se pagará este mes. | | `currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `pdf_language` | `es`, `en`, `bi` | | Idioma del PDF: es (por omisión) o en. | | `addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `related` | objeto | | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `related.relationship` | string | sí | Tipo de relación (TipoRelacion): 01 nota de crédito, 03 devolución, 04 sustitución, 07 anticipo… | | `related.uuids` | arreglo de string | sí | Folios fiscales relacionados. (hasta 20) | | `exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `folio` | integer | | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. (de 1 a 2000000000) | | `continue_sequence` | boolean | | Con un folio elegido: true para que los siguientes sigan desde ese número. | | `date` | string | | Fecha de emisión (Fecha) si no es ahora, AAAA-MM-DDTHH:MM:SS hora de la Ciudad de México: hasta 72 horas antes. | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. (por omisión false) | | `email_cc` | string o null | | Correos con copia, separados por comas. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "method": "egreso", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "payment_form": "03", "items": [ { "description": "Desarrollo de sitio web", "product_key": "81112100", "unit_price": 10000 } ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.advances.apply("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { method: "egreso", customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, payment_form: "03", items: [ { description: "Desarrollo de sitio web", product_key: "81112100", unit_price: 10000, }, ], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar una constancia de retenciones `POST https://api.fiscalisto.com/v1/retentions` Timbra una constancia de retenciones e información de pagos (Retenciones 2.0) con los folios RET1, RET2… del emisor. Una vez por Idempotency-Key; con send_email y email la manda al receptor al timbrarla. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `code` | string | sí | Clave de la retención (CveRetenc), c_CveRetenc: 01 servicios profesionales, 05 arrendamiento, 14 dividendos, 16 intereses, 18 pagos al extranjero… Las claves 14, 16, 17 y 18 a 24 llevan su complemento (dividends, interest, trust_lease, foreign_payment, share_sale, prize, trust, retirement_plan, mortgage_interest, financial_derivatives); la 26 (plataformas tecnológicas) aún no se puede emitir. | | `description` | string o null | | Descripción de la retención (DescRetenc); obligatoria con la clave 25 (otro tipo). | | `recipient` | objeto | sí | Receptor de la constancia (Receptor): nacional con RFC y código postal, o extranjero. | | `recipient.foreign` | boolean | | true si el receptor es extranjero (sin RFC). | | `recipient.tax_id` | string o null | | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `recipient.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `recipient.zip` | string | | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `recipient.curp` | string o null | | CURP, 18 caracteres. | | `period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `period.year` | integer | sí | Año (Ejercicio). (de 2004 a 2100) | | `period.month` | integer | | Un solo mes, del 1 al 12 (MesIni y MesFin iguales). (de 1 a 12) | | `period.from_month` | integer | | Mes inicial (MesIni), 1 a 12. (de 1 a 12) | | `period.to_month` | integer | | Mes final (MesFin), 1 a 12, del mismo año. (de 1 a 12) | | `totals` | objeto | sí | Totales (Totales). | | `totals.operation` | number | sí | Monto total de la operación (MontoTotOperacion). (de 0 a ∞) | | `totals.taxed` | number | | Monto gravado (MontoTotGrav). Por omisión, operation menos exempt. (de 0 a ∞) | | `totals.exempt` | number | | Monto exento (MontoTotExent). Por omisión 0. (de 0 a ∞) | | `taxes` | arreglo de objeto | sí | Impuestos del comprobante, por tipo. (hasta 50) | | `taxes[].tax` | string | sí | Impuesto: isr, iva o ieps (ImpuestoRet 001, 002, 003). | | `taxes[].base` | number | sí | Base del impuesto retenido (BaseRet). (de 0 a ∞) | | `taxes[].amount` | number | sí | Monto retenido (MontoRet). (de 0 a ∞) | | `taxes[].payment_type` | string | | Tipo de pago de la retención (TipoPagoRet): 01 definitivo IVA, 02 definitivo IEPS, 03 definitivo ISR, 04 provisional ISR. Por omisión el usual de cada impuesto. | | `dividends` | objeto | | Complemento de dividendos. | | `dividends.type` | `01`, `02`, `03`, `04`, `05`, `06` | sí | Tipo de dividendo o utilidad distribuida (CveTipDivOUtil), c_TipoDividendoOUtilidadDistribuida. | | `dividends.isr_mexico` | number | sí | ISR acreditable en México (MontISRAcredRetMexico). (de 0 a ∞) | | `dividends.isr_foreign` | number | sí | ISR acreditable en el extranjero (MontISRAcredRetExtranjero). (de 0 a ∞) | | `dividends.foreign_retention` | number o null | | Retención en el extranjero sobre dividendos (MontRetExtDivExt). | | `dividends.company` | `nacional`, `extranjera` | sí | Tipo de sociedad que distribuye (TipoSocDistrDiv): nacional o extranjera. | | `dividends.national_credit` | number o null | | ISR acreditable nacional (MontISRAcredNal). | | `dividends.accumulated_national` | number o null | | Dividendo acumulable nacional (MontDivAcumNal). | | `dividends.accumulated_foreign` | number o null | | Dividendo acumulable extranjero (MontDivAcumExt). | | `interest` | objeto | | Complemento de intereses. | | `interest.financial_system` | boolean | sí | true si los intereses vienen del sistema financiero (SistFinanciero). | | `interest.retirement` | boolean | sí | true si se retiraron los intereses en el periodo (RetiroAORESRetInt). | | `interest.derivatives` | boolean | sí | true si son operaciones financieras derivadas (OperFinancDerivad). | | `interest.nominal` | number | sí | Interés nominal (MontIntNominal). | | `interest.real` | number | sí | Interés real (MontIntReal). | | `interest.loss` | number | sí | Pérdida (Perdida). | | `foreign_payment` | objeto | | Complemento de pagos a extranjeros. | | `foreign_payment.beneficiary` | boolean | sí | true si quien recibe es el beneficiario efectivo (EsBenefEfectDelCobro). | | `foreign_payment.country` | string o null | | País de residencia del extranjero (PaisDeResidParaEfecFisc), 2 letras. | | `foreign_payment.concept` | `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9` | sí | Concepto del pago (ConceptoPago), c_TipoContribuyenteSujetoRetencion. | | `foreign_payment.description` | string | sí | Descripción del concepto (DescripcionConcepto). (hasta 255 caracteres) | | `foreign_payment.beneficiary_tax_id` | string o null | | RFC del beneficiario, si no es quien recibe (RFC). | | `foreign_payment.beneficiary_curp` | string o null | | CURP del beneficiario (CURP). | | `foreign_payment.beneficiary_name` | string o null | | Nombre o razón social del beneficiario (NomDenRazSocB). | | `trust_lease` | objeto | | Complemento de arrendamiento en fideicomiso (Arrendamientoenfideicomiso 1.0), obligatorio con code 17. | | `trust_lease.trustee_payment` | number | sí | Pago provisional que el fiduciario efectuó al arrendador en el periodo (PagProvEfecPorFiduc). (de 0 a ∞) | | `trust_lease.income` | number | sí | Rendimientos del periodo por el arrendamiento de los bienes (RendimFideicom). (de 0 a ∞) | | `trust_lease.deductions` | number | sí | Deducciones del arrendamiento en el periodo (DeduccCorresp). (de 0 a ∞) | | `trust_lease.withheld_total` | number o null | | Monto total retenido del arrendamiento en el periodo (MontTotRet). | | `trust_lease.fibra_result` | number o null | | Resultado fiscal distribuido por FIBRAS (MontResFiscDistFibras). | | `trust_lease.other_amount` | number o null | | Monto de otros conceptos distribuidos (MontOtrosConceptDistr). | | `trust_lease.other_description` | string o null | | Qué son los otros conceptos distribuidos (DescrMontOtrosConceptDistr); obligatoria con other_amount. | | `share_sale` | objeto | | Complemento de enajenación de acciones u operaciones de valores (EnajenaciondeAcciones 1.0), obligatorio con code 19. | | `share_sale.contract` | string | sí | Descripción del contrato de intermediación (ContratoIntermediacion). (hasta 300 caracteres) | | `share_sale.gain` | number | sí | Ganancia obtenida (Ganancia). (de 0 a ∞) | | `share_sale.loss` | number | sí | Pérdida (Perdida). (de 0 a ∞) | | `prize` | objeto | | Complemento de premios (Premios 1.0), obligatorio con code 20. Sus montos son los de totals. | | `prize.state` | string | sí | Entidad federativa donde se paga el premio (EntidadFederativa), c_EntidadesFederativas de catRetenciones: 01 Aguascalientes … 09 Ciudad de México … 32 Zacatecas. | | `trust` | objeto | | Complemento de fideicomisos que no realizan actividades empresariales (Fideicomisonoempresarial 1.0), obligatorio con code 21. | | `trust.income` | objeto | sí | Ingresos o entradas del fideicomiso en el periodo (IngresosOEntradas) y la parte del receptor. | | `trust.income.concept` | string | sí | Concepto de los ingresos (IntegracIngresos/Concepto), hasta 100 caracteres. (hasta 100 caracteres) | | `trust.income.total` | number | sí | Ingresos totales del fideicomiso en el periodo (MontTotEntradasPeriodo). (de 0 a ∞) | | `trust.income.share` | number | sí | Parte proporcional de los ingresos acumulables del receptor (PartPropAcumDelFideicom). (de 0 a ∞) | | `trust.income.proportion` | number | sí | Proporción de participación del receptor según el contrato (PropDelMontTot). (de 0 a 999.999999) | | `trust.deductions` | objeto | sí | Deducciones o salidas del fideicomiso en el periodo (DeduccOSalidas) y la parte del receptor. | | `trust.deductions.concept` | string | sí | Concepto de los egresos (IntegracEgresos/ConceptoS), hasta 100 caracteres. (hasta 100 caracteres) | | `trust.deductions.total` | number | sí | Egresos totales del fideicomiso en el periodo (MontTotEgresPeriodo). (de 0 a ∞) | | `trust.deductions.share` | number | sí | Parte proporcional de las deducciones autorizadas del receptor (PartPropDelFideicom). (de 0 a ∞) | | `trust.deductions.proportion` | number | sí | Proporción de participación del receptor según el contrato (PropDelMontTot). (de 0 a 999.999999) | | `trust.withholding` | objeto | sí | Retención efectuada al fideicomiso (RetEfectFideicomiso). | | `trust.withholding.amount` | number | sí | Monto de la retención (MontRetRelPagFideic). (de 0 a ∞) | | `trust.withholding.description` | string | sí | Descripción de la retención (DescRetRelPagFideic), hasta 100 caracteres. (hasta 100 caracteres) | | `retirement_plan` | objeto | | Complemento de planes personales de retiro (Planesderetiro 1.1), obligatorio con code 22: lo del año anterior. | | `retirement_plan.financial_system` | boolean | sí | true si el plan es del sistema financiero (SistemaFinanc). | | `retirement_plan.contributions_last_year` | number o null | | Aportaciones actualizadas del año anterior (MontTotAportAnioInmAnterior). | | `retirement_plan.real_interest` | number | sí | Intereses reales devengados o percibidos el año anterior (MontIntRealesDevengAniooInmAnt). (de 0 a ∞) | | `retirement_plan.early_withdrawals` | boolean | sí | true si hubo retiros antes de cumplir los requisitos de permanencia (HuboRetirosAnioInmAntPer). | | `retirement_plan.early_withdrawn` | number o null | | Monto retirado antes de cumplir la permanencia (MontTotRetiradoAnioInmAntPer); obligatorio con early_withdrawals. | | `retirement_plan.exempt_withdrawn` | number o null | | Monto exento de lo retirado el año anterior (MontTotExentRetiradoAnioInmAnt). | | `retirement_plan.excess_withdrawn` | number o null | | Excedente del monto exento de lo retirado el año anterior (MontTotExedenteAnioInmAnt). | | `retirement_plan.withdrawals` | boolean | sí | true si hubo retiros el año anterior (HuboRetirosAnioInmAnt). | | `retirement_plan.withdrawn` | number o null | | Monto retirado el año anterior (MontTotRetiradoAnioInmAnt); obligatorio con withdrawals. | | `retirement_plan.reference` | string o null | | Número de referencia, contrato o cuenta del cliente (NumReferencia). | | `retirement_plan.contributions` | arreglo de objeto | | Aportaciones o depósitos (AportacionesODepositos), uno por tipo y hasta tres. (hasta 3) | | `retirement_plan.contributions[].type` | `a`, `b`, `c` | sí | Tipo de aportación (TipoAportacionODeposito): a (art. 151 fracc. V LISR), b (art. 185 LISR) o c (art. 258 RLISR, con trustee_rfc). | | `retirement_plan.contributions[].amount` | number | sí | Monto de la aportación o depósito (MontAportODep). (de 0 a ∞) | | `retirement_plan.contributions[].trustee_rfc` | string o null | | RFC de la fiduciaria que administra el plan (RFCFiduciaria); obligatorio con el tipo c. | | `mortgage_interest` | objeto | | Complemento de intereses reales deducibles por créditos hipotecarios (Intereseshipotecarios 1.0), obligatorio con code 23. | | `mortgage_interest.financial_institution` | boolean | sí | true si el crédito lo otorgó una institución financiera (CreditoDeInstFinanc). | | `mortgage_interest.outstanding_balance` | number | sí | Saldo insoluto al 31 de diciembre del año anterior, o a la fecha del contrato si es de este año (SaldoInsoluto). (de 0 a ∞) | | `mortgage_interest.deductible_proportion` | number o null | | Proporción deducible del crédito sobre los intereses reales devengados y pagados (PropDeducDelCredit). | | `mortgage_interest.nominal_accrued` | number o null | | Intereses nominales devengados (MontTotIntNominalesDev). | | `mortgage_interest.nominal_accrued_paid` | number o null | | Intereses nominales devengados y pagados (MontTotIntNominalesDevYPag). | | `mortgage_interest.real_deductible` | number o null | | Intereses reales pagados deducibles (MontTotIntRealPagDeduc). | | `mortgage_interest.contract` | string o null | | Número de contrato del crédito hipotecario (NumContrato). | | `financial_derivatives` | objeto | | Complemento de operaciones financieras derivadas de capital (Operacionesconderivados 1.0), obligatorio con code 24. | | `financial_derivatives.gain` | number | sí | Ganancia acumulable (MontGanAcum). (de 0 a ∞) | | `financial_derivatives.loss` | number | sí | Pérdida deducible (MontPerdDed). (de 0 a ∞) | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `email` | string | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. (hasta 1000 caracteres) | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "code": "01", "recipient": { "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826" }, "period": { "year": 2026, "month": 9 }, "totals": { "operation": 10000 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000 }, { "tax": "iva", "base": 10000, "amount": 1066.67 } ], "metadata": { "proveedor": "42" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.create({ code: "01", recipient: { tax_id: "PEPJ800101AB1", legal_name: "JUAN PEREZ PEREZ", zip: "33826", }, period: { year: 2026, month: 9 }, totals: { operation: 10000 }, taxes: [ { tax: "isr", base: 10000, amount: 1000 }, { tax: "iva", base: 10000, amount: 1066.67 }, ], metadata: { proveedor: "42" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Retention`. Sus campos están en «Objetos» al final de este archivo (Retention). ```json { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Retention`. Sus campos están en «Objetos» al final de este archivo (Retention). ```json { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar constancias `GET https://api.fiscalisto.com/v1/retentions` Las constancias del emisor, las más recientes primero. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `status` | consulta | | valid, canceled, rejected o processing. | | `recipient` | consulta | | RFC del receptor. | | `year` | consulta | | Ejercicio, 4 dígitos. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.retentions.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `RetentionList`. Sus campos están en «Objetos» al final de este archivo (RetentionList). ```json { "object": "list", "data": [ { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ], "has_more": false, "url": "/v1/retentions" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una constancia `GET https://api.fiscalisto.com/v1/retentions/{id}` Una constancia de retenciones con su estado, sus impuestos retenidos y, si se mandó, a quién. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `issuer` | consulta | | Con un UUID en vez del id, el emisor (iss_…) si la cuenta tiene varios. | | `refresh` | consulta | | true para preguntar su estado al SAT ahora (cancelaciones en espera). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.get("ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv"); ``` ## Respuesta 200 Correcto. Un objeto `Retention`. Sus campos están en «Objetos» al final de este archivo (Retention). ```json { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el PDF `GET https://api.fiscalisto.com/v1/retentions/{id}/pdf` La representación impresa de la constancia. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `issuer` | consulta | | Con un UUID en vez del id, el emisor (iss_…) si la cuenta tiene varios. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("constancia.pdf", await fiscalisto.retentions.pdf("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el XML `GET https://api.fiscalisto.com/v1/retentions/{id}/xml` El XML timbrado de la constancia, tal como lo tiene el SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `issuer` | consulta | | Con un UUID en vez del id, el emisor (iss_…) si la cuenta tiene varios. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("constancia.xml", await fiscalisto.retentions.xml("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Enviar por correo `POST https://api.fiscalisto.com/v1/retentions/{id}/send` Manda el PDF y el XML de la constancia a los correos de to (o al que se dio al timbrarla). Dos envíos a la vez de la misma constancia: el segundo contesta email_failed. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `to` | string | | A quién (correos separados por comas). (hasta 1000 caracteres) | Ejemplo: ```json { "to": "juan@example.com" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.send("ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", { to: "juan@example.com" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Retention`. Sus campos están en «Objetos» al final de este archivo (Retention). ```json { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cancelar una constancia `POST https://api.fiscalisto.com/v1/retentions/{id}/cancel` Se cancela en el momento (no espera aceptación). No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `motive` | `01`, `02`, `03`, `04` | | Motivo de cancelación (c_MotivoCancelacion): 01 con errores con relación (lleva substitution), 02 con errores sin relación (por omisión), 03 no se llevó a cabo, 04 solo en una factura global, cuando una venta incluida se facturó a nombre del cliente. (por omisión "02") | | `substitution` | string | | El comprobante que sustituye a este (su id o su UUID), con el motivo 01. | Ejemplo: ```json { "motive": "02" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const retention = await fiscalisto.retentions.cancel("ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", { motive: "02" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Retention`. Sus campos están en «Objetos» al final de este archivo (Retention). ```json { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ``` ## Errores Puede responder 400, 401, 403, 404, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un empleado `POST https://api.fiscalisto.com/v1/employees` Un empleado del emisor para sus recibos de nómina: RFC, CURP, NSS, salario diario, salario diario integrado y periodicidad. Lo que no mande tiene el valor usual (contrato indefinido, jornada diurna, quincenal, riesgo clase I). warnings dice lo que el SAT podría rechazar. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `number` | string | sí | Número de empleado (NumEmpleado). (hasta 15 caracteres) | | `name` | string | sí | Nombre completo del empleado, como en su constancia de situación fiscal. (hasta 300 caracteres) | | `tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `curp` | string | sí | CURP, 18 caracteres. | | `social_security_number` | string | sí | Número de seguridad social (NumSeguridadSocial), 11 dígitos. | | `zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `email` | string o null | | Correo del empleado, a donde van sus recibos con send_email. | | `start_date` | string | sí | Fecha de inicio de la relación laboral (FechaInicioRelLaboral), AAAA-MM-DD. | | `end_date` | string o null | | Último día trabajado si ya no trabaja ahí, AAAA-MM-DD. | | `daily_salary` | number | sí | Salario diario. | | `integrated_daily_salary` | number | sí | Salario diario integrado (SalarioDiarioIntegrado): no menor al salario diario. | | `contract_type` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` | | Tipo de contrato (TipoContrato), c_TipoContrato. Por omisión 01, por tiempo indeterminado. | | `workday_type` | string | | Tipo de jornada (TipoJornada), c_TipoJornada. Por omisión 01, diurna. | | `periodicity` | string | | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `risk_class` | string | | Clase de riesgo del puesto ante el IMSS (RiesgoPuesto), 1 a 5. Por omisión 1. | | `state` | string | sí | Entidad federativa donde trabaja (ClaveEntFed): JAL, CMX, NLE… | | `border_zone` | boolean | | true si trabaja en la zona libre de la frontera norte (salario mínimo de frontera). | | `unionized` | boolean | | true si es sindicalizado (Sindicalizado). | | `department` | string o null | | Departamento (Departamento). | | `position` | string o null | | Puesto (Puesto). | Ejemplo: ```json { "number": "9", "name": "ANA GÓMEZ RUIZ", "tax_id": "GORA800101JK2", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "start_date": "2024-03-04", "daily_salary": 320, "integrated_daily_salary": 334.66, "periodicity": "02", "state": "CMX" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const employee = await fiscalisto.employees.create({ number: "9", name: "ANA GÓMEZ RUIZ", tax_id: "GORA800101JK2", curp: "GORA800101MDFMZN09", social_security_number: "12345678903", zip: "06000", start_date: "2024-03-04", daily_salary: 320, integrated_daily_salary: 334.66, periodicity: "02", state: "CMX", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Employee`. Sus campos están en «Objetos» al final de este archivo (Employee). ```json { "id": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "object": "employee", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "9", "name": "ANA GÓMEZ RUIZ", "tax_id": "GORA800101JK2", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "email": null, "start_date": "2024-03-04", "end_date": null, "daily_salary": 320, "integrated_daily_salary": 334.66, "contract_type": "01", "workday_type": "01", "regime_type": "02", "periodicity": "02", "risk_class": "1", "state": "CMX", "border_zone": false, "unionized": false, "department": null, "position": null, "warnings": [], "created_at": "2026-10-20T09:02:11-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar empleados `GET https://api.fiscalisto.com/v1/employees` Los empleados del emisor, los más recientes primero. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.employees.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `EmployeeList`. Sus campos están en «Objetos» al final de este archivo (EmployeeList). ```json { "object": "list", "data": [ { "id": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "object": "employee", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "9", "name": "ANA GÓMEZ RUIZ", "tax_id": "GORA800101JK2", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "email": null, "start_date": "2024-03-04", "end_date": null, "daily_salary": 320, "integrated_daily_salary": 334.66, "contract_type": "01", "workday_type": "01", "regime_type": "02", "periodicity": "02", "risk_class": "1", "state": "CMX", "border_zone": false, "unionized": false, "department": null, "position": null, "warnings": [], "created_at": "2026-10-20T09:02:11-06:00" } ], "has_more": false, "url": "/v1/employees" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un empleado `GET https://api.fiscalisto.com/v1/employees/{id}` Un empleado con sus datos y lo que el SAT podría rechazar en warnings. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const employee = await fiscalisto.employees.get("emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV"); ``` ## Respuesta 200 Correcto. Un objeto `Employee`. Sus campos están en «Objetos» al final de este archivo (Employee). ```json { "id": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "object": "employee", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "9", "name": "ANA GÓMEZ RUIZ", "tax_id": "GORA800101JK2", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "email": null, "start_date": "2024-03-04", "end_date": null, "daily_salary": 320, "integrated_daily_salary": 334.66, "contract_type": "01", "workday_type": "01", "regime_type": "02", "periodicity": "02", "risk_class": "1", "state": "CMX", "border_zone": false, "unionized": false, "department": null, "position": null, "warnings": [], "created_at": "2026-10-20T09:02:11-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Actualizar un empleado `POST https://api.fiscalisto.com/v1/employees/{id}` Cambia solo los campos que manda; los recibos ya timbrados no cambian. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `number` | string | | Número de empleado (NumEmpleado). (hasta 15 caracteres) | | `name` | string | | Nombre completo del empleado, como en su constancia de situación fiscal. (hasta 300 caracteres) | | `tax_id` | string | | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `curp` | string | | CURP, 18 caracteres. | | `social_security_number` | string | | Número de seguridad social (NumSeguridadSocial), 11 dígitos. | | `zip` | string | | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `email` | string o null | | Correo del empleado, a donde van sus recibos con send_email. | | `start_date` | string | | Fecha de inicio de la relación laboral (FechaInicioRelLaboral), AAAA-MM-DD. | | `end_date` | string o null | | Último día trabajado si ya no trabaja ahí, AAAA-MM-DD. | | `daily_salary` | number | | Salario diario. | | `integrated_daily_salary` | number | | Salario diario integrado (SalarioDiarioIntegrado): no menor al salario diario. | | `contract_type` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` | | Tipo de contrato (TipoContrato), c_TipoContrato. Por omisión 01, por tiempo indeterminado. | | `workday_type` | string | | Tipo de jornada (TipoJornada), c_TipoJornada. Por omisión 01, diurna. | | `periodicity` | string | | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `risk_class` | string | | Clase de riesgo del puesto ante el IMSS (RiesgoPuesto), 1 a 5. Por omisión 1. | | `state` | string | | Entidad federativa donde trabaja (ClaveEntFed): JAL, CMX, NLE… | | `border_zone` | boolean | | true si trabaja en la zona libre de la frontera norte (salario mínimo de frontera). | | `unionized` | boolean | | true si es sindicalizado (Sindicalizado). | | `department` | string o null | | Departamento (Departamento). | | `position` | string o null | | Puesto (Puesto). | Ejemplo: ```json { "daily_salary": 350, "integrated_daily_salary": 366 } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const employee = await fiscalisto.employees.update("emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", { daily_salary: 350, integrated_daily_salary: 366, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Employee`. Sus campos están en «Objetos» al final de este archivo (Employee). ```json { "id": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "object": "employee", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "9", "name": "ANA GÓMEZ RUIZ", "tax_id": "GORA800101JK2", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "email": null, "start_date": "2024-03-04", "end_date": null, "daily_salary": 320, "integrated_daily_salary": 334.66, "contract_type": "01", "workday_type": "01", "regime_type": "02", "periodicity": "02", "risk_class": "1", "state": "CMX", "border_zone": false, "unionized": false, "department": null, "position": null, "warnings": [], "created_at": "2026-10-20T09:02:11-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar un empleado `DELETE https://api.fiscalisto.com/v1/employees/{id}` Sus recibos timbrados se quedan. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.employees.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar un recibo de nómina `POST https://api.fiscalisto.com/v1/payroll_receipts` Nómina ordinaria (Nómina 1.2): Fiscalisto calcula sueldo, ISR con subsidio al empleo e IMSS del empleado y el periodo, como la app. Queda como factura tipo N (GET /v1/invoices/{id}). El emisor necesita employer_registration. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `employee` | string | sí | El empleado (emp_…). | | `period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `period.start` | string | sí | Primer día del periodo (FechaInicialPago), AAAA-MM-DD. | | `period.end` | string | sí | Último día del periodo (FechaFinalPago), AAAA-MM-DD. | | `period.payment_date` | string | | Fecha de pago (FechaPago), AAAA-MM-DD. Por omisión el último día del periodo. | | `days` | number | | Días pagados (NumDiasPagados). Por omisión, todos los del periodo que le tocan. (de -∞ a 31) | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "employee": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "period": { "start": "2026-10-10", "end": "2026-10-16" }, "send_email": true, "metadata": { "corrida": "2026-42" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.payrollReceipts.create({ employee: "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", period: { start: "2026-10-10", end: "2026-10-16" }, send_email: true, metadata: { corrida: "2026-42" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Calcular un recibo sin timbrar `POST https://api.fiscalisto.com/v1/payroll_receipts/preview` El recibo como se timbraría (percepciones, ISR con el subsidio para el empleo, cuotas IMSS y el neto), sin timbrar ni usar timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `employee` | string | sí | El empleado (emp_…). | | `period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `period.start` | string | sí | Primer día del periodo (FechaInicialPago), AAAA-MM-DD. | | `period.end` | string | sí | Último día del periodo (FechaFinalPago), AAAA-MM-DD. | | `period.payment_date` | string | | Fecha de pago (FechaPago), AAAA-MM-DD. Por omisión el último día del periodo. | | `days` | number | | Días pagados (NumDiasPagados). Por omisión, todos los del periodo que le tocan. (de -∞ a 31) | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "employee": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "period": { "start": "2026-10-10", "end": "2026-10-16" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const payrollReceiptPreview = await fiscalisto.payrollReceipts.preview({ employee: "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", period: { start: "2026-10-10", end: "2026-10-16" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `PayrollReceiptPreview`. Sus campos están en «Objetos» al final de este archivo (PayrollReceiptPreview). ```json { "object": "payroll_receipt_preview", "employee": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "periodicity": "02", "period": { "start": "2026-10-10", "end": "2026-10-16", "payment_date": "2026-10-16" }, "days": 7, "contribution_base_salary": 334.66, "perceptions": [ { "type": "001", "key": "001", "concept": "Sueldo", "taxed": 2240, "exempt": 0 } ], "deductions": [ { "type": "001", "key": "001", "concept": "Cuotas IMSS", "amount": 55.63 }, { "type": "002", "key": "002", "concept": "ISR", "amount": 37.73 } ], "other_payments": [ { "type": "002", "key": "002", "concept": "Subsidio para el empleo del Decreto que otorga el subsidio para el empleo (DOF 1 de mayo de 2024)", "amount": 0, "subsidy_caused": 123.34, "subsidy_applied": 123.34 } ], "isr": { "tariff": 161.07, "subsidy": 123.34, "withheld": 37.73, "exempt_minimum": false }, "totals": { "salaries": 2240, "taxed": 2240, "exempt": 0, "perceptions": 2240, "other_deductions": 55.63, "taxes_withheld": 37.73, "deductions": 93.36, "other_payments": 0, "subtotal": 2240, "net": 2146.64 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar un lote de facturas `POST https://api.fiscalisto.com/v1/invoice_batches` Hasta 1,000 facturas de ingreso de un emisor, timbradas en segundo plano. Se revisan todas antes de empezar: si una no pasa, no se timbra ninguna. Responde 202; escuche invoice_batch.completed o consulte el lote. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `name` | string | | Nombre del lote, para usted. (hasta 120 caracteres) | | `invoices` | arreglo de objeto | sí | Las facturas, como el cuerpo de POST /v1/invoices, con reference opcional. (hasta 1000) | | `invoices[].issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `invoices[].branch` | string (brn_…) | | Sucursal (brn_…) desde donde se factura: su código postal va en LugarExpedicion, su dirección en el PDF y, si las tiene, sus series. Sin ella, el domicilio fiscal del emisor (la Matriz). | | `invoices[].warehouse` | string (whs_…) | | Almacén (whs_…) de donde salen sus productos con track_stock. Por omisión, el de su sucursal (o el de la Matriz). | | `invoices[].customer` | string (cus_…) o objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `invoices[].customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `invoices[].customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `invoices[].customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `invoices[].customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `invoices[].customer.email` | string o null | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `invoices[].customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `invoices[].customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `invoices[].customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `invoices[].customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `invoices[].items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. (hasta 200) Sus campos, los mismos que `items` en «Aplicar un anticipo». | | `invoices[].type` | `I`, `T` | | I ingreso (por omisión; con global, factura global) o T traslado. Notas de crédito: POST /v1/credit_notes. Pagos: POST /v1/payments. (por omisión "I") | | `invoices[].carta_porte` | objeto | | Complemento Carta Porte 3.1 (traslado de mercancías). Sus campos, los mismos que `carta_porte` en «Aplicar un anticipo». | | `invoices[].ine` | objeto | | Complemento INE (procesos electorales). | | `invoices[].ine.process` | `Ordinario`, `Precampaña`, `Campaña` | sí | Tipo de proceso (TipoProceso): Ordinario, Precampaña o Campaña. | | `invoices[].ine.committee` | `Ejecutivo Nacional`, `Ejecutivo Estatal`, `Directivo Estatal` | | Tipo de comité (TipoComite), con el proceso Ordinario. | | `invoices[].ine.accounting_id` | string | | Clave de contabilidad (IdContabilidad), hasta 6 dígitos. | | `invoices[].ine.entities` | arreglo de objeto | | Entidades (Entidad): clave del estado, ámbito y claves de contabilidad. (hasta 64) | | `invoices[].ine.entities[].key` | string | sí | Clave de la entidad (ClaveEntidad), 3 letras. (3 caracteres) | | `invoices[].ine.entities[].scope` | `Local`, `Federal` | | Ámbito (Ambito): Local o Federal. | | `invoices[].ine.entities[].accounting_ids` | arreglo de string | | Claves de contabilidad (IdContabilidad). (hasta 50) | | `invoices[].leyendas` | arreglo de objeto | | Complemento de leyendas fiscales. (hasta 10) | | `invoices[].leyendas[].text` | string | sí | Texto de la leyenda (textoLeyenda). (hasta 1000 caracteres) | | `invoices[].leyendas[].disposition` | string | | Disposición fiscal (disposicionFiscal), por ejemplo LISR. (hasta 30 caracteres) | | `invoices[].leyendas[].norm` | string | | Norma o artículo (norma). (hasta 100 caracteres) | | `invoices[].donation` | objeto | | Complemento de donatarias. | | `invoices[].donation.in_kind` | boolean | | true si el donativo es en especie (va sin forma de pago en efectivo). | | `invoices[].donation.deducted` | boolean | | true si el donativo es deducible para quien lo da. | | `invoices[].local_taxes` | arreglo de objeto | | Impuestos locales (complemento implocal): traslados y retenciones estatales. (hasta 20) | | `invoices[].local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `invoices[].local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `invoices[].local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de 0 a 100) | | `invoices[].local_taxes[].amount` | number | sí | Importe del impuesto local. (de 0 a 1000000000000) | | `invoices[].export` | `01`, `02`, `03`, `04` | | Exportación (Exportacion), c_Exportacion: 01 no aplica (por omisión), 02 definitiva… | | `invoices[].comercio_exterior` | objeto | | Complemento de comercio exterior 2.0. Sus campos, los mismos que `comercio_exterior` en «Aplicar un anticipo». | | `invoices[].global` | objeto | | Factura global a público en general (InformacionGlobal): periodicidad, meses y año. | | `invoices[].global.periodicity` | string | sí | Periodicidad (Periodicidad), c_Periodicidad: 01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral. | | `invoices[].global.months` | string | sí | Meses (Meses), c_Meses: 01 a 12, o 13 a 18 bimestres. | | `invoices[].global.year` | integer | sí | Año (Año). (de 2022 a 2100) | | `invoices[].use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `invoices[].payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `invoices[].payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `invoices[].paid_later` | boolean | | Con PUE: true si aún no le pagan («Por cobrar» en la app) aunque se pagará este mes. | | `invoices[].currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `invoices[].series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `invoices[].notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `invoices[].pdf_language` | `es`, `en`, `bi` | | Idioma del PDF: es (por omisión) o en. | | `invoices[].addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `invoices[].related` | objeto | | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `invoices[].related.relationship` | string | sí | Tipo de relación (TipoRelacion): 01 nota de crédito, 03 devolución, 04 sustitución, 07 anticipo… | | `invoices[].related.uuids` | arreglo de string | sí | Folios fiscales relacionados. (hasta 20) | | `invoices[].exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `invoices[].folio` | integer | | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. (de 1 a 2000000000) | | `invoices[].continue_sequence` | boolean | | Con un folio elegido: true para que los siguientes sigan desde ese número. | | `invoices[].date` | string | | Fecha de emisión (Fecha) si no es ahora, AAAA-MM-DDTHH:MM:SS hora de la Ciudad de México: hasta 72 horas antes. | | `invoices[].send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. (por omisión false) | | `invoices[].email_cc` | string o null | | Correos con copia, separados por comas. | | `invoices[].metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `invoices[].reference` | string | | Su identificador de esta factura (su número de pedido), hasta 100 caracteres. (hasta 100 caracteres) | Ejemplo: ```json { "name": "Cuotas de octubre", "invoices": [ { "reference": "depto-101", "customer": "cus_OufpKqnrvx9p1", "payment_form": "03", "items": [ { "product": "prd_OufpKqnrvx9p1" } ] }, { "reference": "depto-102", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "payment_form": "03", "items": [ { "description": "Cuota de mantenimiento", "product_key": "80131500", "unit_price": 1500 } ] } ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoiceBatch = await fiscalisto.invoiceBatches.create({ name: "Cuotas de octubre", invoices: [ { reference: "depto-101", customer: "cus_OufpKqnrvx9p1", payment_form: "03", items: [{ product: "prd_OufpKqnrvx9p1" }], }, { reference: "depto-102", customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, payment_form: "03", items: [ { description: "Cuota de mantenimiento", product_key: "80131500", unit_price: 1500, }, ], }, ], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 202 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `InvoiceBatch`. Sus campos están en «Objetos» al final de este archivo (InvoiceBatch). ```json { "id": "bat_OufpKqnrvx9p4hMdcoapAo5aaGJEme7mXj", "object": "invoice_batch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Cuotas de octubre", "status": "completed", "total": 2, "stamped": 1, "failed": 1, "created_at": "2026-10-24T10:15:42-06:00", "completed_at": "2026-10-24T10:15:42-06:00", "processing": 0, "invoices": [ { "index": 0, "reference": "depto-101", "status": "valid", "invoice": "inv_OufpKqnrvx9p0mn7VjQ6JlfipKAkHWstOK", "uuid": "DB494B07-D6DE-4F70-8ED0-C8B864FF8717", "total": 1740, "error": null }, { "index": 1, "reference": "depto-102", "status": "failed", "invoice": null, "uuid": null, "total": 1740, "error": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital." } ] } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar lotes `GET https://api.fiscalisto.com/v1/invoice_batches` Los lotes de facturas, los más recientes primero, con cuántas se timbraron y cuántas fallaron. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.invoiceBatches.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `InvoiceBatchList`. Sus campos están en «Objetos» al final de este archivo (InvoiceBatchList). ```json { "object": "list", "data": [ { "id": "bat_OufpKqnrvx9p4hMdcoapAo5aaGJEme7mXj", "object": "invoice_batch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Cuotas de octubre", "status": "completed", "total": 2, "stamped": 1, "failed": 1, "created_at": "2026-10-24T10:15:42-06:00", "completed_at": "2026-10-24T10:15:42-06:00", "processing": 0, "invoices": [ { "index": 0, "reference": "depto-101", "status": "valid", "invoice": "inv_OufpKqnrvx9p0mn7VjQ6JlfipKAkHWstOK", "uuid": "DB494B07-D6DE-4F70-8ED0-C8B864FF8717", "total": 1740, "error": null }, { "index": 1, "reference": "depto-102", "status": "failed", "invoice": null, "uuid": null, "total": 1740, "error": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital." } ] } ], "has_more": false, "url": "/v1/invoice_batches" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un lote `GET https://api.fiscalisto.com/v1/invoice_batches/{id}` Con cada factura: su estado, su id y su error. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoiceBatch = await fiscalisto.invoiceBatches.get("bat_OufpKqnrvx9p4hMdcoapAo5aaGJEme7mXj"); ``` ## Respuesta 200 Correcto. Un objeto `InvoiceBatch`. Sus campos están en «Objetos» al final de este archivo (InvoiceBatch). ```json { "id": "bat_OufpKqnrvx9p4hMdcoapAo5aaGJEme7mXj", "object": "invoice_batch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Cuotas de octubre", "status": "completed", "total": 2, "stamped": 1, "failed": 1, "created_at": "2026-10-24T10:15:42-06:00", "completed_at": "2026-10-24T10:15:42-06:00", "processing": 0, "invoices": [ { "index": 0, "reference": "depto-101", "status": "valid", "invoice": "inv_OufpKqnrvx9p0mn7VjQ6JlfipKAkHWstOK", "uuid": "DB494B07-D6DE-4F70-8ED0-C8B864FF8717", "total": 1740, "error": null }, { "index": 1, "reference": "depto-102", "status": "failed", "invoice": null, "uuid": null, "total": 1740, "error": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital." } ] } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Validar los datos fiscales de un cliente `POST https://api.fiscalisto.com/v1/tax_ids/validate` Antes de timbrar: si el RFC, el nombre, el régimen, el código postal y el uso van a pasar. Sin cif_id revisa lo que se puede revisar sin el SAT (formato, régimen para ese tipo de persona, código postal en el catálogo, uso para el régimen, nombre sin «S.A. de C.V.») y las listas del SAT. Con el idCIF de la constancia lee el registro del SAT y compara nombre, código postal y régimen, como hará el PAC. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `tax_id` | string | sí | El RFC del cliente. (hasta 20 caracteres) | | `legal_name` | string | | El nombre o razón social que va a poner en la factura. (hasta 300 caracteres) | | `tax_system` | string | | Su régimen fiscal (c_RegimenFiscal), como 601 o 612. | | `zip` | string | | El código postal de su domicilio fiscal. | | `use` | string | | El uso del CFDI (c_UsoCFDI), como G03. | | `cif_id` | string | | El idCIF de su constancia de situación fiscal (11 dígitos, arriba a la izquierda, junto al QR). Con él se compara todo con lo que tiene el SAT. | Ejemplo: ```json { "tax_id": "ICV060329BY0", "legal_name": "Inmobiliaria CVA, S.A. de C.V.", "tax_system": "601", "zip": "33800", "use": "G03", "cif_id": "12345678901" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const taxIdValidation = await fiscalisto.taxIds.validate({ tax_id: "ICV060329BY0", legal_name: "Inmobiliaria CVA, S.A. de C.V.", tax_system: "601", zip: "33800", use: "G03", cif_id: "12345678901", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `TaxIdValidation`. Sus campos están en «Objetos» al final de este archivo (TaxIdValidation). ```json { "object": "tax_id_validation", "tax_id": "ICV060329BY0", "kind": "company", "valid": false, "checked_with_sat": true, "errors": [ { "param": "legal_name", "code": "legal_name_capital_regime", "message": "Sin «S.A. de C.V.»: en CFDI 4.0 el nombre va sin régimen de capital, como en la constancia.", "suggestion": "INMOBILIARIA CVA" }, { "param": "zip", "code": "zip_mismatch", "message": "El código postal no es el del domicilio fiscal que tiene el SAT.", "suggestion": "33826" } ], "warnings": [], "sat_record": { "legal_name": "INMOBILIARIA CVA", "zip": "33826", "tax_systems": [ "601" ] }, "sat_error": null, "sat_lists": { "object": "sat_list_check", "tax_id": "ICV060329BY0", "name": null, "listed": false, "risk": "none", "lists": [], "lists_updated_at": "2026-10-24T00:12:40-06:00" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear una sucursal `POST https://api.fiscalisto.com/v1/branches` Otro lugar desde donde factura el emisor, con su propio código postal: sus facturas llevan ese CP en LugarExpedicion (y su zona horaria en la Fecha, y el IVA 8 % si es de la franja fronteriza), su dirección en el PDF y, si las da, sus propias series. Un CP que ya tiene otra sucursal contesta branch_exists; el del domicilio fiscal del emisor no es sucursal (es la Matriz). Hasta 200 por emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…). Por omisión, su único emisor. | | `name` | string | sí | Cómo la llama su equipo, por ejemplo «Sucursal Monterrey». Va en el PDF. (hasta 60 caracteres) | | `zip` | string | sí | Su código postal (LugarExpedicion de sus CFDI): uno por sucursal, distinto del domicilio fiscal del emisor. | | `address` | objeto | | La dirección que va en el PDF de sus facturas. | | `address.street` | string o null | | Calle. | | `address.exterior` | string o null | | Número exterior. | | `address.interior` | string o null | | Número interior. | | `address.neighborhood` | string o null | | Colonia. | | `address.city` | string o null | | Municipio o alcaldía. | | `address.state` | string o null | | Estado; por omisión, el de su código postal. | | `phone` | string o null | | Teléfono, para el PDF. | | `email` | string (email) o null | | Correo, para el PDF. | | `series` | objeto | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. | | `series.invoice` | string o null | | Serie de sus facturas (tipo I); sin ella, la del emisor. | | `series.credit_note` | string o null | | Serie de sus notas de crédito (tipo E); sin ella, la del emisor. | | `series.payment` | string o null | | Serie de sus complementos de pago (tipo P); sin ella, la del emisor. | | `series.transfer` | string o null | | Serie de sus traslados (tipo T); sin ella, la del emisor. | Ejemplo: ```json { "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "neighborhood": "Centro", "city": "Monterrey" }, "phone": "81 1234 5678", "series": { "invoice": "MTY" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const branch = await fiscalisto.branches.create({ name: "Sucursal Monterrey", zip: "64000", address: { street: "Av. Constitución", exterior: "1500", neighborhood: "Centro", city: "Monterrey", }, phone: "81 1234 5678", series: { invoice: "MTY" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Branch`. Sus campos están en «Objetos» al final de este archivo (Branch). ```json { "id": "brn_OufpKqnrvx9p3", "object": "branch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "interior": null, "neighborhood": "Centro", "city": "Monterrey", "state": "Nuevo León" }, "phone": "81 1234 5678", "email": null, "series": { "invoice": "MTY", "credit_note": null, "payment": null, "transfer": null }, "archived": false, "created_at": "2026-10-27T09:12:00-06:00", "updated_at": "2026-10-27T09:12:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar sucursales `GET https://api.fiscalisto.com/v1/branches` Las sucursales del emisor, las más recientes primero. Las archivadas solo con include_archived=true. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `include_archived` | consulta | | true para incluir las archivadas. | | `zip` | consulta | | Solo la de este código postal. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.branches.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `BranchList`. Sus campos están en «Objetos» al final de este archivo (BranchList). ```json { "object": "list", "data": [ { "id": "brn_OufpKqnrvx9p3", "object": "branch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "interior": null, "neighborhood": "Centro", "city": "Monterrey", "state": "Nuevo León" }, "phone": "81 1234 5678", "email": null, "series": { "invoice": "MTY", "credit_note": null, "payment": null, "transfer": null }, "archived": false, "created_at": "2026-10-27T09:12:00-06:00", "updated_at": "2026-10-27T09:12:00-06:00" } ], "has_more": false, "url": "/v1/branches" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una sucursal `GET https://api.fiscalisto.com/v1/branches/{id}` Una sucursal del emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const branch = await fiscalisto.branches.get("brn_OufpKqnrvx9p3"); ``` ## Respuesta 200 Correcto. Un objeto `Branch`. Sus campos están en «Objetos» al final de este archivo (Branch). ```json { "id": "brn_OufpKqnrvx9p3", "object": "branch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "interior": null, "neighborhood": "Centro", "city": "Monterrey", "state": "Nuevo León" }, "phone": "81 1234 5678", "email": null, "series": { "invoice": "MTY", "credit_note": null, "payment": null, "transfer": null }, "archived": false, "created_at": "2026-10-27T09:12:00-06:00", "updated_at": "2026-10-27T09:12:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Actualizar una sucursal `POST https://api.fiscalisto.com/v1/branches/{id}` Cambia solo los campos que manda; una serie en null vuelve a la del emisor. archived: true la archiva (ya no se factura desde ella y sus facturas la siguen nombrando), false la restaura. El código postal de una sucursal que ya facturó no cambia: si se mudó, archívela y cree otra. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | string | | Cómo la llama su equipo, por ejemplo «Sucursal Monterrey». Va en el PDF. (hasta 60 caracteres) | | `zip` | string | | Su código postal (LugarExpedicion de sus CFDI): uno por sucursal, distinto del domicilio fiscal del emisor. | | `address` | objeto | | La dirección que va en el PDF de sus facturas. | | `address.street` | string o null | | Calle. | | `address.exterior` | string o null | | Número exterior. | | `address.interior` | string o null | | Número interior. | | `address.neighborhood` | string o null | | Colonia. | | `address.city` | string o null | | Municipio o alcaldía. | | `address.state` | string o null | | Estado; por omisión, el de su código postal. | | `phone` | string o null | | Teléfono, para el PDF. | | `email` | string (email) o null | | Correo, para el PDF. | | `series` | objeto | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. | | `series.invoice` | string o null | | Serie de sus facturas (tipo I); sin ella, la del emisor. | | `series.credit_note` | string o null | | Serie de sus notas de crédito (tipo E); sin ella, la del emisor. | | `series.payment` | string o null | | Serie de sus complementos de pago (tipo P); sin ella, la del emisor. | | `series.transfer` | string o null | | Serie de sus traslados (tipo T); sin ella, la del emisor. | | `archived` | boolean | | true la archiva: ya no se puede facturar desde ella, y sus facturas la siguen nombrando. false la restaura. | Ejemplo: ```json { "archived": true } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const branch = await fiscalisto.branches.update("brn_OufpKqnrvx9p3", { archived: true }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Branch`. Sus campos están en «Objetos» al final de este archivo (Branch). ```json { "id": "brn_OufpKqnrvx9p3", "object": "branch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "interior": null, "neighborhood": "Centro", "city": "Monterrey", "state": "Nuevo León" }, "phone": "81 1234 5678", "email": null, "series": { "invoice": "MTY", "credit_note": null, "payment": null, "transfer": null }, "archived": false, "created_at": "2026-10-27T09:12:00-06:00", "updated_at": "2026-10-27T09:12:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar una sucursal `DELETE https://api.fiscalisto.com/v1/branches/{id}` Solo una que nunca facturó; con facturas contesta branch_has_invoices: archívela con archived: true. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.branches.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un cliente `POST https://api.fiscalisto.com/v1/customers` Un cliente del catálogo del emisor, con sus datos fiscales como vienen en su constancia. Un RFC que ya está en el catálogo contesta customer_exists. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `email` | string o null | | Correo del cliente (varios separados por comas), a donde van sus facturas con send_email. | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `foreign` | objeto o null | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). | | `foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | Ejemplo: ```json { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customer = await fiscalisto.customers.create({ tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Customer`. Sus campos están en «Objetos» al final de este archivo (Customer). ```json { "id": "cus_OufpKqnrvx9p1", "object": "customer", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example", "use": "G03", "foreign": null, "ieps_taxpayer": false, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar clientes `GET https://api.fiscalisto.com/v1/customers` Los clientes del emisor, los más recientes primero. Con tax_id, el de ese RFC. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `tax_id` | consulta | | Solo el de este RFC. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.customers.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `CustomerList`. Sus campos están en «Objetos» al final de este archivo (CustomerList). ```json { "object": "list", "data": [ { "id": "cus_OufpKqnrvx9p1", "object": "customer", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example", "use": "G03", "foreign": null, "ieps_taxpayer": false, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ], "has_more": false, "url": "/v1/customers" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un cliente `GET https://api.fiscalisto.com/v1/customers/{id}` Un cliente del catálogo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customer = await fiscalisto.customers.get("cus_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Customer`. Sus campos están en «Objetos» al final de este archivo (Customer). ```json { "id": "cus_OufpKqnrvx9p1", "object": "customer", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example", "use": "G03", "foreign": null, "ieps_taxpayer": false, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Actualizar un cliente `POST https://api.fiscalisto.com/v1/customers/{id}` Cambia solo los campos que manda. Las facturas ya timbradas no cambian. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `tax_id` | string | | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `legal_name` | string | | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `tax_system` | string | | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `zip` | string | | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `email` | string o null | | Correo del cliente (varios separados por comas), a donde van sus facturas con send_email. | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `foreign` | objeto o null | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). | | `foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | Ejemplo: ```json { "email": "facturas@cva.example" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customer = await fiscalisto.customers.update("cus_OufpKqnrvx9p1", { email: "facturas@cva.example" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Customer`. Sus campos están en «Objetos» al final de este archivo (Customer). ```json { "id": "cus_OufpKqnrvx9p1", "object": "customer", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example", "use": "G03", "foreign": null, "ieps_taxpayer": false, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar un cliente `DELETE https://api.fiscalisto.com/v1/customers/{id}` Lo quita del catálogo. Sus facturas timbradas siguen igual. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.customers.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Pedirle sus datos fiscales a un cliente `POST https://api.fiscalisto.com/v1/customer_data_requests` Una liga (url) que usted le manda a su cliente: la abre en fiscalisto.com y da su RFC, nombre, régimen, código postal y uso, escritos o leídos de su constancia, revisados antes de aceptarlos. Al mandarlos llega customer_data_request.completed. Un cliente nuevo queda en el catálogo del emisor; si ya existía con otros datos, los nuevos vienen en submission para que usted los aplique. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…) que va a facturarle. Por omisión, su único emisor. | | `reference` | string | | Su id para este cliente o pedido, como «Pedido 1042». El cliente la ve arriba del formulario y viene de vuelta en el evento. (hasta 100 caracteres) | | `description` | string | | Una línea que el cliente ve arriba del formulario: para qué son sus datos. (hasta 300 caracteres) | | `expires_in_days` | integer | | Días que la liga sirve, de 1 a 60. Por omisión 60. (de 1 a 60) | Ejemplo: ```json { "reference": "cliente-8812", "description": "Para facturar su suscripción de octubre." } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customerDataRequest = await fiscalisto.customerDataRequests.create({ reference: "cliente-8812", description: "Para facturar su suscripción de octubre.", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `CustomerDataRequest`. Sus campos están en «Objetos» al final de este archivo (CustomerDataRequest). ```json { "id": "cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys", "object": "customer_data_request", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "status": "completed", "url": null, "reference": "cliente-8812", "description": "Para facturar su suscripción de octubre.", "expires_at": "2026-12-23T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00", "submission": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "use": "G03", "email": "facturas@inmobiliariacva.mx", "source": "constancia", "customer": "cus_0q1Vmmw52sbD1", "applied": true, "submitted_at": "2026-10-24T11:02:41-06:00" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar solicitudes de datos `GET https://api.fiscalisto.com/v1/customer_data_requests` Las ligas para pedir datos fiscales, las más recientes primero, con su estado. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.customerDataRequests.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `CustomerDataRequestList`. Sus campos están en «Objetos» al final de este archivo (CustomerDataRequestList). ```json { "object": "list", "data": [ { "id": "cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys", "object": "customer_data_request", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "status": "completed", "url": null, "reference": "cliente-8812", "description": "Para facturar su suscripción de octubre.", "expires_at": "2026-12-23T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00", "submission": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "use": "G03", "email": "facturas@inmobiliariacva.mx", "source": "constancia", "customer": "cus_0q1Vmmw52sbD1", "applied": true, "submitted_at": "2026-10-24T11:02:41-06:00" } } ], "has_more": false, "url": "/v1/customer_data_requests" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una solicitud de datos `GET https://api.fiscalisto.com/v1/customer_data_requests/{id}` Una solicitud de datos: su liga y, si el cliente ya contestó, sus datos fiscales y el cliente que se guardó. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customerDataRequest = await fiscalisto.customerDataRequests.get("cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys"); ``` ## Respuesta 200 Correcto. Un objeto `CustomerDataRequest`. Sus campos están en «Objetos» al final de este archivo (CustomerDataRequest). ```json { "id": "cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys", "object": "customer_data_request", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "status": "completed", "url": null, "reference": "cliente-8812", "description": "Para facturar su suscripción de octubre.", "expires_at": "2026-12-23T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00", "submission": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "use": "G03", "email": "facturas@inmobiliariacva.mx", "source": "constancia", "customer": "cus_0q1Vmmw52sbD1", "applied": true, "submitted_at": "2026-10-24T11:02:41-06:00" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cancelar una solicitud de datos `DELETE https://api.fiscalisto.com/v1/customer_data_requests/{id}` La liga deja de funcionar. Una que el cliente ya contestó no se cancela. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const customerDataRequest = await fiscalisto.customerDataRequests.cancel("cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys"); ``` ## Respuesta 200 Correcto. Un objeto `CustomerDataRequest`. Sus campos están en «Objetos» al final de este archivo (CustomerDataRequest). ```json { "id": "cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys", "object": "customer_data_request", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "status": "completed", "url": null, "reference": "cliente-8812", "description": "Para facturar su suscripción de octubre.", "expires_at": "2026-12-23T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00", "submission": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "use": "G03", "email": "facturas@inmobiliariacva.mx", "source": "constancia", "customer": "cus_0q1Vmmw52sbD1", "applied": true, "submitted_at": "2026-10-24T11:02:41-06:00" } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un producto `POST https://api.fiscalisto.com/v1/products` Un producto o servicio del catálogo del emisor, con su clave del SAT, su unidad, su precio y sus impuestos, para usarlo en items con product. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `name` | string o null | | Nombre corto para encontrarlo, solo para usted; en el CFDI va description. | | `description` | string | sí | Descripción. (hasta 1000 caracteres) | | `product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… (hasta 5 caracteres) | | `unit_name` | string o null | | Nombre de la unidad (Unidad), hasta 20 caracteres. | | `sku` | string o null | | Número de identificación o SKU (NoIdentificacion). | | `unit_price` | number | | Precio unitario antes de impuestos (ValorUnitario). (de 0 a 1000000000000) | | `open_price` | boolean | | Precio abierto: sin precio fijo (una noche de hotel que cambia cada día); cada factura manda su unit_price. | | `taxability` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` | | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… | | `iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `ieps` | number o null | | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. | | `ieps_factor` | `Tasa`, `Cuota` | | Tasa o Cuota (TipoFactor del IEPS). | | `iva_retention` | number | | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). (de 0 a 0.16) | | `isr_retention` | number | | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). (de 0 a 0.35) | | `local_taxes` | arreglo de objeto | | Impuestos locales que lleva, como el ISH 3% de hospedaje: van en cada factura con él, sobre sus conceptos. (hasta 20) | | `local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de -∞ a 100) | | `track_stock` | boolean | | Lleva existencias (Inventario): cada factura que lo vende las saca del almacén de su sucursal, y una cancelada las regresa. Ver GET /v1/products/{id}/stock. | | `cost` | number o null | | Su costo por unidad en pesos sin IVA, hasta que sus compras den otro: vale las existencias que entran sin unit_cost. | | `barcode` | string o null | | Código de barras (EAN, UPC o el suyo). Una factura cuyo concepto lo trae como NoIdentificacion saca existencias de este producto. | | `min_stock` | number o null | | Existencia mínima: por debajo, el aviso de existencias bajas lo incluye. | Ejemplo: ```json { "description": "Mensualidad", "product_key": "86121700", "sku": "MEN-1", "unit_price": 2500 } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const product = await fiscalisto.products.create({ description: "Mensualidad", product_key: "86121700", sku: "MEN-1", unit_price: 2500, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Product`. Sus campos están en «Objetos» al final de este archivo (Product). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": null, "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "unit_price": 2500, "open_price": false, "taxability": "02", "iva": "0.16", "ieps": null, "ieps_factor": null, "iva_retention": 0, "isr_retention": 0, "local_taxes": [], "track_stock": false, "cost": null, "barcode": null, "min_stock": null, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar productos `GET https://api.fiscalisto.com/v1/products` Los productos del emisor, los más recientes primero. Filtre por sku o product_key. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `sku` | consulta | | Solo el de este SKU. | | `product_key` | consulta | | Solo los de esta clave del SAT. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.products.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `ProductList`. Sus campos están en «Objetos» al final de este archivo (ProductList). ```json { "object": "list", "data": [ { "id": "prd_OufpKqnrvx9p1", "object": "product", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": null, "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "unit_price": 2500, "open_price": false, "taxability": "02", "iva": "0.16", "ieps": null, "ieps_factor": null, "iva_retention": 0, "isr_retention": 0, "local_taxes": [], "track_stock": false, "cost": null, "barcode": null, "min_stock": null, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ], "has_more": false, "url": "/v1/products" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un producto `GET https://api.fiscalisto.com/v1/products/{id}` Un producto del catálogo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const product = await fiscalisto.products.get("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Product`. Sus campos están en «Objetos» al final de este archivo (Product). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": null, "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "unit_price": 2500, "open_price": false, "taxability": "02", "iva": "0.16", "ieps": null, "ieps_factor": null, "iva_retention": 0, "isr_retention": 0, "local_taxes": [], "track_stock": false, "cost": null, "barcode": null, "min_stock": null, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Actualizar un producto `POST https://api.fiscalisto.com/v1/products/{id}` Cambia solo los campos que manda. Las facturas ya timbradas no cambian. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | string o null | | Nombre corto para encontrarlo, solo para usted; en el CFDI va description. | | `description` | string | | Descripción. (hasta 1000 caracteres) | | `product_key` | string | | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… (hasta 5 caracteres) | | `unit_name` | string o null | | Nombre de la unidad (Unidad), hasta 20 caracteres. | | `sku` | string o null | | Número de identificación o SKU (NoIdentificacion). | | `unit_price` | number | | Precio unitario antes de impuestos (ValorUnitario). (de 0 a 1000000000000) | | `open_price` | boolean | | Precio abierto: sin precio fijo (una noche de hotel que cambia cada día); cada factura manda su unit_price. | | `taxability` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` | | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… | | `iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `ieps` | number o null | | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. | | `ieps_factor` | `Tasa`, `Cuota` | | Tasa o Cuota (TipoFactor del IEPS). | | `iva_retention` | number | | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). (de 0 a 0.16) | | `isr_retention` | number | | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). (de 0 a 0.35) | | `local_taxes` | arreglo de objeto | | Impuestos locales que lleva, como el ISH 3% de hospedaje: van en cada factura con él, sobre sus conceptos. (hasta 20) | | `local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de -∞ a 100) | | `track_stock` | boolean | | Lleva existencias (Inventario): cada factura que lo vende las saca del almacén de su sucursal, y una cancelada las regresa. Ver GET /v1/products/{id}/stock. | | `cost` | number o null | | Su costo por unidad en pesos sin IVA, hasta que sus compras den otro: vale las existencias que entran sin unit_cost. | | `barcode` | string o null | | Código de barras (EAN, UPC o el suyo). Una factura cuyo concepto lo trae como NoIdentificacion saca existencias de este producto. | | `min_stock` | number o null | | Existencia mínima: por debajo, el aviso de existencias bajas lo incluye. | Ejemplo: ```json { "unit_price": 2600 } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const product = await fiscalisto.products.update("prd_OufpKqnrvx9p1", { unit_price: 2600 }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Product`. Sus campos están en «Objetos» al final de este archivo (Product). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": null, "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "unit_price": 2500, "open_price": false, "taxability": "02", "iva": "0.16", "ieps": null, "ieps_factor": null, "iva_retention": 0, "isr_retention": 0, "local_taxes": [], "track_stock": false, "cost": null, "barcode": null, "min_stock": null, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar un producto `DELETE https://api.fiscalisto.com/v1/products/{id}` Lo quita del catálogo. Las facturas que lo usaron siguen igual. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.products.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver la opinión de cumplimiento (32-D) `GET https://api.fiscalisto.com/v1/issuers/{id}/compliance_opinion` La última opinión que trajo del SAT: positiva, negativa (con sus motivos), sin obligaciones o en suspensión. result es null hasta la primera consulta. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const complianceOpinion = await fiscalisto.issuers.complianceOpinion("id"); ``` ## Respuesta 200 Correcto. Un objeto `ComplianceOpinion`. Sus campos están en «Objetos» al final de este archivo (ComplianceOpinion). ```json { "object": "compliance_opinion", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "result": "positive", "date": "2026-10-27", "folio": "26NA20261027TEST", "reasons": [], "pdf": true, "checked_at": "2026-10-27T09:12:00-06:00", "last_error": null } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Pedir la opinión de cumplimiento al SAT `POST https://api.fiscalisto.com/v1/issuers/{id}/compliance_opinion/refresh` La pide al SAT ahora con la e.firma guardada del emisor (POST /v1/issuers/{id}/efirma con keep_for_downloads: true). 1 timbre en producción, una vez cada 10 minutos; regresa si el SAT no contesta. Si el SAT tarda, contesta status processing y el resultado llega a GET /v1/issuers/{id}/compliance_opinion. En pruebas da una positiva de ejemplo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satRefresh = await fiscalisto.issuers.refreshComplianceOpinion("id", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `SatRefresh`. Sus campos están en «Objetos» al final de este archivo (SatRefresh). ```json { "object": "sat_refresh", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "document": "compliance_opinion", "status": "completed", "refreshed_at": "2026-10-27T09:12:00-06:00", "next_refresh_at": "2026-10-27T09:22:00-06:00", "url": "/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el PDF de la opinión de cumplimiento `GET https://api.fiscalisto.com/v1/issuers/{id}/compliance_opinion/pdf` El PDF de la última opinión, como lo da el SAT. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.issuers.complianceOpinionPdf("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver la constancia de situación fiscal `GET https://api.fiscalisto.com/v1/issuers/{id}/tax_status_certificate` Lo que dice la última constancia generada: razón social, regímenes, código postal, estatus en el padrón e idCIF. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const taxStatusCertificate = await fiscalisto.issuers.taxStatusCertificate("id"); ``` ## Respuesta 200 Correcto. Un objeto `TaxStatusCertificate`. Sus campos están en «Objetos» al final de este archivo (TaxStatusCertificate). ```json { "object": "tax_status_certificate", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "issued_on": "2026-10-27", "id_cif": "26010000000", "tax_id": "EKU9003173C9", "legal_name": "ESCUELA KEMPER URGATE", "tax_systems": [ { "code": "601", "name": "Régimen General de Ley Personas Morales" } ], "zip": "42501", "status": "ACTIVO", "pdf": true, "created_at": "2026-10-27T09:12:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Generar la constancia de situación fiscal en el SAT `POST https://api.fiscalisto.com/v1/issuers/{id}/tax_status_certificate/refresh` Genera una constancia nueva con la e.firma guardada del emisor. 1 timbre en producción, una vez cada 10 minutos; regresa si el SAT no contesta. En pruebas hace una con los datos del emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satRefresh = await fiscalisto.issuers.refreshTaxStatusCertificate("id", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `SatRefresh`. Sus campos están en «Objetos» al final de este archivo (SatRefresh). ```json { "object": "sat_refresh", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "document": "compliance_opinion", "status": "completed", "refreshed_at": "2026-10-27T09:12:00-06:00", "next_refresh_at": "2026-10-27T09:22:00-06:00", "url": "/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el PDF de la constancia `GET https://api.fiscalisto.com/v1/issuers/{id}/tax_status_certificate/pdf` El PDF de la última constancia, como lo da el SAT. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.issuers.taxStatusCertificatePdf("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Revisar el buzón tributario en el SAT `POST https://api.fiscalisto.com/v1/issuers/{id}/sat_mailbox/refresh` Lee los avisos del buzón tributario del emisor ahora, con su e.firma guardada; nunca abre notificaciones (abrirlas firma el acuse y corren los plazos). 1 timbre en producción, una vez cada 10 minutos. En pruebas deja dos avisos de ejemplo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satRefresh = await fiscalisto.issuers.refreshSatMailbox("id", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `SatRefresh`. Sus campos están en «Objetos» al final de este archivo (SatRefresh). ```json { "object": "sat_refresh", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "document": "compliance_opinion", "status": "completed", "refreshed_at": "2026-10-27T09:12:00-06:00", "next_refresh_at": "2026-10-27T09:22:00-06:00", "url": "/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar avisos del buzón tributario `GET https://api.fiscalisto.com/v1/sat_mailbox_messages` Los avisos que trajo del buzón del emisor, los más recientes primero. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `unread` | consulta | | true para solo los no leídos en el SAT, false para los leídos. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satMailboxMessages.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `SatMailboxMessageList`. Sus campos están en «Objetos» al final de este archivo (SatMailboxMessageList). ```json { "object": "list", "data": [ { "id": "sbm_OufpKqnrvx9p1", "object": "sat_mailbox_message", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "subject": "Requerimiento de información", "summary": "Se le requiere la documentación que acredite…", "source": "mailbox", "unread": true, "urgency": "urgent", "kind": "Requerimiento", "action_required": true, "received_at": "2026-10-27", "created_at": "2026-10-27T09:12:00-06:00" } ], "has_more": false, "url": "/v1/sat_mailbox_messages" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un aviso del buzón tributario `GET https://api.fiscalisto.com/v1/sat_mailbox_messages/{id}` Un aviso del buzón, con su urgencia y si pide hacer algo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satMailboxMessage = await fiscalisto.satMailboxMessages.get("sbm_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `SatMailboxMessage`. Sus campos están en «Objetos» al final de este archivo (SatMailboxMessage). ```json { "id": "sbm_OufpKqnrvx9p1", "object": "sat_mailbox_message", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "subject": "Requerimiento de información", "summary": "Se le requiere la documentación que acredite…", "source": "mailbox", "unread": true, "urgency": "urgent", "kind": "Requerimiento", "action_required": true, "received_at": "2026-10-27", "created_at": "2026-10-27T09:12:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar hallazgos en los CFDI `GET https://api.fiscalisto.com/v1/sat_findings` Lo que Fiscalisto encontró en los CFDI del emisor (los suyos y los descargados del SAT) y nadie ha revisado, por tipo y del más grave al más leve: proveedores en la lista 69-B, CFDI cancelados, pagos en efectivo de más de $2,000, errores de CFDI 4.0. Todos en una página. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `direction` | consulta | | issued (emitidos) o received (recibidos); sin él, los dos. | | `date_gte` | consulta | | CFDI desde este día (AAAA-MM-DD). | | `date_lte` | consulta | | CFDI hasta este día (AAAA-MM-DD, completo). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.satFindings.list(); ``` ## Respuesta 200 Correcto. Un objeto `SatFindingList`. Sus campos están en «Objetos» al final de este archivo (SatFindingList). ```json { "object": "list", "data": [ { "id": "efos_definitivo", "object": "sat_finding", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "code": "efos_definitivo", "severity": "critical", "title": "Proveedor en la lista 69-B", "explanation": "El SAT presume que sus operaciones son simuladas: sus CFDI no se deducen.", "invoices": 3, "amount": 48720 } ], "has_more": false, "url": "/v1/sat_findings" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar almacenes `GET https://api.fiscalisto.com/v1/warehouses` Los almacenes del emisor, los más recientes primero: el de la Matriz y uno por sucursal se crean solos. Todos en una página. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `include_archived` | consulta | | true para incluir los archivados. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.warehouses.list(); ``` ## Respuesta 200 Correcto. Un objeto `WarehouseList`. Sus campos están en «Objetos» al final de este archivo (WarehouseList). ```json { "object": "list", "data": [ { "id": "whs_OufpKqnrvx9p1", "object": "warehouse", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Matriz", "code": null, "type": "warehouse", "branch": null, "default": true, "archived": false, "created_at": "2026-10-27T09:12:00-06:00" } ], "has_more": false, "url": "/v1/warehouses" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un almacén `GET https://api.fiscalisto.com/v1/warehouses/{id}` Un almacén del emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const warehouse = await fiscalisto.warehouses.get("whs_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Warehouse`. Sus campos están en «Objetos» al final de este archivo (Warehouse). ```json { "id": "whs_OufpKqnrvx9p1", "object": "warehouse", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Matriz", "code": null, "type": "warehouse", "branch": null, "default": true, "archived": false, "created_at": "2026-10-27T09:12:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver las existencias de un almacén `GET https://api.fiscalisto.com/v1/warehouses/{id}/stock` Lo que hay de cada producto en el almacén, el producto más reciente primero. Solo los que tienen existencias, salvo con include_empty=true (todos los que llevan track_stock). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `include_empty` | consulta | | true para incluir los productos con track_stock que están en 0. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.warehouses.stock("id")) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `StockLevelList`. Sus campos están en «Objetos» al final de este archivo (StockLevelList). ```json { "object": "list", "data": [ { "id": "stl_OufpKqnrvx9p4gfFC6", "object": "stock_level", "livemode": false, "product": "prd_OufpKqnrvx9p2", "warehouse": "whs_OufpKqnrvx9p1", "on_hand": 48, "reserved": 6, "available": 42, "value": 4320, "average_cost": 90 } ], "has_more": false, "url": "/v1/warehouses/{id}/stock" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver las existencias de un producto `GET https://api.fiscalisto.com/v1/products/{id}/stock` Lo que hay del producto en cada almacén del emisor (también en 0), apartado por cotizaciones y disponible. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.products.stock("id"); ``` ## Respuesta 200 Correcto. Un objeto `StockLevelList`. Sus campos están en «Objetos» al final de este archivo (StockLevelList). ```json { "object": "list", "data": [ { "id": "stl_OufpKqnrvx9p4gfFC6", "object": "stock_level", "livemode": false, "product": "prd_OufpKqnrvx9p2", "warehouse": "whs_OufpKqnrvx9p1", "on_hand": 48, "reserved": 6, "available": 42, "value": 4320, "average_cost": 90 } ], "has_more": false, "url": "/v1/products/{id}/stock" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar movimientos (kárdex) `GET https://api.fiscalisto.com/v1/stock_movements` Cada entrada y salida de existencias, la más reciente primero: ventas (de sus facturas), compras, ajustes, traspasos y lo que deshizo una cancelación. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `product` | consulta | | Solo los de este producto (prd_…). | | `warehouse` | consulta | | Solo los de este almacén (whs_…). | | `type` | consulta | | Solo los de este tipo. | | `date_gte` | consulta | | Desde este día (AAAA-MM-DD) u hora (AAAA-MM-DDTHH:MM:SS) de la Ciudad de México. | | `date_lte` | consulta | | Hasta este día (completo) u hora de la Ciudad de México. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.stockMovements.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `StockMovementList`. Sus campos están en «Objetos» al final de este archivo (StockMovementList). ```json { "object": "list", "data": [ { "id": "stm_OufpKqnrvx9p7", "object": "stock_movement", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "product": "prd_OufpKqnrvx9p2", "warehouse": "whs_OufpKqnrvx9p1", "type": "sale", "quantity": -2, "unit_cost": 90, "value": -180, "lot": null, "source": { "type": "invoice", "id": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718" }, "reason": null, "date": "2026-10-27T10:15:42", "reversed": false, "created_at": "2026-10-27T10:15:43-06:00" } ], "has_more": false, "url": "/v1/stock_movements" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un movimiento `GET https://api.fiscalisto.com/v1/stock_movements/{id}` Una entrada o salida de existencias, con lo que la causó (source). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const stockMovement = await fiscalisto.stockMovements.get("stm_OufpKqnrvx9p7"); ``` ## Respuesta 200 Correcto. Un objeto `StockMovement`. Sus campos están en «Objetos» al final de este archivo (StockMovement). ```json { "id": "stm_OufpKqnrvx9p7", "object": "stock_movement", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "product": "prd_OufpKqnrvx9p2", "warehouse": "whs_OufpKqnrvx9p1", "type": "sale", "quantity": -2, "unit_cost": 90, "value": -180, "lot": null, "source": { "type": "invoice", "id": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718" }, "reason": null, "date": "2026-10-27T10:15:42", "reversed": false, "created_at": "2026-10-27T10:15:43-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ajustar existencias `POST https://api.fiscalisto.com/v1/stock_adjustments` Mete o saca existencias de un almacén con su motivo: el inventario inicial, una merma, un robo, un sobrante. Las entradas valen su unit_cost (o el costo promedio), las salidas el costo del emisor (promedio o PEPS). Los productos deben llevar track_stock: true. Se aplica al momento (status applied). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…). Por omisión, su único emisor. | | `warehouse` | string | | El almacén (whs_…). Por omisión, el de la Matriz. | | `reason` | `shrinkage`, `theft`, `damage`, `expiration`, `internal_use`, `gift`, `initial`, `surplus`, `count`, `other` | sí | Por qué: shrinkage (merma), theft (robo o extravío), damage (daño), expiration (caducidad), internal_use (uso interno), gift (muestra o regalo), initial (inventario inicial), surplus (sobrante), count (conteo físico) u other. | | `note` | string o null | | Una nota, para el kárdex. | | `lines` | arreglo de objeto | sí | Lo que entra o sale de cada producto, hasta 500 líneas. (hasta 500) | | `lines[].product` | string | sí | El producto (prd_…), con track_stock: true. | | `lines[].quantity` | number | sí | Cuánto entra (positivo) o sale (negativo), en la unidad del producto. | | `lines[].unit_cost` | number | | Lo que costó cada unidad que entra, en pesos sin IVA; sin él, el costo promedio de hoy. (de 0 a ∞) | | `lines[].lot` | string | | El lote o número de serie, si el producto los lleva. (hasta 60 caracteres) | | `lines[].expires_on` | string | | La caducidad de un lote nuevo (AAAA-MM-DD). | Ejemplo: ```json { "reason": "initial", "note": "Inventario de apertura", "lines": [ { "product": "prd_OufpKqnrvx9p2", "quantity": 50, "unit_cost": 90 } ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const stockAdjustment = await fiscalisto.stockAdjustments.create({ reason: "initial", note: "Inventario de apertura", lines: [ { product: "prd_OufpKqnrvx9p2", quantity: 50, unit_cost: 90, }, ], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `StockAdjustment`. Sus campos están en «Objetos» al final de este archivo (StockAdjustment). ```json { "id": "adj_OufpKqnrvx9p1", "object": "stock_adjustment", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "warehouse": "whs_OufpKqnrvx9p1", "reason": "initial", "note": "Inventario de apertura", "status": "applied", "value": 4500, "lines": [ { "product": "prd_OufpKqnrvx9p2", "quantity": 50, "unit_cost": 90, "lot": null } ], "created_at": "2026-10-27T09:30:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar ajustes `GET https://api.fiscalisto.com/v1/stock_adjustments` Los ajustes del emisor, los más recientes primero. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `warehouse` | consulta | | Solo los de este almacén (whs_…). | | `status` | consulta | | applied, pending, rejected o reversed. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.stockAdjustments.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `StockAdjustmentList`. Sus campos están en «Objetos» al final de este archivo (StockAdjustmentList). ```json { "object": "list", "data": [ { "id": "adj_OufpKqnrvx9p1", "object": "stock_adjustment", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "warehouse": "whs_OufpKqnrvx9p1", "reason": "initial", "note": "Inventario de apertura", "status": "applied", "value": 4500, "lines": [ { "product": "prd_OufpKqnrvx9p2", "quantity": 50, "unit_cost": 90, "lot": null } ], "created_at": "2026-10-27T09:30:00-06:00" } ], "has_more": false, "url": "/v1/stock_adjustments" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un ajuste `GET https://api.fiscalisto.com/v1/stock_adjustments/{id}` Un ajuste de existencias, con sus líneas. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const stockAdjustment = await fiscalisto.stockAdjustments.get("adj_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `StockAdjustment`. Sus campos están en «Objetos» al final de este archivo (StockAdjustment). ```json { "id": "adj_OufpKqnrvx9p1", "object": "stock_adjustment", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "warehouse": "whs_OufpKqnrvx9p1", "reason": "initial", "note": "Inventario de apertura", "status": "applied", "value": 4500, "lines": [ { "product": "prd_OufpKqnrvx9p2", "quantity": 50, "unit_cost": 90, "lot": null } ], "created_at": "2026-10-27T09:30:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Consultar un catálogo del SAT `GET https://api.fiscalisto.com/v1/sat_catalogs/{name}` product_keys y unit_keys se buscan con q (texto o clave). Los demás vienen completos: tax_systems, cfdi_uses, payment_forms, payment_methods, relationship_types, cancellation_motives, export_types, tax_objects, retention_codes. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | ruta | sí | El catálogo, uno de: product_keys, unit_keys, tax_systems, cfdi_uses, payment_forms, payment_methods, relationship_types, cancellation_motives, export_types, tax_objects, retention_codes. | | `q` | consulta | | Qué buscar: palabras («consultoría») o una clave. | | `limit` | consulta | | Hasta cuántos, de 1 a 100 (búsquedas). Por omisión 20. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.satCatalogs.get("product_keys"); ``` ## Respuesta 200 Correcto. Un objeto `SatCatalogEntryList`. Sus campos están en «Objetos» al final de este archivo (SatCatalogEntryList). ```json { "object": "list", "data": [ { "code": "80131500", "description": "Alquiler de propiedades", "similar_words": "renta" } ], "has_more": false, "url": "/v1/sat_catalogs/{name}" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500, 503 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Revisar un RFC en las listas del SAT `GET https://api.fiscalisto.com/v1/sat_lists/{tax_id}` Si el RFC aparece en la lista 69-B (operaciones simuladas), 69-B Bis o del art. 69 (no localizados, créditos firmes, exigibles, cancelados, sentencias, CSD sin efectos). Las listas se actualizan cada día con los archivos del SAT. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `tax_id` | ruta | sí | El RFC a revisar (12 caracteres para morales, 13 para físicas). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satListCheck = await fiscalisto.satLists.get("{tax_id}"); ``` ## Respuesta 200 Correcto. Un objeto `SatListCheck`. Sus campos están en «Objetos» al final de este archivo (SatListCheck). ```json { "object": "sat_list_check", "tax_id": "AAA080808HL8", "name": "EMPRESA SIMULADORA SA DE CV", "listed": true, "risk": "high", "lists": [ { "list": "69b", "source": "69b", "source_name": "69-B (operaciones simuladas)", "situation": "Definitivo", "published_on": null, "details": { "Publicación página SAT definitivos": "15/03/2024", "Número y fecha de oficio global de definitivos": "500-05-2024-1234 de fecha 01/03/2024" } } ], "lists_updated_at": "2026-10-24T00:12:40-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Revisar varios RFC en las listas del SAT `POST https://api.fiscalisto.com/v1/sat_lists/check` Hasta 1,000 RFC en una solicitud, en el mismo orden en que los mandó (sin repetidos). No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `tax_ids` | arreglo de string | sí | Los RFC a revisar, hasta 1000. (hasta 1000) | Ejemplo: ```json { "tax_ids": [ "AAA080808HL8", "ICV060329BY0" ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.satLists.check({ tax_ids: ["AAA080808HL8", "ICV060329BY0"] }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `SatListCheckList`. Sus campos están en «Objetos» al final de este archivo (SatListCheckList). ```json { "object": "list", "data": [ { "object": "sat_list_check", "tax_id": "AAA080808HL8", "name": "EMPRESA SIMULADORA SA DE CV", "listed": true, "risk": "high", "lists": [ { "list": "69b", "source": "69b", "source_name": "69-B (operaciones simuladas)", "situation": "Definitivo", "published_on": null, "details": { "Publicación página SAT definitivos": "15/03/2024", "Número y fecha de oficio global de definitivos": "500-05-2024-1234 de fecha 01/03/2024" } } ], "lists_updated_at": "2026-10-24T00:12:40-06:00" } ], "has_more": false, "url": "/v1/sat_lists/check" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Pedirle al SAT las facturas de un emisor `POST https://api.fiscalisto.com/v1/sat_downloads` La descarga masiva del SAT: las facturas que el emisor recibió (o emitió) en un periodo de hasta un año, con su XML. Contesta 202 de inmediato; el SAT tarda de minutos a horas y al terminar llega sat_download.completed (o sat_download.failed) y las facturas quedan en GET /v1/sat_invoices. En producción necesita la e.firma guardada del emisor; en pruebas no se le pregunta al SAT: la descarga trae facturas de ejemplo al instante. En producción usa 1 timbre por cada 100 CFDI que trae (al menos 1). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…). Por omisión, su único emisor. | | `direction` | `received`, `issued` | | received (las facturas que le hicieron al emisor, por omisión) o issued (las que emitió, aunque no haya sido con Fiscalisto). (por omisión "received") | | `from` | string | sí | Desde qué día (incluido), en hora de la Ciudad de México. | | `to` | string | sí | Hasta qué día (incluido). A lo más un año después de from, y no después de hoy. | | `type` | `I`, `E`, `P`, `T`, `N` | | Solo un tipo de comprobante: I, E, P, T o N. Por omisión, todos. | | `counterparty_tax_id` | string | | Solo las de un RFC: el emisor de las recibidas, o el receptor de las emitidas. | | `uuid` | string (uuid) | | Una sola factura, por su folio fiscal (UUID). | Ejemplo: ```json { "direction": "received", "from": "2026-09-01", "to": "2026-09-30" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDownload = await fiscalisto.satDownloads.create({ direction: "received", from: "2026-09-01", to: "2026-09-30", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 202 Correcto. Un objeto `SatDownload`. Sus campos están en «Objetos» al final de este archivo (SatDownload). ```json { "id": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "object": "sat_download", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "direction": "received", "from": "2026-09-01", "to": "2026-09-30", "type": null, "counterparty_tax_id": null, "uuid": null, "status": "completed", "new_invoices": 8, "error": null, "created_at": "2026-10-24T10:20:00-06:00", "completed_at": "2026-10-24T10:27:41-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar descargas del SAT `GET https://api.fiscalisto.com/v1/sat_downloads` Las descargas pedidas al SAT, las más recientes primero, con su estado. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satDownloads.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `SatDownloadList`. Sus campos están en «Objetos» al final de este archivo (SatDownloadList). ```json { "object": "list", "data": [ { "id": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "object": "sat_download", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "direction": "received", "from": "2026-09-01", "to": "2026-09-30", "type": null, "counterparty_tax_id": null, "uuid": null, "status": "completed", "new_invoices": 8, "error": null, "created_at": "2026-10-24T10:20:00-06:00", "completed_at": "2026-10-24T10:27:41-06:00" } ], "has_more": false, "url": "/v1/sat_downloads" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una descarga del SAT `GET https://api.fiscalisto.com/v1/sat_downloads/{id}` Una descarga del SAT: su periodo, su estado y cuántos CFDI trajo. Al terminar llega sat_download.completed. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDownload = await fiscalisto.satDownloads.get("sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc"); ``` ## Respuesta 200 Correcto. Un objeto `SatDownload`. Sus campos están en «Objetos» al final de este archivo (SatDownload). ```json { "id": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "object": "sat_download", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "direction": "received", "from": "2026-09-01", "to": "2026-09-30", "type": null, "counterparty_tax_id": null, "uuid": null, "status": "completed", "new_invoices": 8, "error": null, "created_at": "2026-10-24T10:20:00-06:00", "completed_at": "2026-10-24T10:27:41-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar facturas del SAT `GET https://api.fiscalisto.com/v1/sat_invoices` Las facturas que trajeron sus descargas (y las demás que Fiscalisto tenga del emisor), de la más reciente a la más antigua por fecha. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `direction` | consulta | | received (por omisión) o issued. | | `type` | consulta | | I, E, P, T o N. | | `counterparty_tax_id` | consulta | | El RFC de quien la emitió (recibidas) o de quien la recibió (emitidas). | | `date_gte` | consulta | | Con fecha desde este día (AAAA-MM-DD). | | `date_lte` | consulta | | Con fecha hasta este día (AAAA-MM-DD), incluido. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satInvoices.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `SatInvoiceList`. Sus campos están en «Objetos» al final de este archivo (SatInvoiceList). ```json { "object": "list", "data": [ { "id": "sat_OufpKqnrvx9p1F", "object": "sat_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "uuid": "9B2D4E61-7C3A-4F18-8E5D-0A6B9C1D2E3F", "direction": "received", "type": "I", "date": "2026-09-12T11:04:00", "series": "A", "folio": "4821", "issued_by": { "tax_id": "XIA190128J61", "legal_name": "XENON INDUSTRIAL ARTICLES" }, "issued_to": { "tax_id": "TOL190312KT4", "legal_name": "TOLPI" }, "currency": "MXN", "exchange_rate": null, "payment_method": "PUE", "payment_form": "03", "use": "G03", "subtotal": 18400, "total": 21344, "taxes": { "iva": 2944, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "ieps_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "sat_status": "active", "sat_download": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "downloaded_at": "2026-10-24T10:27:40-06:00" } ], "has_more": false, "url": "/v1/sat_invoices" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una factura del SAT `GET https://api.fiscalisto.com/v1/sat_invoices/{id}` Un CFDI que trajo una descarga del SAT, con sus importes y su estado ante el SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satInvoice = await fiscalisto.satInvoices.get("sat_OufpKqnrvx9p1F"); ``` ## Respuesta 200 Correcto. Un objeto `SatInvoice`. Sus campos están en «Objetos» al final de este archivo (SatInvoice). ```json { "id": "sat_OufpKqnrvx9p1F", "object": "sat_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "uuid": "9B2D4E61-7C3A-4F18-8E5D-0A6B9C1D2E3F", "direction": "received", "type": "I", "date": "2026-09-12T11:04:00", "series": "A", "folio": "4821", "issued_by": { "tax_id": "XIA190128J61", "legal_name": "XENON INDUSTRIAL ARTICLES" }, "issued_to": { "tax_id": "TOL190312KT4", "legal_name": "TOLPI" }, "currency": "MXN", "exchange_rate": null, "payment_method": "PUE", "payment_form": "03", "use": "G03", "subtotal": 18400, "total": 21344, "taxes": { "iva": 2944, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "ieps_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "sat_status": "active", "sat_download": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "downloaded_at": "2026-10-24T10:27:40-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el XML de una factura del SAT `GET https://api.fiscalisto.com/v1/sat_invoices/{id}/xml` El CFDI tal como lo entregó el SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.xml", await fiscalisto.satInvoices.xml("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Consultar las declaraciones de un emisor en el SAT `POST https://api.fiscalisto.com/v1/sat_declarations/refresh` Entra al SAT con la e.firma guardada del emisor y trae las declaraciones que presentó: provisionales o definitivas (desde 2022), la anual de persona moral (desde 2019) y las del servicio anterior Declaraciones y pagos, con su PDF, su acuse y sus cifras. La primera consulta trae todos los años que el SAT conserva; las siguientes, este año y el anterior. Solo consulta: nunca presenta ni firma nada. En producción usa 1 timbre por consulta (regresa si el SAT no contesta) y se puede hacer una vez cada 10 minutos por emisor. Contesta en menos de un minuto; si queda algo por bajar (status processing), sigue solo en la siguiente hora sin otro timbre. En pruebas no se le pregunta al SAT: trae declaraciones de ejemplo al instante. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…). Por omisión, su único emisor. | Ejemplo: ```json {} ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDeclarationsRefresh = await fiscalisto.satDeclarations.refresh({ idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `SatDeclarationsRefresh`. Sus campos están en «Objetos» al final de este archivo (SatDeclarationsRefresh). ```json { "object": "sat_declarations_refresh", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "completed", "found": 10, "downloaded": 10, "pending": 0, "warning": null, "refreshed_at": "2026-10-26T09:12:00-06:00", "next_refresh_at": "2026-10-26T09:22:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar declaraciones del SAT `GET https://api.fiscalisto.com/v1/sat_declarations` Las declaraciones que trajeron sus consultas, del ejercicio y mes más recientes a los más antiguos (la anual antes de los meses de su año). No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `year` | consulta | | Solo las de un ejercicio, como 2025. | | `source` | consulta | | monthly (provisionales o definitivas), annual (la anual) o dyp (Declaraciones y pagos). | | `month` | consulta | | Solo las de un mes (1 a 12). | | `type` | consulta | | normal o complementary (complementarias). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.satDeclarations.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `SatDeclarationList`. Sus campos están en «Objetos» al final de este archivo (SatDeclarationList). ```json { "object": "list", "data": [ { "id": "dcl_OufpKqnrvx9p1F", "object": "sat_declaration", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "source": "monthly", "year": 2026, "month": 9, "period": "Septiembre", "type": "normal", "complementary_type": null, "operation_number": "92026090000001", "payment_line": "0426TEST2026090001", "filed_on": "2026-10-15", "profit_coefficient": 0.0219, "isr_due": 7718.53, "vat_due": 16520, "total_due": 24238.53, "sections": [ { "tax": "ISR personas morales", "fields": [ { "label": "INGRESOS NOMINALES DEL PERIODO", "value": 292500 }, { "label": "COEFICIENTE DE UTILIDAD", "value": 0.0219 }, { "label": "IMPUESTO A CARGO", "value": 7718.53 } ] } ], "receipt": { "concepts": [ { "concept": "ISR personas morales", "due": 7718.53, "amount_due": 7718.53, "amount_to_pay": 7718.53 }, { "concept": "IVA personas morales", "due": 16520, "amount_due": 16520, "amount_to_pay": 16520 } ], "total": 24238.53, "payment_line": "0426TEST2026090001", "valid_until": null }, "summary": { "obligations": [ { "obligation": "isr", "name": "ISR personas morales", "read": true, "amount_due": 7718.53, "amount_to_pay": 7718.53, "figures": { "income": 292500, "invoiced_income": 292500, "income_ytd": 2632500, "deductions": null, "profit_coefficient": 0.0219, "taxable_profit": 57651.75, "losses_applied": 0, "tax_base": 57651.75, "tax_caused": 17295.53, "prior_payments": 9577, "withheld": 0, "surcharges": null, "inflation": null, "paid_before": null, "due": 7718.53, "in_favor": null, "to_pay": 7718.53 } }, { "obligation": "vat", "name": "IVA personas morales", "read": false, "amount_due": 16520, "amount_to_pay": 16520, "figures": { "taxable_16": null, "taxable_0": null, "exempt": null, "charged": null, "paid_16": null, "creditable": null, "withheld": null, "balance_applied": null, "surcharges": null, "inflation": null, "paid_before": null, "due": null, "in_favor": null, "to_pay": null } } ], "total_to_pay": 24238.53 }, "files": { "pdf": true, "receipt": true, "financial_statements": false }, "files_error": null, "created_at": "2026-10-26T09:12:00-06:00", "updated_at": "2026-10-26T09:12:04-06:00" } ], "has_more": false, "url": "/v1/sat_declarations" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver el año mes por mes `GET https://api.fiscalisto.com/v1/sat_declarations/calendar` Cada mes del ejercicio con sus obligaciones: presentada (con sus declaraciones), falta (venció y no la encontramos) o por presentar, con el día en que vence (el 17 del mes siguiente, o el día hábil siguiente, más los días por el sexto dígito del RFC). Se arma con lo que trajeron sus consultas; no usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `year` | consulta | | El ejercicio, como 2026. Por omisión, el actual. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDeclarationCalendar = await fiscalisto.satDeclarations.calendar(); ``` ## Respuesta 200 Correcto. Un objeto `SatDeclarationCalendar`. Sus campos están en «Objetos» al final de este archivo (SatDeclarationCalendar). ```json { "object": "sat_declaration_calendar", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "year": 2026, "missing": 1, "months": [ { "month": 8, "due_on": "2026-09-18", "obligations": [ { "obligation": "isr", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1E" ] }, { "obligation": "vat", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1E" ] }, { "obligation": "isr_salaries", "status": "missing", "declarations": [] } ] }, { "month": 9, "due_on": "2026-10-19", "obligations": [ { "obligation": "isr", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1F" ] }, { "obligation": "vat", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1F" ] } ] }, { "month": null, "due_on": "2027-03-31", "obligations": [ { "obligation": "annual_return", "status": "due", "declarations": [] } ] } ] } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una declaración del SAT `GET https://api.fiscalisto.com/v1/sat_declarations/{id}` Una declaración presentada al SAT, con sus cifras en summary y si ya están su PDF y su acuse. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const satDeclaration = await fiscalisto.satDeclarations.get("dcl_OufpKqnrvx9p1F"); ``` ## Respuesta 200 Correcto. Un objeto `SatDeclaration`. Sus campos están en «Objetos» al final de este archivo (SatDeclaration). ```json { "id": "dcl_OufpKqnrvx9p1F", "object": "sat_declaration", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "source": "monthly", "year": 2026, "month": 9, "period": "Septiembre", "type": "normal", "complementary_type": null, "operation_number": "92026090000001", "payment_line": "0426TEST2026090001", "filed_on": "2026-10-15", "profit_coefficient": 0.0219, "isr_due": 7718.53, "vat_due": 16520, "total_due": 24238.53, "sections": [ { "tax": "ISR personas morales", "fields": [ { "label": "INGRESOS NOMINALES DEL PERIODO", "value": 292500 }, { "label": "COEFICIENTE DE UTILIDAD", "value": 0.0219 }, { "label": "IMPUESTO A CARGO", "value": 7718.53 } ] } ], "receipt": { "concepts": [ { "concept": "ISR personas morales", "due": 7718.53, "amount_due": 7718.53, "amount_to_pay": 7718.53 }, { "concept": "IVA personas morales", "due": 16520, "amount_due": 16520, "amount_to_pay": 16520 } ], "total": 24238.53, "payment_line": "0426TEST2026090001", "valid_until": null }, "summary": { "obligations": [ { "obligation": "isr", "name": "ISR personas morales", "read": true, "amount_due": 7718.53, "amount_to_pay": 7718.53, "figures": { "income": 292500, "invoiced_income": 292500, "income_ytd": 2632500, "deductions": null, "profit_coefficient": 0.0219, "taxable_profit": 57651.75, "losses_applied": 0, "tax_base": 57651.75, "tax_caused": 17295.53, "prior_payments": 9577, "withheld": 0, "surcharges": null, "inflation": null, "paid_before": null, "due": 7718.53, "in_favor": null, "to_pay": 7718.53 } }, { "obligation": "vat", "name": "IVA personas morales", "read": false, "amount_due": 16520, "amount_to_pay": 16520, "figures": { "taxable_16": null, "taxable_0": null, "exempt": null, "charged": null, "paid_16": null, "creditable": null, "withheld": null, "balance_applied": null, "surcharges": null, "inflation": null, "paid_before": null, "due": null, "in_favor": null, "to_pay": null } } ], "total_to_pay": 24238.53 }, "files": { "pdf": true, "receipt": true, "financial_statements": false }, "files_error": null, "created_at": "2026-10-26T09:12:00-06:00", "updated_at": "2026-10-26T09:12:04-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el PDF de una declaración `GET https://api.fiscalisto.com/v1/sat_declarations/{id}/pdf` La declaración como la da el SAT. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.satDeclarations.pdf("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el acuse de una declaración `GET https://api.fiscalisto.com/v1/sat_declarations/{id}/receipt` El acuse de recibo, con la línea de captura si hay algo a pagar. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.satDeclarations.receipt("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar los estados financieros de una anual `GET https://api.fiscalisto.com/v1/sat_declarations/{id}/financial_statements` El Excel de estados financieros de la anual de persona moral, como lo da el SAT. No usa timbres. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.xml", await fiscalisto.satDeclarations.financialStatements("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Registrar un ticket `POST https://api.fiscalisto.com/v1/tickets` Una venta que su comprador factura él mismo: con el código (code) y el total en el portal del emisor (url, que puede incrustar en su página con fiscalisto.js), da sus datos fiscales y la factura se timbra al momento con los timbres de su cuenta. Lo que nadie factura entra solo en la factura global del periodo. Registrar otra vez el mismo folio con el mismo total contesta el ticket que ya existe (los reintentos son seguros); con otro total, 409. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…) que vendió. Por omisión, su único emisor. | | `folio` | string | sí | El folio de la venta en su sistema, único por emisor: letras, números, - _ / y punto. Registrar el mismo folio con el mismo total responde el ticket que ya existe. (hasta 40 caracteres) | | `total` | number | sí | Lo que se cobró, impuestos incluidos y sin propina. (de -∞ a 10000000) | | `tip` | number | | Propina: no va en la factura. El cliente puede escribir el total con ella o sin ella. (de 0 a 10000000) | | `payment_form` | string | sí | c_FormaPago de la venta: 01 efectivo, 04 tarjeta de crédito… | | `sold_at` | string | | Cuándo se vendió: ISO 8601 (2026-10-02T13:45:00-06:00) o la hora de la Ciudad de México sin zona. Por omisión, ahora. (hasta 40 caracteres) | | `product` | string | | Un producto (prd_…) de su catálogo: el concepto de la factura. | | `description` | string | | El concepto. Con `product`, reemplaza su descripción; sin él, va con `product_key`. (hasta 1000 caracteres) | | `product_key` | string | | c_ClaveProdServ (8 dígitos), si no usa `product`. | | `unit_key` | string | | c_ClaveUnidad. Por omisión, la de la clave del producto. | | `iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA incluida en el total. Por omisión 0.16. | Ejemplo: ```json { "folio": "A1042", "total": 580, "payment_form": "04", "product_key": "90101501", "description": "Consumo de alimentos", "sold_at": "2026-10-24T14:32:00-06:00" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const ticket = await fiscalisto.tickets.create({ folio: "A1042", total: 580, payment_form: "04", product_key: "90101501", description: "Consumo de alimentos", sold_at: "2026-10-24T14:32:00-06:00", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Ticket`. Sus campos están en «Objetos» al final de este archivo (Ticket). ```json { "id": "tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "object": "ticket", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "folio": "A1042", "code": "A1042-K7QX", "status": "pending", "url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c?c=A1042-K7QX", "total": 580, "tip": 0, "payment_form": "04", "description": "Consumo de alimentos", "product_key": "90101501", "sold_at": "2026-10-24T14:32:00-06:00", "invoiceable_until": "2026-10-31", "invoice": null, "global_invoice": null, "customer": null, "created_at": "2026-10-24T14:32:05-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar tickets `GET https://api.fiscalisto.com/v1/tickets` Del más reciente al más antiguo por fecha de venta. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `status` | consulta | | pending, invoiced, global o cancelled. | | `sold_at_gte` | consulta | | Vendidos desde (ISO 8601 o AAAA-MM-DD). | | `sold_at_lte` | consulta | | Vendidos hasta (ISO 8601 o AAAA-MM-DD, el día completo). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.tickets.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `TicketList`. Sus campos están en «Objetos» al final de este archivo (TicketList). ```json { "object": "list", "data": [ { "id": "tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "object": "ticket", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "folio": "A1042", "code": "A1042-K7QX", "status": "pending", "url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c?c=A1042-K7QX", "total": 580, "tip": 0, "payment_form": "04", "description": "Consumo de alimentos", "product_key": "90101501", "sold_at": "2026-10-24T14:32:00-06:00", "invoiceable_until": "2026-10-31", "invoice": null, "global_invoice": null, "customer": null, "created_at": "2026-10-24T14:32:05-06:00" } ], "has_more": false, "url": "/v1/tickets" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un ticket `GET https://api.fiscalisto.com/v1/tickets/{id}` Un ticket de autofactura: si ya se facturó (invoice) o entró en la factura global (global_invoice). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const ticket = await fiscalisto.tickets.get("tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN"); ``` ## Respuesta 200 Correcto. Un objeto `Ticket`. Sus campos están en «Objetos» al final de este archivo (Ticket). ```json { "id": "tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "object": "ticket", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "folio": "A1042", "code": "A1042-K7QX", "status": "pending", "url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c?c=A1042-K7QX", "total": 580, "tip": 0, "payment_form": "04", "description": "Consumo de alimentos", "product_key": "90101501", "sold_at": "2026-10-24T14:32:00-06:00", "invoiceable_until": "2026-10-31", "invoice": null, "global_invoice": null, "customer": null, "created_at": "2026-10-24T14:32:05-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cancelar un ticket `DELETE https://api.fiscalisto.com/v1/tickets/{id}` Una venta que no se hizo (devolución, error): ya no se puede facturar ni entra en la factura global. Solo mientras esté pendiente. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const ticket = await fiscalisto.tickets.cancel("tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN"); ``` ## Respuesta 200 Correcto. Un objeto `Ticket`. Sus campos están en «Objetos» al final de este archivo (Ticket). ```json { "id": "tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "object": "ticket", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "folio": "A1042", "code": "A1042-K7QX", "status": "pending", "url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c?c=A1042-K7QX", "total": 580, "tip": 0, "payment_form": "04", "description": "Consumo de alimentos", "product_key": "90101501", "sold_at": "2026-10-24T14:32:00-06:00", "invoiceable_until": "2026-10-31", "invoice": null, "global_invoice": null, "customer": null, "created_at": "2026-10-24T14:32:05-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver el portal de autofactura de un emisor `GET https://api.fiscalisto.com/v1/issuers/{id}/self_invoicing` El portal de autofactura del emisor: su dirección, hasta cuándo se puede facturar un ticket y cómo se arma la factura global. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const selfInvoicing = await fiscalisto.issuers.getSelfInvoicing("id"); ``` ## Respuesta 200 Correcto. Un objeto `SelfInvoicing`. Sus campos están en «Objetos» al final de este archivo (SelfInvoicing). ```json { "object": "self_invoicing", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "portal_url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c", "window": "month", "window_days": 7, "global_periodicity": "04" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cambiar las reglas de autofactura `POST https://api.fiscalisto.com/v1/issuers/{id}/self_invoicing` Hasta cuándo se puede facturar un ticket y cada cuánto se timbra la factura global. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `window` | `month`, `days` | | Hasta cuándo se puede facturar un ticket: `month`, hasta el fin del mes de la venta (lo usual ante el SAT); `days`, `window_days` días después. | | `window_days` | integer | | Días para facturar con `window: days`. (de 1 a 365) | | `global_periodicity` | `01`, `02`, `03`, `04` | | c_Periodicidad de la factura global con los tickets que nadie facturó: 01 diaria, 02 semanal, 03 quincenal, 04 mensual. | Ejemplo: ```json { "window": "month", "global_periodicity": "04" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const selfInvoicing = await fiscalisto.issuers.updateSelfInvoicing("id", { window: "month", global_periodicity: "04" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `SelfInvoicing`. Sus campos están en «Objetos» al final de este archivo (SelfInvoicing). ```json { "object": "self_invoicing", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "portal_url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c", "window": "month", "window_days": 7, "global_periodicity": "04" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar las pólizas de un mes `GET https://api.fiscalisto.com/v1/accounting/journal_entries` Las pólizas de diario del mes, con sus cuentas y código agrupador del SAT: una por cada CFDI de ingreso o egreso vigente, emitido o recibido (los recibidos con la categoría de su gasto); una por cada pago de un complemento; una por cada recibo de nómina, y una con la depreciación de los activos fijos. Un PUE se registra cobrado o pagado según su forma de pago. En pesos; un CFDI en otra moneda sin tipo de cambio no se registra. No incluye movimientos de banco sin CFDI ni provisiones. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM). | | `limit` | consulta | | Cuántas por página, de 1 a 1,000. Por omisión 100. | | `starting_after` | consulta | | El id de la última póliza de la página anterior. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.accounting.journalEntries({ month: "2026-10" })) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `JournalEntryList`. Sus campos están en «Objetos» al final de este archivo (JournalEntryList). ```json { "object": "list", "data": [ { "id": "D-202610-00001", "object": "journal_entry", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "type": "invoice", "date": "2026-10-02", "description": "Factura A1042 INMOBILIARIA CVA", "invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "uuid": "5F3C1B2A-9D8E-4F7A-B6C5-1D2E3F4A5B6C", "tax_id": "ICV060329BY0", "total": 5800, "currency": "MXN", "exchange_rate": null, "lines": [ { "account": "105-01", "account_name": "Clientes nacionales", "grouping_code": "105.01", "debit": 5800, "credit": 0 }, { "account": "401-01", "account_name": "Ventas y/o servicios gravados a la tasa general", "grouping_code": "401.01", "debit": 0, "credit": 5000 }, { "account": "208-01", "account_name": "IVA trasladado cobrado", "grouping_code": "208.01", "debit": 0, "credit": 800 } ] } ], "has_more": false, "url": "/v1/accounting/journal_entries" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver la balanza de comprobación `GET https://api.fiscalisto.com/v1/accounting/trial_balance` Saldo inicial, cargos, abonos y saldo final de cada cuenta en el mes. Los saldos iniciales se acumulan desde el primer CFDI, o desde los saldos iniciales capturados en la app (las de resultados, desde enero), y los resultados de cada año anterior se cierran a capital. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM). | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const trialBalance = await fiscalisto.accounting.trialBalance({ month: "2026-10" }); ``` ## Respuesta 200 Correcto. Un objeto `TrialBalance`. Sus campos están en «Objetos» al final de este archivo (TrialBalance). ```json { "object": "trial_balance", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-10", "journal_entries": 1, "accounts": [ { "object": "ledger_account", "number": "105-01", "name": "Clientes nacionales", "grouping_code": "105.01", "nature": "debit", "opening_balance": 0, "debit": 5800, "credit": 0, "closing_balance": 5800 }, { "object": "ledger_account", "number": "208-01", "name": "IVA trasladado cobrado", "grouping_code": "208.01", "nature": "credit", "opening_balance": 0, "debit": 0, "credit": 800, "closing_balance": 800 }, { "object": "ledger_account", "number": "401-01", "name": "Ventas y/o servicios gravados a la tasa general", "grouping_code": "401.01", "nature": "credit", "opening_balance": 0, "debit": 0, "credit": 5000, "closing_balance": 5000 } ], "totals": { "debit": 5800, "credit": 5800 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver los estados financieros `GET https://api.fiscalisto.com/v1/accounting/financial_statements` El estado de resultados (del mes y del año hasta ese mes) y el balance general al cierre del mes, leídos de la misma balanza, así que siempre suman las mismas pólizas. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM). | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const financialStatements = await fiscalisto.accounting.financialStatements({ month: "2026-10" }); ``` ## Respuesta 200 Correcto. Un objeto `FinancialStatements`. Sus campos están en «Objetos» al final de este archivo (FinancialStatements). ```json { "object": "financial_statements", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-10", "income_statement": { "month": { "sections": [ { "key": "ingresos", "name": "Ingresos", "total": 5000, "groups": [ { "grouping_code": "401", "name": "Ingresos", "amount": 5000, "accounts": [ { "number": "401-01", "name": "Ventas y/o servicios gravados a la tasa general", "amount": 5000 } ] } ] }, { "key": "costos", "name": "Costo de ventas", "total": 0, "groups": [] }, { "key": "gastos", "name": "Gastos de operación", "total": 0, "groups": [] }, { "key": "financiamiento", "name": "Resultado integral de financiamiento", "total": 0, "groups": [] }, { "key": "otros", "name": "Otros ingresos y gastos", "total": 0, "groups": [] } ], "net_income": 5000 }, "year_to_date": { "sections": [ { "key": "ingresos", "name": "Ingresos", "total": 5000, "groups": [ { "grouping_code": "401", "name": "Ingresos", "amount": 5000, "accounts": [ { "number": "401-01", "name": "Ventas y/o servicios gravados a la tasa general", "amount": 5000 } ] } ] }, { "key": "costos", "name": "Costo de ventas", "total": 0, "groups": [] }, { "key": "gastos", "name": "Gastos de operación", "total": 0, "groups": [] }, { "key": "financiamiento", "name": "Resultado integral de financiamiento", "total": 0, "groups": [] }, { "key": "otros", "name": "Otros ingresos y gastos", "total": 0, "groups": [] } ], "net_income": 5000 } }, "balance_sheet": { "assets": { "total": 5800, "groups": [ { "grouping_code": "105", "name": "Clientes", "amount": 5800, "accounts": [ { "number": "105-01", "name": "Clientes nacionales", "amount": 5800 } ] } ] }, "liabilities": { "total": 800, "groups": [ { "grouping_code": "208", "name": "IVA trasladado cobrado", "amount": 800, "accounts": [ { "number": "208-01", "name": "IVA trasladado cobrado", "amount": 800 } ] } ] }, "equity": { "total": 0, "groups": [] }, "net_income_year_to_date": 5000, "liabilities_and_equity": 5800, "difference": 0 }, "journal_entries": 1, "opening_balances_date": null, "missing_invoices": 0 } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cruzar lo declarado con lo facturado `GET https://api.fiscalisto.com/v1/accounting/cross_checks` Cada mes ya vencido del año: el IVA trasladado, acreditable y retenido y los ingresos para ISR que el emisor declaró (de GET /v1/sat_declarations, después de POST /v1/sat_declarations/refresh) contra lo que dicen sus CFDI. Hasta $2 de diferencia es redondeo (las declaraciones van en pesos enteros). Una cifra que la declaración no trae en un renglón reconocible es unreadable con declared null, nunca 0. También el IVA a favor y el régimen con que factura. Gratis: no consulta al SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `year` | consulta | | El ejercicio. Por omisión, el actual. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const crossChecks = await fiscalisto.accounting.crossChecks(); ``` ## Respuesta 200 Correcto. Un objeto `CrossChecks`. Sus campos están en «Objetos» al final de este archivo (CrossChecks). ```json { "object": "cross_checks", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "year": 2026, "months": [ { "month": "2026-09", "status": "differs", "checks": [ { "key": "vat_charged", "status": "differs", "declared": 16000, "declared_without_cfdi": 0, "invoiced": 17600, "difference": -1600, "operation_number": "260930001234" }, { "key": "vat_creditable", "status": "matches", "declared": 4800, "declared_without_cfdi": 0, "invoiced": 4800.4, "difference": -0.4, "operation_number": "260930001234" }, { "key": "vat_withheld", "status": "matches", "declared": 0, "declared_without_cfdi": 0, "invoiced": 0, "difference": 0, "operation_number": "260930001234" }, { "key": "isr_income", "status": "unreadable", "declared": null, "declared_without_cfdi": 0, "invoiced": 110000, "difference": null, "operation_number": "260930001234" }, { "key": "isr_withheld_salaries", "status": "matches", "declared": 8450, "declared_without_cfdi": 0, "invoiced": 8449.6, "difference": 0.4, "operation_number": "260930001234" } ] } ], "vat_balance_in_favor": { "periods": [ { "month": "2026-02", "amount": 3000, "operation_number": "260317004567" } ], "total": 3000, "credited": 1200, "remaining": 1800 }, "tax_regimes": { "registered": [ "601" ], "invoiced": [ { "tax_regime": "601", "invoices": 214, "first_date": "2026-01-03", "last_date": "2026-10-24", "registered": true } ] } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver los CFDI de una cifra del cruce `GET https://api.fiscalisto.com/v1/accounting/cross_checks/cfdi` Los CFDI que forman lo facturado (invoiced) de una cifra del cruce de un mes, con los mismos cálculos de Impuestos: cada factura PUE o nota de crédito en su mes y cada factura PPD por lo que se pagó en el mes, con lo que aporta. La suma de cfdi más adjustment es invoiced. Sirve para ver por qué una cifra differs. Gratis: no consulta al SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM), cuya declaración ya venció. | | `key` | consulta | sí | La cifra: vat_charged, vat_creditable, vat_withheld, isr_income, isr_withheld_salaries, isr_withheld_suppliers o vat_withheld_suppliers. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const crossCheckCfdi = await fiscalisto.accounting.crossCheckCfdi({ month: "2026-09", key: "vat_creditable" }); ``` ## Respuesta 200 Correcto. Un objeto `CrossCheckCfdi`. Sus campos están en «Objetos» al final de este archivo (CrossCheckCfdi). ```json { "object": "cross_check_cfdi", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-09", "key": "vat_creditable", "status": "differs", "declared": 0, "declared_without_cfdi": 0, "invoiced": 152.98, "difference": -152.98, "operation_number": "261150269496", "cfdi": [ { "uuid": "5E2D6C1A-9B3F-4C7E-8A21-3F6B9D0C4E57", "type": "invoice", "date": "2026-09-12", "tax_id": "CNM980114PI2", "name": "CADENA COMERCIAL OXXO", "amount": 96.55 }, { "uuid": "A1F4C2B9-7D3E-4E8A-9C05-1B2D3E4F5A6B", "type": "payment", "date": "2026-09-25", "tax_id": "TME840315KT6", "name": "TELEFONOS DE MEXICO", "amount": 56.43 } ], "adjustment": 0 } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver el catálogo de cuentas `GET https://api.fiscalisto.com/v1/accounting/accounts` Las cuentas que usan las pólizas, con su código agrupador del SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const { data } = await fiscalisto.accounting.accounts(); ``` ## Respuesta 200 Correcto. Un objeto `LedgerAccountList`. Sus campos están en «Objetos» al final de este archivo (LedgerAccountList). ```json { "object": "list", "data": [ { "object": "ledger_account", "number": "105-01", "name": "Clientes nacionales", "grouping_code": "105.01", "nature": "debit" } ], "has_more": false, "url": "/v1/accounting/accounts" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar la contabilidad electrónica `GET https://api.fiscalisto.com/v1/accounting/electronic_accounting` El ZIP del mes para el Buzón Tributario: el catálogo (CT) y la balanza (BN, o BC si es complementaria), cada uno en su ZIP con el nombre que espera el SAT, en el formato 1.3 del Anexo 24. Con journal_request, también las pólizas (PL), que se entregan cuando el SAT las pide. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM). | | `type` | consulta | | normal (BN, por omisión) o complementary (BC, con modified_on). | | `modified_on` | consulta | | complementary: el día en que se modificó (AAAA-MM-DD). | | `journal_request` | consulta | | Para incluir las pólizas: AF acto de fiscalización, FC fiscalización compulsa (con order_number), DE devolución o CO compensación (con procedure_number). | | `order_number` | consulta | | AF y FC: el número de orden (ABC1234567/26). | | `procedure_number` | consulta | | DE y CO: el número de trámite (AB123456789012). | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("contabilidad.zip", await fiscalisto.accounting.electronicAccounting({ month: "2026-10" })); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver la DIOT de un mes `GET https://api.fiscalisto.com/v1/accounting/diot` Lo efectivamente pagado a cada proveedor en el mes (facturas PUE y complementos de pago recibidos), con sus notas de crédito como devoluciones, por tipo de operación. Sale de las facturas recibidas que trajo de las descargas del SAT. Desde enero de 2025 (formato 2025). ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM). | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const diot = await fiscalisto.accounting.diot({ month: "2026-10" }); ``` ## Respuesta 200 Correcto. Un objeto `Diot`. Sus campos están en «Objetos» al final de este archivo (Diot). ```json { "object": "diot", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-09", "proportion": 1, "lines": [ { "tax_id": "CACX7605101P8", "name": "XOCHILT CASAS CHAVEZ", "third_party_type": "04", "operation_type": "03", "paid": { "base_16": 12000, "base_8": 0, "base_0": 0, "exempt": 0, "not_taxable": 0, "vat": 1920 }, "returns": { "base_16": 0, "base_8": 0, "base_0": 0, "exempt": 0, "not_taxable": 0, "vat": 0 }, "blacklisted": false } ], "warnings": [] } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar la DIOT para el SAT `GET https://api.fiscalisto.com/v1/accounting/diot/file` El TXT de carga masiva de la DIOT 2025: 54 campos separados por |, pesos enteros. Con group_small_suppliers=true junta en una línea global a los proveedores chicos (regla 4.5.2 RMF). Un mes sin pagos a proveedores responde diot_empty: esa se presenta sin operaciones en el portal del SAT. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `month` | consulta | sí | El mes (AAAA-MM). | | `group_small_suppliers` | consulta | | true para agrupar proveedores de hasta $50,000 en el mes (regla 4.5.2). | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("diot.txt", await fiscalisto.accounting.diotFile({ month: "2026-10" })); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Conectar una tienda `POST https://api.fiscalisto.com/v1/store_connections` Shopify: con el dominio y las llaves de la app de la tienda (Dev Dashboard, permisos read_orders y write_orders) registra los webhooks y queda active. Mercado Libre: queda pending hasta que el vendedor autorice en authorize_url. Desde entonces cada pedido pagado es un ticket (folio SHOP-1001 o ML-…), que su comprador factura en el portal o entra en la factura global; un pedido cancelado o reembolsado completo cancela su ticket. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…) dueño de la tienda. Por omisión, su único emisor. | | `platform` | `shopify`, `mercadolibre` | sí | shopify o mercadolibre. | | `shop` | string | | Shopify: el dominio de la tienda (mi-tienda.myshopify.com). | | `client_id` | string | | Shopify: el Client ID de la app de la tienda (Dev Dashboard). (hasta 200 caracteres) | | `client_secret` | string | | Shopify: el Client secret de esa app; con él se firman los webhooks. Se guarda cifrado y nunca se muestra. (hasta 200 caracteres) | | `access_token` | string | | Shopify, solo apps personalizadas antiguas: su token de la Admin API (shpat_…); client_secret es entonces su API secret key. | | `return_url` | string (uri) | | Mercado Libre: a dónde volver después de autorizar (se agregan store_connection y status). (hasta 500 caracteres) | | `settings` | objeto | | Cómo se vuelve ticket cada pedido. | | `settings.product_key` | string | | c_ClaveProdServ de lo que vende (8 dígitos). | | `settings.unit_key` | string | | c_ClaveUnidad. Por omisión, la de la clave del producto. | | `settings.iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA incluida en los precios. Por omisión 0.16. | | `settings.description` | string | | El concepto de cada ticket. Por omisión, el pedido y sus artículos («Pedido 1001: Playera (2), Gorra»). (hasta 200 caracteres) | | `settings.folio_prefix` | string | | Lo que va antes del número del pedido en el folio del ticket. Por omisión SHOP- o ML-. | | `settings.payment_form` | string | | c_FormaPago cuando la forma de pago del pedido no está en payment_forms. Por omisión 04 (Shopify) o 31 (Mercado Libre). | | `settings.payment_forms` | objeto | | c_FormaPago por pasarela de Shopify (shopify_payments, paypal, manual…) o tipo de pago de Mercado Libre (credit_card, debit_card, account_money, ticket, bank_transfer). | | `settings.auto_invoice` | boolean | | Mercado Libre: timbrar en cuanto se paga si el comprador dejó sus datos fiscales completos. Por omisión true. | Ejemplo: ```json { "platform": "shopify", "shop": "mi-tienda.myshopify.com", "client_id": "8c1f0e7a2b3d4e5f6a7b8c9d0e1f2a3b", "client_secret": "shpss_0123456789abcdef0123456789abcdef", "settings": { "product_key": "53101600", "payment_forms": { "paypal": "31" } } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeConnection = await fiscalisto.storeConnections.create({ platform: "shopify", shop: "mi-tienda.myshopify.com", client_id: "8c1f0e7a2b3d4e5f6a7b8c9d0e1f2a3b", client_secret: "shpss_0123456789abcdef0123456789abcdef", settings: { product_key: "53101600", payment_forms: { paypal: "31" }, }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `StoreConnection`. Sus campos están en «Objetos» al final de este archivo (StoreConnection). ```json { "id": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "object": "store_connection", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "platform": "shopify", "shop": "mi-tienda.myshopify.com", "status": "active", "authorize_url": null, "settings": { "product_key": "53101600", "unit_key": null, "iva": "0.16", "description": null, "folio_prefix": "SHOP-", "payment_form": null, "payment_forms": { "paypal": "31" }, "auto_invoice": null }, "orders_count": 128, "last_order_at": "2026-10-25T10:12:44-06:00", "last_error": null, "created_at": "2026-10-01T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar tiendas `GET https://api.fiscalisto.com/v1/store_connections` Las más recientes primero, también las desconectadas. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.storeConnections.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `StoreConnectionList`. Sus campos están en «Objetos» al final de este archivo (StoreConnectionList). ```json { "object": "list", "data": [ { "id": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "object": "store_connection", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "platform": "shopify", "shop": "mi-tienda.myshopify.com", "status": "active", "authorize_url": null, "settings": { "product_key": "53101600", "unit_key": null, "iva": "0.16", "description": null, "folio_prefix": "SHOP-", "payment_form": null, "payment_forms": { "paypal": "31" }, "auto_invoice": null }, "orders_count": 128, "last_order_at": "2026-10-25T10:12:44-06:00", "last_error": null, "created_at": "2026-10-01T09:00:00-06:00" } ], "has_more": false, "url": "/v1/store_connections" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una tienda `GET https://api.fiscalisto.com/v1/store_connections/{id}` Una tienda conectada (Shopify, Mercado Libre o WooCommerce), su estado y lo último que importó. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeConnection = await fiscalisto.storeConnections.get("stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx"); ``` ## Respuesta 200 Correcto. Un objeto `StoreConnection`. Sus campos están en «Objetos» al final de este archivo (StoreConnection). ```json { "id": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "object": "store_connection", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "platform": "shopify", "shop": "mi-tienda.myshopify.com", "status": "active", "authorize_url": null, "settings": { "product_key": "53101600", "unit_key": null, "iva": "0.16", "description": null, "folio_prefix": "SHOP-", "payment_form": null, "payment_forms": { "paypal": "31" }, "auto_invoice": null }, "orders_count": 128, "last_order_at": "2026-10-25T10:12:44-06:00", "last_error": null, "created_at": "2026-10-01T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cambiar cómo se vuelven tickets sus pedidos `POST https://api.fiscalisto.com/v1/store_connections/{id}` Cuenta para los pedidos que lleguen; los tickets ya hechos no cambian. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `settings` | objeto | sí | Lo que cambia; lo demás queda. | | `settings.product_key` | string | | c_ClaveProdServ de lo que vende (8 dígitos). | | `settings.unit_key` | string | | c_ClaveUnidad. Por omisión, la de la clave del producto. | | `settings.iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA incluida en los precios. Por omisión 0.16. | | `settings.description` | string | | El concepto de cada ticket. Por omisión, el pedido y sus artículos («Pedido 1001: Playera (2), Gorra»). (hasta 200 caracteres) | | `settings.folio_prefix` | string | | Lo que va antes del número del pedido en el folio del ticket. Por omisión SHOP- o ML-. | | `settings.payment_form` | string | | c_FormaPago cuando la forma de pago del pedido no está en payment_forms. Por omisión 04 (Shopify) o 31 (Mercado Libre). | | `settings.payment_forms` | objeto | | c_FormaPago por pasarela de Shopify (shopify_payments, paypal, manual…) o tipo de pago de Mercado Libre (credit_card, debit_card, account_money, ticket, bank_transfer). | | `settings.auto_invoice` | boolean | | Mercado Libre: timbrar en cuanto se paga si el comprador dejó sus datos fiscales completos. Por omisión true. | Ejemplo: ```json { "settings": { "folio_prefix": "WEB-", "iva": "0.16" } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeConnection = await fiscalisto.storeConnections.update("stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", { settings: { folio_prefix: "WEB-", iva: "0.16" }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `StoreConnection`. Sus campos están en «Objetos» al final de este archivo (StoreConnection). ```json { "id": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "object": "store_connection", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "platform": "shopify", "shop": "mi-tienda.myshopify.com", "status": "active", "authorize_url": null, "settings": { "product_key": "53101600", "unit_key": null, "iva": "0.16", "description": null, "folio_prefix": "SHOP-", "payment_form": null, "payment_forms": { "paypal": "31" }, "auto_invoice": null }, "orders_count": 128, "last_order_at": "2026-10-25T10:12:44-06:00", "last_error": null, "created_at": "2026-10-01T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Desconectar una tienda `DELETE https://api.fiscalisto.com/v1/store_connections/{id}` Sus pedidos dejan de llegar y se borran sus llaves y webhooks. Los tickets que ya hizo siguen. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeConnection = await fiscalisto.storeConnections.delete("stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx"); ``` ## Respuesta 200 Correcto. Un objeto `StoreConnection`. Sus campos están en «Objetos» al final de este archivo (StoreConnection). ```json { "id": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "object": "store_connection", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "platform": "shopify", "shop": "mi-tienda.myshopify.com", "status": "active", "authorize_url": null, "settings": { "product_key": "53101600", "unit_key": null, "iva": "0.16", "description": null, "folio_prefix": "SHOP-", "payment_form": null, "payment_forms": { "paypal": "31" }, "auto_invoice": null }, "orders_count": 128, "last_order_at": "2026-10-25T10:12:44-06:00", "last_error": null, "created_at": "2026-10-01T09:00:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Traer pedidos pagados `POST https://api.fiscalisto.com/v1/store_connections/{id}/import_orders` Los pedidos pagados desde una fecha (hasta 31 días, 200 a la vez) que no llegaron por webhook: la tienda se conectó después o estuvo caída. Un pedido que ya es ticket no se repite. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `since` | string | | Desde qué día (AAAA-MM-DD, hasta 31 días atrás). Por omisión, hace 7 días. | Ejemplo: ```json { "since": "2026-10-18" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const storeImport = await fiscalisto.storeConnections.importOrders("id", { since: "2026-10-18" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `StoreImport`. Sus campos están en «Objetos» al final de este archivo (StoreImport). ```json { "object": "store_import", "livemode": false, "store_connection": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "since": "2026-10-18", "tickets": 14, "invoiced": 0, "skipped": [ { "order": "1042", "reason": "está en USD; solo se registran pedidos en pesos." } ] } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear una factura recurrente `POST https://api.fiscalisto.com/v1/recurring_invoices` Una factura que el emisor timbra solo en cada fecha (cada interval meses en day_of_month, o cada interval semanas o días), a la hora que elija, con los timbres de su cuenta, y la manda al cliente si send_email. Cada una es una factura normal (invoice.stamped, con recurring_invoice) y nunca se timbra dos veces el mismo periodo. invoice es el cuerpo de POST /v1/invoices: se revisa ahora, con las mismas reglas. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `name` | string | sí | Para reconocerla: «Renta oficina 3», «Plan Pro de ACME». (hasta 80 caracteres) | | `schedule` | objeto | sí | Cada cuánto y a qué hora se timbra. | | `schedule.frequency` | `monthly`, `weekly`, `daily` | sí | monthly: cada interval meses en day_of_month. weekly o daily: cada interval semanas o días desde start_date. | | `schedule.interval` | integer | | Cada cuántos meses, semanas o días. Por omisión 1. Hasta 24 meses, 52 semanas o 365 días. (de 1 a 365) | | `schedule.day_of_month` | integer o `"last"` | | Con monthly: el día, del 1 al 28, o "last" (el último del mes). Por omisión el día de start_date, o el de hoy. | | `schedule.start_date` | string | | La primera factura, AAAA-MM-DD (hoy o después). Por omisión: con monthly, el próximo day_of_month; con weekly o daily, hoy. | | `schedule.hour` | integer | | La hora (0 a 23) en que se timbra, en time_zone. Por omisión 7. (de 0 a 23) | | `schedule.time_zone` | `America/Mexico_City`, `America/Cancun`, `America/Mazatlan`, `America/Hermosillo`, `America/Tijuana`, `America/Ciudad_Juarez` | | La zona de esa hora. Por omisión America/Mexico_City. | | `invoice` | objeto | sí | La factura que se repite: el cuerpo de POST /v1/invoices, sin issuer, folio, date, global ni metadata. En description puede usar {mes}, {mes anterior}, {mes siguiente}, {año}, {año anterior}, {año siguiente} y {periodo}: cada factura los llena con su fecha. | | `invoice.issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `invoice.branch` | string (brn_…) | | Sucursal (brn_…) desde donde se factura: su código postal va en LugarExpedicion, su dirección en el PDF y, si las tiene, sus series. Sin ella, el domicilio fiscal del emisor (la Matriz). | | `invoice.warehouse` | string (whs_…) | | Almacén (whs_…) de donde salen sus productos con track_stock. Por omisión, el de su sucursal (o el de la Matriz). | | `invoice.customer` | string (cus_…) o objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `invoice.customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `invoice.customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `invoice.customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `invoice.customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `invoice.customer.email` | string o null | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `invoice.customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `invoice.customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `invoice.customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `invoice.customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `invoice.items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. (hasta 200) Sus campos, los mismos que `items` en «Aplicar un anticipo». | | `invoice.type` | `I`, `T` | | I ingreso (por omisión; con global, factura global) o T traslado. Notas de crédito: POST /v1/credit_notes. Pagos: POST /v1/payments. (por omisión "I") | | `invoice.carta_porte` | objeto | | Complemento Carta Porte 3.1 (traslado de mercancías). Sus campos, los mismos que `carta_porte` en «Aplicar un anticipo». | | `invoice.ine` | objeto | | Complemento INE (procesos electorales). | | `invoice.ine.process` | `Ordinario`, `Precampaña`, `Campaña` | sí | Tipo de proceso (TipoProceso): Ordinario, Precampaña o Campaña. | | `invoice.ine.committee` | `Ejecutivo Nacional`, `Ejecutivo Estatal`, `Directivo Estatal` | | Tipo de comité (TipoComite), con el proceso Ordinario. | | `invoice.ine.accounting_id` | string | | Clave de contabilidad (IdContabilidad), hasta 6 dígitos. | | `invoice.ine.entities` | arreglo de objeto | | Entidades (Entidad): clave del estado, ámbito y claves de contabilidad. (hasta 64) | | `invoice.ine.entities[].key` | string | sí | Clave de la entidad (ClaveEntidad), 3 letras. (3 caracteres) | | `invoice.ine.entities[].scope` | `Local`, `Federal` | | Ámbito (Ambito): Local o Federal. | | `invoice.ine.entities[].accounting_ids` | arreglo de string | | Claves de contabilidad (IdContabilidad). (hasta 50) | | `invoice.leyendas` | arreglo de objeto | | Complemento de leyendas fiscales. (hasta 10) | | `invoice.leyendas[].text` | string | sí | Texto de la leyenda (textoLeyenda). (hasta 1000 caracteres) | | `invoice.leyendas[].disposition` | string | | Disposición fiscal (disposicionFiscal), por ejemplo LISR. (hasta 30 caracteres) | | `invoice.leyendas[].norm` | string | | Norma o artículo (norma). (hasta 100 caracteres) | | `invoice.donation` | objeto | | Complemento de donatarias. | | `invoice.donation.in_kind` | boolean | | true si el donativo es en especie (va sin forma de pago en efectivo). | | `invoice.donation.deducted` | boolean | | true si el donativo es deducible para quien lo da. | | `invoice.local_taxes` | arreglo de objeto | | Impuestos locales (complemento implocal): traslados y retenciones estatales. (hasta 20) | | `invoice.local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `invoice.local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `invoice.local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de 0 a 100) | | `invoice.local_taxes[].amount` | number | sí | Importe del impuesto local. (de 0 a 1000000000000) | | `invoice.export` | `01`, `02`, `03`, `04` | | Exportación (Exportacion), c_Exportacion: 01 no aplica (por omisión), 02 definitiva… | | `invoice.comercio_exterior` | objeto | | Complemento de comercio exterior 2.0. Sus campos, los mismos que `comercio_exterior` en «Aplicar un anticipo». | | `invoice.global` | objeto | | Factura global a público en general (InformacionGlobal): periodicidad, meses y año. | | `invoice.global.periodicity` | string | sí | Periodicidad (Periodicidad), c_Periodicidad: 01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral. | | `invoice.global.months` | string | sí | Meses (Meses), c_Meses: 01 a 12, o 13 a 18 bimestres. | | `invoice.global.year` | integer | sí | Año (Año). (de 2022 a 2100) | | `invoice.use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `invoice.payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `invoice.payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `invoice.paid_later` | boolean | | Con PUE: true si aún no le pagan («Por cobrar» en la app) aunque se pagará este mes. | | `invoice.currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `invoice.series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `invoice.notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `invoice.pdf_language` | `es`, `en`, `bi` | | Idioma del PDF: es (por omisión) o en. | | `invoice.addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `invoice.related` | objeto | | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `invoice.related.relationship` | string | sí | Tipo de relación (TipoRelacion): 01 nota de crédito, 03 devolución, 04 sustitución, 07 anticipo… | | `invoice.related.uuids` | arreglo de string | sí | Folios fiscales relacionados. (hasta 20) | | `invoice.exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `invoice.folio` | integer | | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. (de 1 a 2000000000) | | `invoice.continue_sequence` | boolean | | Con un folio elegido: true para que los siguientes sigan desde ese número. | | `invoice.date` | string | | Fecha de emisión (Fecha) si no es ahora, AAAA-MM-DDTHH:MM:SS hora de la Ciudad de México: hasta 72 horas antes. | | `invoice.send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. (por omisión false) | | `invoice.email_cc` | string o null | | Correos con copia, separados por comas. | | `invoice.metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `send_email` | boolean | | Mandar cada factura al correo del cliente (customer.email o el del catálogo). Por omisión true. | | `catch_up` | `all`, `latest` | | Si quedaron periodos sin timbrar (sin timbres, en pausa con su fecha pasada): all los timbra todos, cada uno con su mes; latest solo el más reciente. Por omisión all. | Ejemplo: ```json { "name": "Renta oficina 3", "schedule": { "frequency": "monthly", "day_of_month": 1, "hour": 7 }, "invoice": { "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example" }, "payment_form": "03", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "unit_price": 15000 } ] } } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const recurringInvoice = await fiscalisto.recurringInvoices.create({ name: "Renta oficina 3", schedule: { frequency: "monthly", day_of_month: 1, hour: 7, }, invoice: { customer: { tax_id: "ICV060329BY0", legal_name: "INMOBILIARIA CVA", tax_system: "601", zip: "33826", email: "pagos@cva.example", }, payment_form: "03", items: [ { description: "Renta de oficina de {mes} {año}", product_key: "80131500", unit_price: 15000, }, ], }, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `RecurringInvoice`. Sus campos están en «Objetos» al final de este archivo (RecurringInvoice). ```json { "id": "rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", "object": "recurring_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Renta oficina 3", "status": "active", "schedule": { "frequency": "monthly", "interval": 1, "day_of_month": 1, "hour": 7, "time_zone": "America/Mexico_City" }, "next_invoice_on": "2026-11-01", "last_invoiced_on": "2026-10-01", "last_invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "last_error": null, "send_email": true, "catch_up": "all", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "quantity": 1, "unit_key": "E48", "unit_price": 15000 } ], "total": 17400, "created_at": "2026-09-28T11:02:14-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar facturas recurrentes `GET https://api.fiscalisto.com/v1/recurring_invoices` Las más recientes primero. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `status` | consulta | | active o paused. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.recurringInvoices.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `RecurringInvoiceList`. Sus campos están en «Objetos» al final de este archivo (RecurringInvoiceList). ```json { "object": "list", "data": [ { "id": "rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", "object": "recurring_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Renta oficina 3", "status": "active", "schedule": { "frequency": "monthly", "interval": 1, "day_of_month": 1, "hour": 7, "time_zone": "America/Mexico_City" }, "next_invoice_on": "2026-11-01", "last_invoiced_on": "2026-10-01", "last_invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "last_error": null, "send_email": true, "catch_up": "all", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "quantity": 1, "unit_key": "E48", "unit_price": 15000 } ], "total": 17400, "created_at": "2026-09-28T11:02:14-06:00" } ], "has_more": false, "url": "/v1/recurring_invoices" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una factura recurrente `GET https://api.fiscalisto.com/v1/recurring_invoices/{id}` Una factura recurrente: su calendario, la próxima fecha, la última factura que timbró y, si falló, por qué en last_error. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const recurringInvoice = await fiscalisto.recurringInvoices.get("rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx"); ``` ## Respuesta 200 Correcto. Un objeto `RecurringInvoice`. Sus campos están en «Objetos» al final de este archivo (RecurringInvoice). ```json { "id": "rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", "object": "recurring_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Renta oficina 3", "status": "active", "schedule": { "frequency": "monthly", "interval": 1, "day_of_month": 1, "hour": 7, "time_zone": "America/Mexico_City" }, "next_invoice_on": "2026-11-01", "last_invoiced_on": "2026-10-01", "last_invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "last_error": null, "send_email": true, "catch_up": "all", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "quantity": 1, "unit_key": "E48", "unit_price": 15000 } ], "total": 17400, "created_at": "2026-09-28T11:02:14-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cambiar o pausar una factura recurrente `POST https://api.fiscalisto.com/v1/recurring_invoices/{id}` Solo lo que mande cambia. invoice reemplaza la factura completa desde la siguiente vez. active: false la pausa; active: true la reanuda desde hoy, sin timbrar las fechas que pasaron en pausa. schedule.start_date mueve la siguiente fecha. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `name` | string | | Nombre. (hasta 80 caracteres) | | `schedule` | objeto | | Cada cuánto y a qué hora se timbra. | | `schedule.frequency` | `monthly`, `weekly`, `daily` | | monthly: cada interval meses en day_of_month. weekly o daily: cada interval semanas o días desde start_date. | | `schedule.interval` | integer | | Cada cuántos meses, semanas o días. Por omisión 1. Hasta 24 meses, 52 semanas o 365 días. (de 1 a 365) | | `schedule.day_of_month` | integer o `"last"` | | Con monthly: el día, del 1 al 28, o "last" (el último del mes). Por omisión el día de start_date, o el de hoy. | | `schedule.start_date` | string | | La primera factura, AAAA-MM-DD (hoy o después). Por omisión: con monthly, el próximo day_of_month; con weekly o daily, hoy. | | `schedule.hour` | integer | | La hora (0 a 23) en que se timbra, en time_zone. Por omisión 7. (de 0 a 23) | | `schedule.time_zone` | `America/Mexico_City`, `America/Cancun`, `America/Mazatlan`, `America/Hermosillo`, `America/Tijuana`, `America/Ciudad_Juarez` | | La zona de esa hora. Por omisión America/Mexico_City. | | `invoice` | objeto | | La factura completa que se repite desde la siguiente vez (reemplaza la anterior). | | `invoice.issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `invoice.branch` | string (brn_…) | | Sucursal (brn_…) desde donde se factura: su código postal va en LugarExpedicion, su dirección en el PDF y, si las tiene, sus series. Sin ella, el domicilio fiscal del emisor (la Matriz). | | `invoice.warehouse` | string (whs_…) | | Almacén (whs_…) de donde salen sus productos con track_stock. Por omisión, el de su sucursal (o el de la Matriz). | | `invoice.customer` | string (cus_…) o objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `invoice.customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `invoice.customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `invoice.customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `invoice.customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `invoice.customer.email` | string o null | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `invoice.customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `invoice.customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `invoice.customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `invoice.customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `invoice.items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. (hasta 200) Sus campos, los mismos que `items` en «Aplicar un anticipo». | | `invoice.type` | `I`, `T` | | I ingreso (por omisión; con global, factura global) o T traslado. Notas de crédito: POST /v1/credit_notes. Pagos: POST /v1/payments. (por omisión "I") | | `invoice.carta_porte` | objeto | | Complemento Carta Porte 3.1 (traslado de mercancías). Sus campos, los mismos que `carta_porte` en «Aplicar un anticipo». | | `invoice.ine` | objeto | | Complemento INE (procesos electorales). | | `invoice.ine.process` | `Ordinario`, `Precampaña`, `Campaña` | sí | Tipo de proceso (TipoProceso): Ordinario, Precampaña o Campaña. | | `invoice.ine.committee` | `Ejecutivo Nacional`, `Ejecutivo Estatal`, `Directivo Estatal` | | Tipo de comité (TipoComite), con el proceso Ordinario. | | `invoice.ine.accounting_id` | string | | Clave de contabilidad (IdContabilidad), hasta 6 dígitos. | | `invoice.ine.entities` | arreglo de objeto | | Entidades (Entidad): clave del estado, ámbito y claves de contabilidad. (hasta 64) | | `invoice.ine.entities[].key` | string | sí | Clave de la entidad (ClaveEntidad), 3 letras. (3 caracteres) | | `invoice.ine.entities[].scope` | `Local`, `Federal` | | Ámbito (Ambito): Local o Federal. | | `invoice.ine.entities[].accounting_ids` | arreglo de string | | Claves de contabilidad (IdContabilidad). (hasta 50) | | `invoice.leyendas` | arreglo de objeto | | Complemento de leyendas fiscales. (hasta 10) | | `invoice.leyendas[].text` | string | sí | Texto de la leyenda (textoLeyenda). (hasta 1000 caracteres) | | `invoice.leyendas[].disposition` | string | | Disposición fiscal (disposicionFiscal), por ejemplo LISR. (hasta 30 caracteres) | | `invoice.leyendas[].norm` | string | | Norma o artículo (norma). (hasta 100 caracteres) | | `invoice.donation` | objeto | | Complemento de donatarias. | | `invoice.donation.in_kind` | boolean | | true si el donativo es en especie (va sin forma de pago en efectivo). | | `invoice.donation.deducted` | boolean | | true si el donativo es deducible para quien lo da. | | `invoice.local_taxes` | arreglo de objeto | | Impuestos locales (complemento implocal): traslados y retenciones estatales. (hasta 20) | | `invoice.local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `invoice.local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `invoice.local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de 0 a 100) | | `invoice.local_taxes[].amount` | number | sí | Importe del impuesto local. (de 0 a 1000000000000) | | `invoice.export` | `01`, `02`, `03`, `04` | | Exportación (Exportacion), c_Exportacion: 01 no aplica (por omisión), 02 definitiva… | | `invoice.comercio_exterior` | objeto | | Complemento de comercio exterior 2.0. Sus campos, los mismos que `comercio_exterior` en «Aplicar un anticipo». | | `invoice.global` | objeto | | Factura global a público en general (InformacionGlobal): periodicidad, meses y año. | | `invoice.global.periodicity` | string | sí | Periodicidad (Periodicidad), c_Periodicidad: 01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral. | | `invoice.global.months` | string | sí | Meses (Meses), c_Meses: 01 a 12, o 13 a 18 bimestres. | | `invoice.global.year` | integer | sí | Año (Año). (de 2022 a 2100) | | `invoice.use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `invoice.payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `invoice.payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `invoice.paid_later` | boolean | | Con PUE: true si aún no le pagan («Por cobrar» en la app) aunque se pagará este mes. | | `invoice.currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `invoice.series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `invoice.notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `invoice.pdf_language` | `es`, `en`, `bi` | | Idioma del PDF: es (por omisión) o en. | | `invoice.addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `invoice.related` | objeto | | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `invoice.related.relationship` | string | sí | Tipo de relación (TipoRelacion): 01 nota de crédito, 03 devolución, 04 sustitución, 07 anticipo… | | `invoice.related.uuids` | arreglo de string | sí | Folios fiscales relacionados. (hasta 20) | | `invoice.exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `invoice.folio` | integer | | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. (de 1 a 2000000000) | | `invoice.continue_sequence` | boolean | | Con un folio elegido: true para que los siguientes sigan desde ese número. | | `invoice.date` | string | | Fecha de emisión (Fecha) si no es ahora, AAAA-MM-DDTHH:MM:SS hora de la Ciudad de México: hasta 72 horas antes. | | `invoice.send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. (por omisión false) | | `invoice.email_cc` | string o null | | Correos con copia, separados por comas. | | `invoice.metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. | | `catch_up` | `all`, `latest` | | Si quedaron periodos sin timbrar: all los timbra todos, cada uno con su mes; latest solo el más reciente. | | `active` | boolean | | false la pausa; true la reanuda desde hoy, sin timbrar las fechas que pasaron en pausa. | Ejemplo: ```json { "active": false } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const recurringInvoice = await fiscalisto.recurringInvoices.update("rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", { active: false }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `RecurringInvoice`. Sus campos están en «Objetos» al final de este archivo (RecurringInvoice). ```json { "id": "rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", "object": "recurring_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Renta oficina 3", "status": "active", "schedule": { "frequency": "monthly", "interval": 1, "day_of_month": 1, "hour": 7, "time_zone": "America/Mexico_City" }, "next_invoice_on": "2026-11-01", "last_invoiced_on": "2026-10-01", "last_invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "last_error": null, "send_email": true, "catch_up": "all", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "quantity": 1, "unit_key": "E48", "unit_price": 15000 } ], "total": 17400, "created_at": "2026-09-28T11:02:14-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar una factura recurrente `DELETE https://api.fiscalisto.com/v1/recurring_invoices/{id}` Ya no timbra más; las facturas que timbró se quedan. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.recurringInvoices.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Saltar la siguiente factura `POST https://api.fiscalisto.com/v1/recurring_invoices/{id}/skip` La siguiente fecha pasa a la que sigue, sin timbrar nada. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const recurringInvoice = await fiscalisto.recurringInvoices.skip("rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `RecurringInvoice`. Sus campos están en «Objetos» al final de este archivo (RecurringInvoice). ```json { "id": "rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", "object": "recurring_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Renta oficina 3", "status": "active", "schedule": { "frequency": "monthly", "interval": 1, "day_of_month": 1, "hour": 7, "time_zone": "America/Mexico_City" }, "next_invoice_on": "2026-11-01", "last_invoiced_on": "2026-10-01", "last_invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "last_error": null, "send_email": true, "catch_up": "all", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "quantity": 1, "unit_key": "E48", "unit_price": 15000 } ], "total": 17400, "created_at": "2026-09-28T11:02:14-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Timbrar ahora la que toca `POST https://api.fiscalisto.com/v1/recurring_invoices/{id}/run` Timbra el periodo que ya toca (next_invoice_on es hoy o antes) sin esperar su hora. Si no toca aún, 409 recurring_not_due. Si la hora llega mientras tanto, no se timbra dos veces: es el mismo periodo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.recurringInvoices.run("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear una cotización `POST https://api.fiscalisto.com/v1/quotes` Una cotización en borrador, con los conceptos y las condiciones de pago de una factura (se revisan con las mismas reglas, así que siempre se puede facturar). customer es un cliente de su catálogo, uno con sus datos fiscales (se agrega a sus clientes) o solo name y email si aún no los tiene. Se manda con POST /v1/quotes/{id}/send. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `issuer` | string | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `customer` | string (cus_…) o objeto | sí | Un cliente de su catálogo (cus_…), uno con sus datos fiscales (como en POST /v1/invoices: se agrega a sus clientes) o solo name y email si aún no los tiene. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `customer.email` | string | | Su correo (hasta 5, separados por comas). (hasta 500 caracteres) | | `customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `customer.name` | string | sí | A quién va dirigida. (hasta 200 caracteres) | | `items` | arreglo de objeto | sí | Los conceptos, como en POST /v1/invoices. (hasta 200) Sus campos, los mismos que `items` en «Aplicar un anticipo». | | `currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `valid_until` | string | | Hasta cuándo es válida (AAAA-MM-DD, hasta un año). Por omisión, en 15 días. | | `terms` | string | | Condiciones: tiempo de entrega, anticipo, garantía… (hasta 2000 caracteres) | | `auto_invoice` | boolean | | Al aceptarla, timbrar la factura sola (con los datos fiscales del cliente; si no los tiene, la página se los pide primero). Por omisión false: la factura se timbra con POST /v1/quotes/{id}/invoice. | Ejemplo: ```json { "customer": "cus_OufpKqnrvx9p42", "payment_form": "03", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "unit_price": 45000 } ], "terms": "50% de anticipo, entrega en 4 semanas.", "auto_invoice": true } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.create({ customer: "cus_OufpKqnrvx9p42", payment_form: "03", items: [ { description: "Desarrollo del sitio web", product_key: "81112103", unit_price: 45000, }, ], terms: "50% de anticipo, entrega en 4 semanas.", auto_invoice: true, }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `Quote`. Sus campos están en «Objetos» al final de este archivo (Quote). ```json { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar cotizaciones `GET https://api.fiscalisto.com/v1/quotes` Las más recientes primero, cada una en su última versión. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `issuer` | consulta | | El emisor (iss_…). Puede omitirse si la llave o la cuenta tienen uno solo. | | `status` | consulta | | Solo las que estén en este estado. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.quotes.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `QuoteList`. Sus campos están en «Objetos» al final de este archivo (QuoteList). ```json { "object": "list", "data": [ { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ], "has_more": false, "url": "/v1/quotes" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver una cotización `GET https://api.fiscalisto.com/v1/quotes/{id}` Su última versión. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.get("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx"); ``` ## Respuesta 200 Correcto. Un objeto `Quote`. Sus campos están en «Objetos» al final de este archivo (Quote). ```json { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cambiar una cotización `POST https://api.fiscalisto.com/v1/quotes/{id}` Solo lo que mande cambia. Un borrador cambia ahí mismo; una que ya se envió se vuelve una versión nueva en borrador (mismo número, version + 1) que el cliente ve al mandarla; mientras, su liga muestra la anterior. Una aceptada, rechazada o facturada ya no cambia. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `customer` | string (cus_…) o objeto | | Un cliente de su catálogo (cus_…), uno con sus datos fiscales (como en POST /v1/invoices: se agrega a sus clientes) o solo name y email si aún no los tiene. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `customer.email` | string | | Su correo (hasta 5, separados por comas). (hasta 500 caracteres) | | `customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `customer.name` | string | sí | A quién va dirigida. (hasta 200 caracteres) | | `items` | arreglo de objeto | | Los conceptos, como en POST /v1/invoices. (hasta 200) Sus campos, los mismos que `items` en «Aplicar un anticipo». | | `currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `valid_until` | string | | Hasta cuándo es válida (AAAA-MM-DD, hasta un año). Por omisión, en 15 días. | | `terms` | string | | Condiciones: tiempo de entrega, anticipo, garantía… (hasta 2000 caracteres) | Ejemplo: ```json { "valid_until": "2026-11-30" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.update("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", { valid_until: "2026-11-30" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Quote`. Sus campos están en «Objetos» al final de este archivo (Quote). ```json { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar una cotización `DELETE https://api.fiscalisto.com/v1/quotes/{id}` Solo una que nunca se envió; si no, 400 quote_not_draft. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.quotes.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Mandar una cotización `POST https://api.fiscalisto.com/v1/quotes/{id}/send` La manda por correo con su PDF y la liga donde el cliente la ve, la acepta, pide cambios o la rechaza. Con send_email: false solo se marca enviada y contesta url, para que usted la mande por WhatsApp o su propio correo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `send_email` | boolean | | Mandarla por correo, con su PDF y la liga (por omisión true). Con false solo se marca enviada y contesta url, para que usted la mande. | | `to` | string | | A quién (hasta 5 correos). Por omisión, el correo del cliente. (hasta 500 caracteres) | | `message` | string | | Un mensaje en el correo. (hasta 2000 caracteres) | | `auto_invoice` | boolean | | Al aceptarla, timbrar la factura sola (con los datos fiscales del cliente; si no los tiene, la página se los pide primero). Por omisión false: la factura se timbra con POST /v1/quotes/{id}/invoice. | Ejemplo: ```json { "to": "compras@cva.example", "message": "Le comparto la cotización del sitio." } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.send("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", { to: "compras@cva.example", message: "Le comparto la cotización del sitio.", }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Quote`. Sus campos están en «Objetos» al final de este archivo (Quote). ```json { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Marcar aceptada `POST https://api.fiscalisto.com/v1/quotes/{id}/accept` El cliente le dijo que sí por teléfono, en persona… Llega quote.accepted. No timbra: para eso, POST /v1/quotes/{id}/invoice. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const quote = await fiscalisto.quotes.accept("quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `Quote`. Sus campos están en «Objetos» al final de este archivo (Quote). ```json { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Facturar una cotización `POST https://api.fiscalisto.com/v1/quotes/{id}/invoice` Timbra la factura con los conceptos de la cotización y los datos fiscales del cliente, y la deja facturada (invoice). Si no estaba aceptada, la marca aceptada. Una en otra moneda va al tipo de cambio de hoy. Sin los datos fiscales del cliente, 400 quote_customer_missing; rechazada o vencida, 400 quote_not_invoiceable. Si ya se facturó, contesta esa factura. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | sí | Uno nuevo por comprobante (8 a 255 caracteres; su id de pedido sirve). Si la respuesta se pierde, repita la solicitud con el mismo valor: contesta lo mismo y nunca se timbra dos veces. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const invoice = await fiscalisto.quotes.invoice("inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Repetir la solicitud con la misma Idempotency-Key contesta lo mismo, con el mismo status y el encabezado idempotent-replayed: true. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Respuesta 202 El PAC no contestó a tiempo: el comprobante queda en processing y se timbra solo en unos momentos. Escuche el evento *.stamped o consúltelo con GET; repetir la solicitud con la misma Idempotency-Key contesta su estado sin timbrar otra vez. Un objeto `Invoice`. Sus campos están en «Objetos» al final de este archivo (Invoice). ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` ## Errores Puede responder 400, 401, 402, 403, 404, 409, 422, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Descargar el PDF `GET https://api.fiscalisto.com/v1/quotes/{id}/pdf` El PDF de su última versión, con el diseño de PDF del emisor. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { writeFile } from "node:fs/promises"; import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); await writeFile("factura.pdf", await fiscalisto.quotes.pdf("id")); ``` ## Respuesta 200 El archivo. ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Crear un webhook `POST https://api.fiscalisto.com/v1/webhook_endpoints` La respuesta trae el secreto de firma (secret) una sola vez. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `url` | string | sí | Dirección https de su servidor que recibe los avisos. (hasta 2048 caracteres) | | `events` | arreglo de string o null | | Eventos que quiere recibir, o ["*"] para todos (por omisión). La lista con cuándo llega cada uno: [Tipos de evento](https://fiscalisto.com/docs/api/webhooks#tipos-de-evento). | | `description` | string o null | | Para qué es, para usted (no se manda en los eventos). | | `api_version` | string o null | | La versión de la API en que llegan sus eventos (por ejemplo 2026-10-24); null o sin mandar: la de la cuenta. Así actualiza un endpoint a la vez. | Ejemplo: ```json { "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "events": [ "invoice.stamped", "invoice.failed", "invoice.canceled" ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookEndpoint = await fiscalisto.webhookEndpoints.create({ url: "https://mi-plataforma.mx/webhooks/fiscalisto", events: [ "invoice.stamped", "invoice.failed", "invoice.canceled", ], }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 201 Correcto. Un objeto `WebhookEndpoint`. Sus campos están en «Objetos» al final de este archivo (WebhookEndpoint). ```json { "id": "whe_0q1Vmmw52sbDpmtsKD1E", "object": "webhook_endpoint", "livemode": false, "sandbox": null, "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "description": null, "events": [ "invoice.stamped" ], "status": "enabled", "disabled_reason": null, "failing_since": null, "previous_secret_expires_at": null, "api_version": null, "created_at": "2026-10-24T10:15:42-06:00", "secret": "whsec_MfKQ9r7Cq1ZzR0nN3xY2bW8eT5uV6sA4" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar webhooks `GET https://api.fiscalisto.com/v1/webhook_endpoints` Los webhooks del modo de la llave. Hasta 16 por modo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.webhookEndpoints.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `WebhookEndpointList`. Sus campos están en «Objetos» al final de este archivo (WebhookEndpointList). ```json { "object": "list", "data": [ { "id": "whe_0q1Vmmw52sbDpmtsKD1E", "object": "webhook_endpoint", "livemode": false, "sandbox": null, "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "description": null, "events": [ "invoice.stamped" ], "status": "enabled", "disabled_reason": null, "failing_since": null, "previous_secret_expires_at": null, "api_version": null, "created_at": "2026-10-24T10:15:42-06:00", "secret": "whsec_MfKQ9r7Cq1ZzR0nN3xY2bW8eT5uV6sA4" } ], "has_more": false, "url": "/v1/webhook_endpoints" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un webhook `GET https://api.fiscalisto.com/v1/webhook_endpoints/{id}` Un webhook: su dirección, los eventos que recibe y si sus entregas fallan. El secreto solo viene al crearlo. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookEndpoint = await fiscalisto.webhookEndpoints.get("whe_0q1Vmmw52sbDpmtsKD1E"); ``` ## Respuesta 200 Correcto. Un objeto `WebhookEndpoint`. Sus campos están en «Objetos» al final de este archivo (WebhookEndpoint). ```json { "id": "whe_0q1Vmmw52sbDpmtsKD1E", "object": "webhook_endpoint", "livemode": false, "sandbox": null, "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "description": null, "events": [ "invoice.stamped" ], "status": "enabled", "disabled_reason": null, "failing_since": null, "previous_secret_expires_at": null, "api_version": null, "created_at": "2026-10-24T10:15:42-06:00", "secret": "whsec_MfKQ9r7Cq1ZzR0nN3xY2bW8eT5uV6sA4" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Actualizar un webhook `POST https://api.fiscalisto.com/v1/webhook_endpoints/{id}` Cambia su dirección, sus eventos, su descripción o su versión de la API; con disabled: false vuelve a encender uno que se apagó. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `url` | string | | Dirección https de su servidor que recibe los avisos. (hasta 2048 caracteres) | | `events` | arreglo de string o null | | Eventos que quiere recibir, o ["*"] para todos (por omisión). La lista con cuándo llega cada uno: [Tipos de evento](https://fiscalisto.com/docs/api/webhooks#tipos-de-evento). | | `description` | string o null | | Para qué es, para usted (no se manda en los eventos). | | `disabled` | boolean | | true para apagarlo sin borrarlo. | | `api_version` | string o null | | La versión de la API en que llegan sus eventos (por ejemplo 2026-10-24); null o sin mandar: la de la cuenta. Así actualiza un endpoint a la vez. | Ejemplo: ```json { "events": [ "*" ] } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookEndpoint = await fiscalisto.webhookEndpoints.update("whe_0q1Vmmw52sbDpmtsKD1E", { events: ["*"] }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `WebhookEndpoint`. Sus campos están en «Objetos» al final de este archivo (WebhookEndpoint). ```json { "id": "whe_0q1Vmmw52sbDpmtsKD1E", "object": "webhook_endpoint", "livemode": false, "sandbox": null, "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "description": null, "events": [ "invoice.stamped" ], "status": "enabled", "disabled_reason": null, "failing_since": null, "previous_secret_expires_at": null, "api_version": null, "created_at": "2026-10-24T10:15:42-06:00", "secret": "whsec_MfKQ9r7Cq1ZzR0nN3xY2bW8eT5uV6sA4" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Borrar un webhook `DELETE https://api.fiscalisto.com/v1/webhook_endpoints/{id}` Deja de mandar eventos a esa dirección. Los eventos siguen en GET /v1/events. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const deleted = await fiscalisto.webhookEndpoints.delete("prd_OufpKqnrvx9p1"); ``` ## Respuesta 200 Correcto. Un objeto `Deleted`. Sus campos están en «Objetos» al final de este archivo (Deleted). ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Cambiar el secreto `POST https://api.fiscalisto.com/v1/webhook_endpoints/{id}/roll_secret` Un secreto nuevo, que la respuesta trae una sola vez. El anterior sigue firmando (una segunda firma en webhook-signature) el tiempo que elija, 24 horas por omisión, para que cambie su servidor sin rechazar eventos. Si se filtró, mande previous_secret_expires_in: 0. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `previous_secret_expires_in` | integer | | Segundos que el secreto anterior sigue firmando, de 0 (deja de firmar ya: úselo si se filtró) a 86400. Por omisión 86400 (24 horas). (de 0 a 86400) | Ejemplo: ```json { "previous_secret_expires_in": 3600 } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookEndpoint = await fiscalisto.webhookEndpoints.rollSecret("whe_0q1Vmmw52sbDpmtsKD1E", { previous_secret_expires_in: 3600 }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `WebhookEndpoint`. Sus campos están en «Objetos» al final de este archivo (WebhookEndpoint). ```json { "id": "whe_0q1Vmmw52sbDpmtsKD1E", "object": "webhook_endpoint", "livemode": false, "sandbox": null, "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "description": null, "events": [ "invoice.stamped" ], "status": "enabled", "disabled_reason": null, "failing_since": null, "previous_secret_expires_at": null, "api_version": null, "created_at": "2026-10-24T10:15:42-06:00", "secret": "whsec_MfKQ9r7Cq1ZzR0nN3xY2bW8eT5uV6sA4" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Mandar un evento de prueba `POST https://api.fiscalisto.com/v1/webhook_endpoints/{id}/test` Manda ahora, una sola vez y sin reintentos, un evento del tipo que elija con un objeto de ejemplo (sus id no existen), firmado como los reales, aunque el webhook no escuche ese tipo. La respuesta trae lo que contestó su servidor. El evento no sale en GET /v1/events, pero sí en las entregas del webhook. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | | `Idempotency-Key` | encabezado | | Opcional (los SDK la mandan solos). Si la respuesta se pierde, repita la solicitud con el mismo valor durante 24 horas: contesta lo mismo, con idempotent-replayed: true, y no lo hace dos veces (ni crea, ni manda correos, ni cobra). | ## Cuerpo | Campo | Tipo | Obligatorio | Descripción | | --- | --- | --- | --- | | `type` | `invoice.stamped`, `invoice.failed`, `invoice.canceled`, `invoice.cancellation_pending`, `invoice.cancellation_rejected`, `invoice.paid`, `invoice.partially_paid`, `retention.stamped`, `retention.failed`, `retention.canceled`, `invoice_batch.completed`, `customer_data_request.completed`, `sat_download.completed`, `sat_download.failed`, `sat_declaration.created`, `sat_declaration.updated`, `ticket.invoiced`, `ticket.included_in_global`, `recurring_invoice.failed`, `quote.viewed`, `quote.accepted`, `quote.changes_requested`, `quote.rejected`, `issuer.ready`, `balance.low`, `test_clock.ready` | | El evento a mandar. Por omisión invoice.stamped. (por omisión "invoice.stamped") | Ejemplo: ```json { "type": "invoice.failed" } ``` ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const webhookTest = await fiscalisto.webhookEndpoints.test("id", { type: "invoice.failed" }, { idempotencyKey: "pedido-A-1042" }); ``` ## Respuesta 200 Correcto. Un objeto `WebhookTest`. Sus campos están en «Objetos» al final de este archivo (WebhookTest). ```json { "object": "webhook_test", "event": { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.stamped", "api_version": "2026-10-24", "livemode": false, "created_at": "2026-10-24T10:15:42-06:00", "data": { "object": { "id": "inv_OufpKqnrvx9p28SINJVjjWKYRhojBTJb8P", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "G03", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Consultoría", "product_key": "80101500", "unit_key": "E48", "unit_name": "Unidad de servicio", "quantity": 1, "unit_price": 1000, "taxability": "02", "iva": "0.16" } ], "subtotal": 1000, "discount": 0, "taxes": { "iva": 160, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 1160, "payment_status": "paid", "amount_due": 0, "amount_paid": 1160, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } } }, "delivery": { "ok": true, "status": 200, "error": null, "response_body": "{\"received\":true}", "duration_ms": 182 } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Listar eventos `GET https://api.fiscalisto.com/v1/events` Los de los últimos 30 días, los más recientes primero. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `limit` | consulta | | Cuántos por página, de 1 a 100. Por omisión 10. | | `starting_after` | consulta | | El id del último de la página anterior, para la siguiente. | | `type` | consulta | | Solo de este tipo (invoice.stamped), o todos los de un recurso con un comodín (invoice.*). | | `object` | consulta | | Solo los de este objeto (inv_…, ret_…, bat_…). | | `delivery_success` | consulta | | false: los que algún webhook no recibió (su último intento falló). true: los que recibieron todos los webhooks a los que se mandaron. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); for await (const item of fiscalisto.events.list()) { console.log(item.id); } ``` ## Respuesta 200 Correcto. Un objeto `EventList`. Sus campos están en «Objetos» al final de este archivo (EventList). ```json { "object": "list", "data": [ { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.stamped", "api_version": "2026-10-24", "livemode": false, "created_at": "2026-10-24T10:15:42-06:00", "data": { "object": { "id": "inv_OufpKqnrvx9p28SINJVjjWKYRhojBTJb8P", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "G03", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Consultoría", "product_key": "80101500", "unit_key": "E48", "unit_name": "Unidad de servicio", "quantity": 1, "unit_price": 1000, "taxability": "02", "iva": "0.16" } ], "subtotal": 1000, "discount": 0, "taxes": { "iva": 160, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 1160, "payment_status": "paid", "amount_due": 0, "amount_paid": 1160, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } } } ], "has_more": false, "url": "/v1/events" } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # Ver un evento `GET https://api.fiscalisto.com/v1/events/{id}` Un evento de los últimos 30 días, con el objeto como estaba cuando pasó. ## Parámetros | Nombre | Dónde | Obligatorio | Descripción | | --- | --- | --- | --- | | `id` | ruta | sí | El id del objeto. | ## Con el SDK de TypeScript ```ts import { Fiscalisto } from "fiscalisto"; // La llave viene de FISCALISTO_API_KEY. const fiscalisto = new Fiscalisto(); const event = await fiscalisto.events.get("evt_0PKhIdhYuAGwzmMQYfkc"); ``` ## Respuesta 200 Correcto. Un objeto `Event`. Sus campos están en «Objetos» al final de este archivo (Event). ```json { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.stamped", "api_version": "2026-10-24", "livemode": false, "created_at": "2026-10-24T10:15:42-06:00", "data": { "object": { "id": "inv_OufpKqnrvx9p28SINJVjjWKYRhojBTJb8P", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "G03", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Consultoría", "product_key": "80101500", "unit_key": "E48", "unit_name": "Unidad de servicio", "quantity": 1, "unit_price": 1000, "taxability": "02", "iva": "0.16" } ], "subtotal": 1000, "discount": 0, "taxes": { "iva": 160, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 1160, "payment_status": "paid", "amount_due": 0, "amount_paid": 1160, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } } } ``` ## Errores Puede responder 400, 401, 403, 404, 429, 500 con el cuerpo de error de siempre; cada código está en https://fiscalisto.com/docs/api/errores.md. --- # invoice.stamped Se timbró una factura, complemento de pago, nota de crédito, anticipo o recibo de nómina, en la misma solicitud o después de processing. data.object es el comprobante. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice.failed El SAT o el PAC rechazaron un comprobante. data.object trae status rejected y el motivo en error. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice.canceled Se canceló un comprobante: de inmediato o porque el cliente aceptó la cancelación (o no respondió en el plazo). Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice.cancellation_pending Se pidió la cancelación y espera la respuesta del cliente en su buzón tributario. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice.cancellation_rejected El cliente rechazó la cancelación o la solicitud venció. Puede pedirla otra vez. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice.paid Una factura de ingreso quedó pagada: su último complemento de pago, una nota de crédito, un depósito conciliado o POST /v1/invoices/{id}/mark_paid. data.object es la factura, con payment_status paid y amount_due 0. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice.partially_paid Llegó un pago por una parte de una factura PPD (un complemento de pago o una nota de crédito). data.object es la factura, con lo que falta en amount_due. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # retention.stamped Se timbró una constancia de retenciones. data.object es la constancia. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # retention.failed El SAT o el PAC rechazaron una constancia de retenciones. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # retention.canceled Se canceló una constancia de retenciones. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # invoice_batch.completed Ya no queda ninguna factura del lote por timbrar. data.object es el lote, con cuántas se timbraron y cuántas no. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # customer_data_request.completed Un cliente mandó sus datos fiscales por la liga de una solicitud. data.object es la solicitud, con lo que mandó en submission. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # sat_download.completed Terminó una descarga del SAT: sus facturas ya están en GET /v1/sat_invoices. data.object es la descarga, con cuántas nuevas trajo. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # sat_download.failed El SAT no pudo terminar una descarga (por ejemplo, la e.firma venció o el SAT la rechazó). data.object es la descarga, con el motivo en error. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # sat_declaration.created Una consulta encontró una declaración nueva del emisor en el SAT. data.object es la declaración; sus archivos y cifras pueden llegar después (sat_declaration.updated). Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # sat_declaration.updated Llegó el PDF, el acuse, los estados financieros o las cifras de una declaración. data.object es la declaración como queda. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # ticket.invoiced Su comprador facturó un ticket en el portal. data.object es el ticket, con la factura en invoice (también llega invoice.stamped). Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # ticket.included_in_global Un ticket que nadie facturó entró en la factura global del periodo. data.object es el ticket, con la factura global en global_invoice. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # recurring_invoice.failed Una factura recurrente no pudo timbrar su periodo (sin timbres, el cliente se borró del catálogo, el SAT la rechazó…). data.object es la factura recurrente, con el motivo en last_error. Se intenta de nuevo cada día a su hora; llega una vez hasta que vuelva a timbrar. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # quote.viewed El cliente abrió por primera vez la liga de una cotización enviada. data.object es la cotización. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # quote.accepted Se aceptó una cotización: el cliente en su liga (decided_by customer) o usted (user). Con auto_invoice y los datos fiscales del cliente, también llega invoice.stamped. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # quote.changes_requested El cliente pidió cambios en la liga de una cotización; lo que escribió viene en customer_comment. A lo más una vez cada 10 minutos. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # quote.rejected El cliente rechazó una cotización en su liga; su motivo, si lo dio, viene en customer_comment. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # issuer.ready El emisor ya puede timbrar en producción: tiene su CSD y firmó el contrato con el PAC (por la API, el panel o una liga de alta). data.object es el emisor. En pruebas llega al terminar una liga de alta. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # balance.low A la cuenta le quedan pocos timbres de producción: llega al bajar a low_balance_at (se elige en Desarrolladores › Timbres) y otra vez cuando se acaban, una vez cada uno hasta que vuelva a tener más. data.object es el saldo, como lo da GET /v1/balance. Siempre en producción. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # test_clock.ready Un reloj de prueba terminó de adelantarse (POST /v1/issuers/{id}/test_clock/advance): ya llegaron los eventos de lo que hizo en el camino. data.object es el reloj. Solo en pruebas. Llega a sus webhooks como POST firmado con Standard Webhooks (encabezados webhook-id, webhook-timestamp y webhook-signature). El cuerpo es un objeto `Event`; `data.object` es el comprobante como lo contesta su GET. Cómo verificar la firma y responder: https://fiscalisto.com/docs/api/webhooks.md. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | --- # Objetos Los objetos que contesta la API y los cuerpos que comparten varias rutas, cada uno con sus campos y un ejemplo. --- # Account La cuenta API de la llave que hizo la solicitud. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"account"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `name` | string | sí | El nombre de la cuenta API. | | `api_version` | string | sí | La versión de la API de la cuenta (la de sus solicitudes sin Fiscalisto-Version). Ver Versiones. | | `latest_api_version` | string | sí | La versión más reciente de la API. | | `key` | objeto | sí | La llave con que se hizo la solicitud: id, nombre y emisores a los que se limita. | | `key.id` | string | sí | Id de la llave. | | `key.name` | string | sí | Nombre de la llave. | | `key.issuers` | arreglo de string o null | sí | Emisores a los que se limita la llave; null si puede usar todos. | | `key.read_only` | boolean | sí | true si la llave solo consulta (key_read_only en lo demás). | | `key.expires_at` | string (date-time) o null | sí | Cuándo deja de funcionar: la vigencia elegida al crearla, o el fin del traslape al cambiarla. null si no vence. | | `key.sandbox` | string o null | sí | El entorno de prueba (sbx_…) de la llave: solo ve sus emisores, webhooks y eventos. null si es de la cuenta. | Ejemplo: ```json { "id": "org_39c5a6b2-02b3-8453-9c35-aba6b43c876a", "object": "account", "livemode": false, "name": "Mi plataforma", "api_version": "2026-10-24", "latest_api_version": "2026-10-24", "key": { "id": "key_vFQbeTxgHVoTlowHaTLR", "name": "Pruebas", "issuers": null, "read_only": false, "expires_at": null, "sandbox": null } } ``` --- # AdvanceList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].object` | `"advance"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].uuid` | string | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `data[].date` | string | sí | Fecha de emisión (Fecha), hora de la Ciudad de México. | | `data[].series` | string o null | sí | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. | | `data[].folio` | string o null | sí | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. | | `data[].customer` | objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `data[].customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].amount` | number | sí | El anticipo antes de impuestos. | | `data[].total` | number | sí | Total en la moneda del comprobante (Total). | | `data[].iva` | string | sí | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `data[].currency` | string | sí | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Balance Timbres de producción de la cuenta, por paquete, el que vence antes primero. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"balance"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `available` | integer | sí | Timbres disponibles en total. (de -9007199254740991 a 9007199254740991) | | `unlimited` | boolean | sí | true si la cuenta timbra sin usar timbres. | | `low_balance_at` | integer | sí | Con esta cantidad o menos llega balance.low (y otra vez al acabarse). Se elige en Desarrolladores › Timbres. (de -9007199254740991 a 9007199254740991) | | `auto_recharge` | objeto o null | sí | La recarga automática con la tarjeta guardada, o null si está apagada. | | `auto_recharge.timbres` | integer | sí | Timbres del paquete que se compra. (de -9007199254740991 a 9007199254740991) | | `auto_recharge.at` | integer | sí | Se compra con esta cantidad o menos. (de -9007199254740991 a 9007199254740991) | | `auto_recharge.max_per_month` | integer | sí | Como máximo tantas compras al mes. (de -9007199254740991 a 9007199254740991) | | `packs` | arreglo de objeto | sí | Los paquetes con timbres, el que vence antes primero (en ese orden se usan). | | `packs[].timbres` | integer | sí | Timbres del paquete. (de -9007199254740991 a 9007199254740991) | | `packs[].remaining` | integer | sí | Los que le quedan. (de -9007199254740991 a 9007199254740991) | | `packs[].purchased_at` | string (date-time) o null | sí | Cuándo se compró. | | `packs[].expires_at` | string (date-time) o null | sí | Cuándo vencen; null si no vencen. | Ejemplo: ```json { "object": "balance", "livemode": true, "available": 20, "unlimited": false, "low_balance_at": 100, "auto_recharge": null, "packs": [ { "timbres": 20, "remaining": 20, "purchased_at": "2026-10-24T10:15:42-06:00", "expires_at": null } ] } ``` --- # BankImport Los depósitos que se agregaron. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"bank_import"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `added` | integer | sí | Cuántos depósitos se agregaron. (de -9007199254740991 a 9007199254740991) | | `duplicates` | integer | sí | Cuántos ya estaban (se omitieron). (de -9007199254740991 a 9007199254740991) | | `data` | arreglo de objeto | sí | Los depósitos agregados. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"bank_transaction"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].date` | string | sí | El día del depósito (AAAA-MM-DD). | | `data[].amount` | number | sí | Lo depositado, en pesos. | | `data[].description` | string | sí | El concepto del banco. | | `data[].reference` | string o null | sí | La referencia o clave de rastreo, o null. | | `data[].account` | string o null | sí | Los últimos 4 dígitos de la cuenta, o null. | | `data[].payer_tax_id` | string o null | sí | El RFC de quien depositó, si se sabe. | | `data[].status` | `open`, `matched`, `classified` | sí | open (sin conciliar), matched (pagó facturas) o classified (no es de una factura). | | `data[].classification` | `venta`, `prestamo`, `traspaso`, `devolucion` o null | sí | classified: venta (aún sin facturar), prestamo, traspaso o devolucion; si no, null. | | `data[].note` | string o null | sí | Una nota al clasificarlo, o null. | | `data[].invoices` | arreglo de string | sí | Las facturas (inv_…) que pagó, si está conciliado. | | `data[].payment` | string o null | sí | El complemento de pago (inv_…) timbrado con él, o null. | | `data[].suggested_match` | objeto o null | sí | Si está abierto, las facturas que parece pagar (por monto, RFC, folio en el concepto y fecha): invoices, lo que toca a cada una (amounts), method, confidence (high o medium) y por qué (reasons). null si ninguna encaja. | | `data[].suggested_match.invoices` | arreglo de string | sí | Las facturas, como el cuerpo de POST /v1/invoices, con reference opcional. | | `data[].suggested_match.amounts` | objeto | sí | Lo que el depósito pagaría de cada factura (por inv_…). | | `data[].suggested_match.method` | `PPD`, `PUE` | sí | egreso (la factura completa y después una nota de crédito por el anticipo, opción A) o remanente (la factura solo por lo que falta, opción B). | | `data[].suggested_match.confidence` | `high`, `medium` | sí | high (el monto y además el RFC, el nombre o el folio coinciden) o medium (revíselo antes). | | `data[].suggested_match.reasons` | arreglo de string | sí | Por qué se sugiere, en palabras. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se mandó. | --- # BankTransaction Un depósito de la cuenta del emisor, y las facturas que pagó. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"bank_transaction"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor. | | `date` | string | sí | El día del depósito (AAAA-MM-DD). | | `amount` | number | sí | Lo depositado, en pesos. | | `description` | string | sí | El concepto del banco. | | `reference` | string o null | sí | La referencia o clave de rastreo, o null. | | `account` | string o null | sí | Los últimos 4 dígitos de la cuenta, o null. | | `payer_tax_id` | string o null | sí | El RFC de quien depositó, si se sabe. | | `status` | `open`, `matched`, `classified` | sí | open (sin conciliar), matched (pagó facturas) o classified (no es de una factura). | | `classification` | `venta`, `prestamo`, `traspaso`, `devolucion` o null | sí | classified: venta (aún sin facturar), prestamo, traspaso o devolucion; si no, null. | | `note` | string o null | sí | Una nota al clasificarlo, o null. | | `invoices` | arreglo de string | sí | Las facturas (inv_…) que pagó, si está conciliado. | | `payment` | string o null | sí | El complemento de pago (inv_…) timbrado con él, o null. | | `suggested_match` | objeto o null | sí | Si está abierto, las facturas que parece pagar (por monto, RFC, folio en el concepto y fecha): invoices, lo que toca a cada una (amounts), method, confidence (high o medium) y por qué (reasons). null si ninguna encaja. | | `suggested_match.invoices` | arreglo de string | sí | Las facturas, como el cuerpo de POST /v1/invoices, con reference opcional. | | `suggested_match.amounts` | objeto | sí | Lo que el depósito pagaría de cada factura (por inv_…). | | `suggested_match.method` | `PPD`, `PUE` | sí | egreso (la factura completa y después una nota de crédito por el anticipo, opción A) o remanente (la factura solo por lo que falta, opción B). | | `suggested_match.confidence` | `high`, `medium` | sí | high (el monto y además el RFC, el nombre o el folio coinciden) o medium (revíselo antes). | | `suggested_match.reasons` | arreglo de string | sí | Por qué se sugiere, en palabras. | | `created_at` | string (date-time) o null | sí | Cuándo se mandó. | Ejemplo: ```json { "id": "btx_OufpKqnrvx9p9Rt4sLm2XyT5uBcW8sKdLx", "object": "bank_transaction", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "date": "2026-10-24", "amount": 5800, "description": "SPEI RECIBIDO INMOBILIARIA CVA · ICV060329BY0", "reference": "F-1024", "account": "4821", "payer_tax_id": "ICV060329BY0", "status": "open", "classification": null, "note": null, "invoices": [], "payment": null, "suggested_match": { "invoices": [ "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN" ], "amounts": { "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN": 5800 }, "method": "PUE", "confidence": "high", "reasons": [ "Mismo monto", "RFC del cliente en el concepto" ] }, "created_at": "2026-10-25T09:00:00-06:00" } ``` --- # BankTransactionList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"bank_transaction"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].date` | string | sí | El día del depósito (AAAA-MM-DD). | | `data[].amount` | number | sí | Lo depositado, en pesos. | | `data[].description` | string | sí | El concepto del banco. | | `data[].reference` | string o null | sí | La referencia o clave de rastreo, o null. | | `data[].account` | string o null | sí | Los últimos 4 dígitos de la cuenta, o null. | | `data[].payer_tax_id` | string o null | sí | El RFC de quien depositó, si se sabe. | | `data[].status` | `open`, `matched`, `classified` | sí | open (sin conciliar), matched (pagó facturas) o classified (no es de una factura). | | `data[].classification` | `venta`, `prestamo`, `traspaso`, `devolucion` o null | sí | classified: venta (aún sin facturar), prestamo, traspaso o devolucion; si no, null. | | `data[].note` | string o null | sí | Una nota al clasificarlo, o null. | | `data[].invoices` | arreglo de string | sí | Las facturas (inv_…) que pagó, si está conciliado. | | `data[].payment` | string o null | sí | El complemento de pago (inv_…) timbrado con él, o null. | | `data[].suggested_match` | objeto o null | sí | Si está abierto, las facturas que parece pagar (por monto, RFC, folio en el concepto y fecha): invoices, lo que toca a cada una (amounts), method, confidence (high o medium) y por qué (reasons). null si ninguna encaja. | | `data[].suggested_match.invoices` | arreglo de string | sí | Las facturas, como el cuerpo de POST /v1/invoices, con reference opcional. | | `data[].suggested_match.amounts` | objeto | sí | Lo que el depósito pagaría de cada factura (por inv_…). | | `data[].suggested_match.method` | `PPD`, `PUE` | sí | egreso (la factura completa y después una nota de crédito por el anticipo, opción A) o remanente (la factura solo por lo que falta, opción B). | | `data[].suggested_match.confidence` | `high`, `medium` | sí | high (el monto y además el RFC, el nombre o el folio coinciden) o medium (revíselo antes). | | `data[].suggested_match.reasons` | arreglo de string | sí | Por qué se sugiere, en palabras. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se mandó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Branch Una sucursal del emisor: otro lugar desde donde factura, con su código postal (LugarExpedicion), su dirección en el PDF y sus series. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"branch"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor de la sucursal. | | `name` | string | sí | Cómo la llama su equipo; va en el PDF. | | `zip` | string | sí | Su código postal: el LugarExpedicion de sus CFDI. | | `address` | objeto | sí | Su dirección, en el PDF de sus facturas. | | `address.street` | string o null | sí | Calle (Calle). | | `address.exterior` | string o null | sí | Número exterior (NumeroExterior). | | `address.interior` | string o null | sí | Número interior (NumeroInterior). | | `address.neighborhood` | string o null | sí | Colonia. | | `address.city` | string o null | sí | Municipio o alcaldía. | | `address.state` | string o null | sí | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. | | `phone` | string o null | sí | Teléfono, en el PDF, o null. | | `email` | string o null | sí | Correo, en el PDF, o null. | | `series` | objeto | sí | La serie de cada tipo de CFDI que se timbra desde ella; null usa la del emisor. | | `series.invoice` | string o null | sí | Serie de sus facturas (tipo I); null, la del emisor. | | `series.credit_note` | string o null | sí | Serie de sus notas de crédito (tipo E); null, la del emisor. | | `series.payment` | string o null | sí | Serie de sus complementos de pago (tipo P); null, la del emisor. | | `series.transfer` | string o null | sí | Serie de sus traslados (tipo T); null, la del emisor. | | `archived` | boolean | sí | true si está archivada: ya no se factura desde ella. | | `created_at` | string (date-time) o null | sí | Cuándo se creó. | | `updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez. | Ejemplo: ```json { "id": "brn_OufpKqnrvx9p3", "object": "branch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Sucursal Monterrey", "zip": "64000", "address": { "street": "Av. Constitución", "exterior": "1500", "interior": null, "neighborhood": "Centro", "city": "Monterrey", "state": "Nuevo León" }, "phone": "81 1234 5678", "email": null, "series": { "invoice": "MTY", "credit_note": null, "payment": null, "transfer": null }, "archived": false, "created_at": "2026-10-27T09:12:00-06:00", "updated_at": "2026-10-27T09:12:00-06:00" } ``` --- # BranchList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"branch"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor de la sucursal. | | `data[].name` | string | sí | Cómo la llama su equipo; va en el PDF. | | `data[].zip` | string | sí | Su código postal: el LugarExpedicion de sus CFDI. | | `data[].address` | objeto | sí | Su dirección, en el PDF de sus facturas. | | `data[].address.street` | string o null | sí | Calle (Calle). | | `data[].address.exterior` | string o null | sí | Número exterior (NumeroExterior). | | `data[].address.interior` | string o null | sí | Número interior (NumeroInterior). | | `data[].address.neighborhood` | string o null | sí | Colonia. | | `data[].address.city` | string o null | sí | Municipio o alcaldía. | | `data[].address.state` | string o null | sí | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. | | `data[].phone` | string o null | sí | Teléfono, en el PDF, o null. | | `data[].email` | string o null | sí | Correo, en el PDF, o null. | | `data[].series` | objeto | sí | La serie de cada tipo de CFDI que se timbra desde ella; null usa la del emisor. | | `data[].series.invoice` | string o null | sí | Serie de sus facturas (tipo I); null, la del emisor. | | `data[].series.credit_note` | string o null | sí | Serie de sus notas de crédito (tipo E); null, la del emisor. | | `data[].series.payment` | string o null | sí | Serie de sus complementos de pago (tipo P); null, la del emisor. | | `data[].series.transfer` | string o null | sí | Serie de sus traslados (tipo T); null, la del emisor. | | `data[].archived` | boolean | sí | true si está archivada: ya no se factura desde ella. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó. | | `data[].updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # ComplianceOpinion La opinión de cumplimiento de obligaciones fiscales (32-D) más reciente del emisor, como la dio el SAT. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"compliance_opinion"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `result` | `positive`, `negative`, `no_obligations`, `suspended` o null | sí | positive, negative, no_obligations (inscrito sin obligaciones) o suspended (en suspensión de actividades); null si no se ha consultado. | | `date` | string o null | sí | El día que la emitió el SAT (AAAA-MM-DD), o null. | | `folio` | string o null | sí | Su folio, o null. | | `reasons` | arreglo de string | sí | Si es negativa, lo que el SAT marca como pendiente. | | `pdf` | boolean | sí | true si su PDF está en GET /v1/issuers/{id}/compliance_opinion/pdf. | | `checked_at` | string (date-time) o null | sí | Cuándo se consultó por última vez, o null. | | `last_error` | string o null | sí | Por qué falló la última consulta, o null. | Ejemplo: ```json { "object": "compliance_opinion", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "result": "positive", "date": "2026-10-27", "folio": "26NA20261027TEST", "reasons": [], "pdf": true, "checked_at": "2026-10-27T09:12:00-06:00", "last_error": null } ``` --- # CrossCheckCfdi Los CFDI que forman una cifra del cruce de un mes (invoiced): cada uno con lo que aporta, y el ajuste que Impuestos agrega encima. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"cross_check_cfdi"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `month` | string | sí | El mes (AAAA-MM). | | `key` | `vat_charged`, `vat_creditable`, `vat_withheld`, `isr_income`, `isr_withheld_salaries`, `isr_withheld_suppliers`, `vat_withheld_suppliers` | sí | La cifra: vat_charged (IVA trasladado), vat_creditable (IVA acreditable), vat_withheld (IVA que le retuvieron), isr_income (ingresos para ISR), isr_withheld_salaries (ISR retenido en sus recibos de nómina), isr_withheld_suppliers o vat_withheld_suppliers (ISR o IVA que retuvo a proveedores). | | `status` | `matches`, `differs`, `not_filed`, `unreadable` | sí | El cruce de esa cifra, como en GET /v1/accounting/cross_checks: matches, differs, not_filed o unreadable. | | `declared` | number o null | sí | Lo declarado, en pesos; null si no hay declaración o no se pudo leer. | | `declared_without_cfdi` | number | sí | De declared, lo que la propia declaración dice que no salió de facturas (ingresos nominales adicionales en isr_income, IVA al 16% sobre la precarga del SAT en vat_charged); 0 si no hay. | | `invoiced` | number | sí | Lo que dicen los CFDI del mes: la suma de cfdi más adjustment. | | `difference` | number o null | sí | declared menos declared_without_cfdi menos invoiced; null si declared es null. | | `operation_number` | string o null | sí | El número de operación de la declaración de donde salió la cifra. | | `cfdi` | arreglo de objeto | sí | Cada CFDI que suma en el mes, de mayor a menor: los PUE y las notas de crédito en su mes, las facturas PPD por lo que se pagó en el mes. | | `cfdi[].uuid` | string o null | sí | El folio fiscal (UUID) del CFDI; el de la factura PPD en un pago. | | `cfdi[].type` | `invoice`, `credit_note`, `payment`, `withholding_certificate` | sí | invoice (factura de ingreso, PUE), credit_note (nota de crédito: resta) o payment (lo pagado en el mes de una factura PPD). | | `cfdi[].date` | string o null | sí | La fecha del CFDI, o la del pago (AAAA-MM-DD). | | `cfdi[].tax_id` | string o null | sí | El RFC de la contraparte: el cliente en lo emitido, el proveedor en lo recibido. | | `cfdi[].name` | string o null | sí | El nombre de la contraparte, como viene en el CFDI. | | `cfdi[].amount` | number | sí | Lo que aporta a la cifra, en pesos; negativo en una nota de crédito. | | `adjustment` | number | sí | Lo que Impuestos agrega sobre los CFDI: en vat_creditable, la proporción de gastos comunes, el IVA retenido enterado del mes anterior y las importaciones; 0 en las demás. | Ejemplo: ```json { "object": "cross_check_cfdi", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-09", "key": "vat_creditable", "status": "differs", "declared": 0, "declared_without_cfdi": 0, "invoiced": 152.98, "difference": -152.98, "operation_number": "261150269496", "cfdi": [ { "uuid": "5E2D6C1A-9B3F-4C7E-8A21-3F6B9D0C4E57", "type": "invoice", "date": "2026-09-12", "tax_id": "CNM980114PI2", "name": "CADENA COMERCIAL OXXO", "amount": 96.55 }, { "uuid": "A1F4C2B9-7D3E-4E8A-9C05-1B2D3E4F5A6B", "type": "payment", "date": "2026-09-25", "tax_id": "TME840315KT6", "name": "TELEFONOS DE MEXICO", "amount": 56.43 } ], "adjustment": 0 } ``` --- # CrossChecks Lo que el emisor declaró cada mes contra lo que dicen sus CFDI, el IVA a favor y el régimen con que factura. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"cross_checks"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `year` | integer | sí | El ejercicio. (de -9007199254740991 a 9007199254740991) | | `months` | arreglo de objeto | sí | Cada mes cuya declaración mensual ya venció (el día 17 del mes siguiente), con sus cruces. Un mes con complementaria toma, por impuesto, la más reciente que lo trae. | | `months[].month` | string | sí | El mes (AAAA-MM). | | `months[].status` | `matches`, `differs`, `not_filed`, `unreadable` | sí | El peor de sus cruces: differs (no coincide), unreadable (una cifra no se pudo leer), not_filed (sin declaración mensual) o matches. | | `months[].checks` | arreglo de objeto | sí | Un cruce por cifra. | | `months[].checks[].key` | `vat_charged`, `vat_creditable`, `vat_withheld`, `isr_income`, `isr_withheld_salaries`, `isr_withheld_suppliers`, `vat_withheld_suppliers` | sí | Qué cifra: vat_charged (IVA trasladado), vat_creditable (IVA acreditable), vat_withheld (IVA que le retuvieron), isr_income (ingresos para ISR: nominales en persona moral, cobrados en los demás) y, solo en los meses en que retuvo, isr_withheld_salaries (ISR retenido por salarios y asimilados, contra sus recibos de nómina), isr_withheld_suppliers (ISR retenido por honorarios y arrendamiento, contra las facturas recibidas que pagó) o vat_withheld_suppliers (IVA retenido a proveedores). | | `months[].checks[].status` | `matches`, `differs`, `not_filed`, `unreadable` | sí | matches (hasta $2 de diferencia), differs, not_filed (no hay declaración del mes, o en una retención ninguna declaración del mes la trae) o unreadable (la declaración no trae la cifra en un renglón reconocible). | | `months[].checks[].declared` | number o null | sí | Lo declarado, en pesos; null si no hay declaración o no se pudo leer. | | `months[].checks[].declared_without_cfdi` | number | sí | De declared, lo que la propia declaración dice que no salió de facturas: en isr_income los ingresos nominales adicionales (cobrado sin factura, intereses) y en vat_charged el IVA al 16% por encima de la precarga del SAT. Se compara aparte; 0 si no hay. | | `months[].checks[].invoiced` | number | sí | Lo que dicen los CFDI del mes, con los mismos cálculos de Impuestos. | | `months[].checks[].difference` | number o null | sí | declared menos declared_without_cfdi menos invoiced; null si declared es null. | | `months[].checks[].operation_number` | string o null | sí | El número de operación de la declaración de donde salió la cifra. | | `vat_balance_in_favor` | objeto | sí | El IVA a favor que muestran las declaraciones del año y lo que meses posteriores acreditaron. Una estimación con las declaraciones leídas, no la cuenta del SAT. | | `vat_balance_in_favor.periods` | arreglo de objeto | sí | Cada mes con IVA a favor. | | `vat_balance_in_favor.periods[].month` | string | sí | El mes cuya declaración muestra el IVA a favor. | | `vat_balance_in_favor.periods[].amount` | number | sí | El IVA a favor de ese mes. | | `vat_balance_in_favor.periods[].operation_number` | string | sí | El número de operación de esa declaración. | | `vat_balance_in_favor.total` | number | sí | La suma del IVA a favor del año. | | `vat_balance_in_favor.credited` | number | sí | Lo que las declaraciones del año acreditaron de saldos a favor de periodos anteriores. | | `vat_balance_in_favor.remaining` | number | sí | total menos credited, nunca menos de 0: lo que quedaría por acreditar o pedir en devolución. | | `tax_regimes` | objeto | sí | Los regímenes del emisor (registered, de su constancia) y con cuáles facturó en el año. | | `tax_regimes.registered` | arreglo de string | sí | Los regímenes de su constancia (c_RegimenFiscal), capturados en la app. | | `tax_regimes.invoiced` | arreglo de objeto | sí | Cada régimen con que facturó en el año (RegimenFiscal del emisor). | | `tax_regimes.invoiced[].tax_regime` | string | sí | El régimen (c_RegimenFiscal). | | `tax_regimes.invoiced[].invoices` | integer | sí | Cuántos CFDI lo llevan (ingreso, egreso y pago, sin cancelados). (de -9007199254740991 a 9007199254740991) | | `tax_regimes.invoiced[].first_date` | string | sí | La fecha del primero (AAAA-MM-DD). | | `tax_regimes.invoiced[].last_date` | string | sí | La fecha del último (AAAA-MM-DD). | | `tax_regimes.invoiced[].registered` | boolean o null | sí | Si el régimen está en su constancia; null si no se capturaron los regímenes de la constancia. | Ejemplo: ```json { "object": "cross_checks", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "year": 2026, "months": [ { "month": "2026-09", "status": "differs", "checks": [ { "key": "vat_charged", "status": "differs", "declared": 16000, "declared_without_cfdi": 0, "invoiced": 17600, "difference": -1600, "operation_number": "260930001234" }, { "key": "vat_creditable", "status": "matches", "declared": 4800, "declared_without_cfdi": 0, "invoiced": 4800.4, "difference": -0.4, "operation_number": "260930001234" }, { "key": "vat_withheld", "status": "matches", "declared": 0, "declared_without_cfdi": 0, "invoiced": 0, "difference": 0, "operation_number": "260930001234" }, { "key": "isr_income", "status": "unreadable", "declared": null, "declared_without_cfdi": 0, "invoiced": 110000, "difference": null, "operation_number": "260930001234" }, { "key": "isr_withheld_salaries", "status": "matches", "declared": 8450, "declared_without_cfdi": 0, "invoiced": 8449.6, "difference": 0.4, "operation_number": "260930001234" } ] } ], "vat_balance_in_favor": { "periods": [ { "month": "2026-02", "amount": 3000, "operation_number": "260317004567" } ], "total": 3000, "credited": 1200, "remaining": 1800 }, "tax_regimes": { "registered": [ "601" ], "invoiced": [ { "tax_regime": "601", "invoices": 214, "first_date": "2026-01-03", "last_date": "2026-10-24", "registered": true } ] } } ``` --- # Customer Un cliente del catálogo de un emisor. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"customer"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `email` | string o null | sí | Correo del cliente (varios separados por comas), a donde van sus facturas. | | `use` | string o null | sí | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `foreign` | objeto o null | sí | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). | | `foreign.tax_id` | string o null | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). | | `ieps_taxpayer` | boolean | sí | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez, ISO 8601 (-06:00). | Ejemplo: ```json { "id": "cus_OufpKqnrvx9p1", "object": "customer", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "email": "pagos@cva.example", "use": "G03", "foreign": null, "ieps_taxpayer": false, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` --- # CustomerDataRequest Una liga para que un cliente dé sus datos fiscales. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"customer_data_request"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor que le va a facturar. | | `status` | `open`, `completed`, `expired`, `canceled` | sí | open (la liga espera), completed (el cliente mandó sus datos), expired (venció) o canceled. | | `url` | string o null | sí | La liga para su cliente, en fiscalisto.com. null cuando ya no está abierta. | | `reference` | string o null | sí | Su id para este cliente o pedido. | | `description` | string o null | sí | Lo que el cliente ve arriba del formulario. | | `expires_at` | string (date-time) o null | sí | Cuándo deja de servir la liga. | | `created_at` | string (date-time) o null | sí | Cuándo se creó. | | `submission` | objeto o null | sí | Lo que mandó el cliente (el último envío: puede corregirlo en las 24 horas siguientes). null mientras no conteste. | | `submission.tax_id` | string | sí | Su RFC. | | `submission.legal_name` | string | sí | Su nombre o razón social, como en su constancia. | | `submission.tax_system` | string | sí | Su régimen fiscal (c_RegimenFiscal). | | `submission.zip` | string | sí | El código postal de su domicilio fiscal. | | `submission.use` | string | sí | El uso del CFDI que eligió (c_UsoCFDI). | | `submission.email` | string o null | sí | Su correo para recibir facturas, si lo dio. | | `submission.source` | `constancia`, `manual` | sí | constancia (leído de su constancia o del SAT con su idCIF) o manual (escrito). | | `submission.customer` | string o null | sí | El cliente (cus_…) en el catálogo del emisor: el nuevo, o el que ya existía con ese RFC. | | `submission.applied` | boolean | sí | true si quedó en el catálogo. false si el cliente ya existía con otros datos: aplíquelos con POST /v1/customers/{id} si son correctos. | | `submission.submitted_at` | string (date-time) o null | sí | Cuándo los mandó. | Ejemplo: ```json { "id": "cdr_0q1Vmmw52sbD4cHPqLfBvX0AkFg3hX2Ys", "object": "customer_data_request", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "status": "completed", "url": null, "reference": "cliente-8812", "description": "Para facturar su suscripción de octubre.", "expires_at": "2026-12-23T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00", "submission": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826", "use": "G03", "email": "facturas@inmobiliariacva.mx", "source": "constancia", "customer": "cus_0q1Vmmw52sbD1", "applied": true, "submitted_at": "2026-10-24T11:02:41-06:00" } } ``` --- # CustomerDataRequestList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"customer_data_request"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor que le va a facturar. | | `data[].status` | `open`, `completed`, `expired`, `canceled` | sí | open (la liga espera), completed (el cliente mandó sus datos), expired (venció) o canceled. | | `data[].url` | string o null | sí | La liga para su cliente, en fiscalisto.com. null cuando ya no está abierta. | | `data[].reference` | string o null | sí | Su id para este cliente o pedido. | | `data[].description` | string o null | sí | Lo que el cliente ve arriba del formulario. | | `data[].expires_at` | string (date-time) o null | sí | Cuándo deja de servir la liga. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó. | | `data[].submission` | objeto o null | sí | Lo que mandó el cliente (el último envío: puede corregirlo en las 24 horas siguientes). null mientras no conteste. | | `data[].submission.tax_id` | string | sí | Su RFC. | | `data[].submission.legal_name` | string | sí | Su nombre o razón social, como en su constancia. | | `data[].submission.tax_system` | string | sí | Su régimen fiscal (c_RegimenFiscal). | | `data[].submission.zip` | string | sí | El código postal de su domicilio fiscal. | | `data[].submission.use` | string | sí | El uso del CFDI que eligió (c_UsoCFDI). | | `data[].submission.email` | string o null | sí | Su correo para recibir facturas, si lo dio. | | `data[].submission.source` | `constancia`, `manual` | sí | constancia (leído de su constancia o del SAT con su idCIF) o manual (escrito). | | `data[].submission.customer` | string o null | sí | El cliente (cus_…) en el catálogo del emisor: el nuevo, o el que ya existía con ese RFC. | | `data[].submission.applied` | boolean | sí | true si quedó en el catálogo. false si el cliente ya existía con otros datos: aplíquelos con POST /v1/customers/{id} si son correctos. | | `data[].submission.submitted_at` | string (date-time) o null | sí | Cuándo los mandó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # CustomerList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"customer"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `data[].zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `data[].email` | string o null | sí | Correo del cliente (varios separados por comas), a donde van sus facturas. | | `data[].use` | string o null | sí | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `data[].foreign` | objeto o null | sí | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `data[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). | | `data[].foreign.tax_id` | string o null | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). | | `data[].ieps_taxpayer` | boolean | sí | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `data[].created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez, ISO 8601 (-06:00). | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Deleted Confirmación de que se borró. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | string | sí | Qué tipo de objeto es («invoice», «customer»…). | | `deleted` | `true` | sí | Siempre true. | Ejemplo: ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "deleted": true } ``` --- # Diot La DIOT de un mes: lo pagado a cada proveedor, por tipo de operación. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"diot"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `month` | string | sí | El mes (AAAA-MM). | | `proportion` | number | sí | La proporción de IVA acreditable del mes para gastos comunes (1 si no la aplica). | | `lines` | arreglo de objeto | sí | Una línea por proveedor y tipo de operación, con lo pagado en el mes. | | `lines[].tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `lines[].name` | string o null | sí | Nombre. | | `lines[].third_party_type` | `15`, `04`, `05` | sí | Tipo de tercero: 04 nacional, 05 extranjero, 15 global. | | `lines[].operation_type` | `85`, `87`, `02`, `03`, `06`, `07` | sí | Tipo de operación: 02 enajenación de bienes, 03 servicios profesionales, 06 uso o goce temporal, 07 importación, 85 otros, 87 operaciones globales. | | `lines[].paid` | objeto | sí | Lo pagado en el mes por tasa, en pesos, con su IVA. | | `lines[].paid.base_16` | number | sí | Valor de los actos al 16 %. | | `lines[].paid.base_8` | number | sí | Valor de los actos al 8 % (región fronteriza). | | `lines[].paid.base_0` | number | sí | Valor de los actos al 0 %. | | `lines[].paid.exempt` | number | sí | Valor de los actos exentos. | | `lines[].paid.not_taxable` | number | sí | Valor de los actos no objeto de IVA. | | `lines[].paid.vat` | number | sí | El IVA de esos actos. | | `lines[].returns` | objeto | sí | Las devoluciones (notas de crédito) del mes, por tasa. | | `lines[].returns.base_16` | number | sí | Valor de los actos al 16 %. | | `lines[].returns.base_8` | number | sí | Valor de los actos al 8 % (región fronteriza). | | `lines[].returns.base_0` | number | sí | Valor de los actos al 0 %. | | `lines[].returns.exempt` | number | sí | Valor de las devoluciones exentas. | | `lines[].returns.not_taxable` | number | sí | Valor de los actos no objeto de IVA. | | `lines[].returns.vat` | number | sí | El IVA de esos actos. | | `lines[].blacklisted` | boolean | sí | El proveedor está en la lista 69-B definitiva: revise si deduce sus CFDI. | | `warnings` | arreglo de string | sí | Lo que conviene revisar antes de presentarla. | Ejemplo: ```json { "object": "diot", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-09", "proportion": 1, "lines": [ { "tax_id": "CACX7605101P8", "name": "XOCHILT CASAS CHAVEZ", "third_party_type": "04", "operation_type": "03", "paid": { "base_16": 12000, "base_8": 0, "base_0": 0, "exempt": 0, "not_taxable": 0, "vat": 1920 }, "returns": { "base_16": 0, "base_8": 0, "base_0": 0, "exempt": 0, "not_taxable": 0, "vat": 0 }, "blacklisted": false } ], "warnings": [] } ``` --- # Employee Un empleado de un emisor, para sus recibos de nómina. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"employee"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `number` | string | sí | Número de empleado (NumEmpleado). | | `name` | string | sí | Nombre completo del empleado. | | `tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `curp` | string | sí | CURP, 18 caracteres. | | `social_security_number` | string | sí | Número de seguridad social (NumSeguridadSocial), 11 dígitos. | | `zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `email` | string o null | sí | Correo del empleado, a donde van sus recibos. | | `start_date` | string | sí | Fecha de inicio de la relación laboral (FechaInicioRelLaboral), AAAA-MM-DD. | | `end_date` | string o null | sí | Último día trabajado si ya no trabaja ahí, AAAA-MM-DD. | | `daily_salary` | number | sí | Salario diario. | | `integrated_daily_salary` | number | sí | Salario diario integrado (SalarioDiarioIntegrado): no menor al salario diario. | | `contract_type` | string | sí | Tipo de contrato (TipoContrato), c_TipoContrato. Por omisión 01, por tiempo indeterminado. | | `workday_type` | string | sí | Tipo de jornada (TipoJornada), c_TipoJornada. Por omisión 01, diurna. | | `regime_type` | string | sí | Tipo de régimen (TipoRegimen): 02 sueldos y salarios. | | `periodicity` | string | sí | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `risk_class` | string | sí | Clase de riesgo del puesto ante el IMSS (RiesgoPuesto), 1 a 5. Por omisión 1. | | `state` | string | sí | Entidad federativa donde trabaja (ClaveEntFed): JAL, CMX, NLE… | | `border_zone` | boolean | sí | true si trabaja en la zona libre de la frontera norte (salario mínimo de frontera). | | `unionized` | boolean | sí | true si es sindicalizado (Sindicalizado). | | `department` | string o null | sí | Departamento (Departamento). | | `position` | string o null | sí | Puesto (Puesto). | | `warnings` | arreglo de string | sí | Lo que el SAT podría observar de sus datos, antes de timbrar. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | Ejemplo: ```json { "id": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "object": "employee", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "9", "name": "ANA GÓMEZ RUIZ", "tax_id": "GORA800101JK2", "curp": "GORA800101MDFMZN09", "social_security_number": "12345678903", "zip": "06000", "email": null, "start_date": "2024-03-04", "end_date": null, "daily_salary": 320, "integrated_daily_salary": 334.66, "contract_type": "01", "workday_type": "01", "regime_type": "02", "periodicity": "02", "risk_class": "1", "state": "CMX", "border_zone": false, "unionized": false, "department": null, "position": null, "warnings": [], "created_at": "2026-10-20T09:02:11-06:00" } ``` --- # EmployeeList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"employee"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].number` | string | sí | Número de empleado (NumEmpleado). | | `data[].name` | string | sí | Nombre completo del empleado. | | `data[].tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].curp` | string | sí | CURP, 18 caracteres. | | `data[].social_security_number` | string | sí | Número de seguridad social (NumSeguridadSocial), 11 dígitos. | | `data[].zip` | string | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `data[].email` | string o null | sí | Correo del empleado, a donde van sus recibos. | | `data[].start_date` | string | sí | Fecha de inicio de la relación laboral (FechaInicioRelLaboral), AAAA-MM-DD. | | `data[].end_date` | string o null | sí | Último día trabajado si ya no trabaja ahí, AAAA-MM-DD. | | `data[].daily_salary` | number | sí | Salario diario. | | `data[].integrated_daily_salary` | number | sí | Salario diario integrado (SalarioDiarioIntegrado): no menor al salario diario. | | `data[].contract_type` | string | sí | Tipo de contrato (TipoContrato), c_TipoContrato. Por omisión 01, por tiempo indeterminado. | | `data[].workday_type` | string | sí | Tipo de jornada (TipoJornada), c_TipoJornada. Por omisión 01, diurna. | | `data[].regime_type` | string | sí | Tipo de régimen (TipoRegimen): 02 sueldos y salarios. | | `data[].periodicity` | string | sí | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `data[].risk_class` | string | sí | Clase de riesgo del puesto ante el IMSS (RiesgoPuesto), 1 a 5. Por omisión 1. | | `data[].state` | string | sí | Entidad federativa donde trabaja (ClaveEntFed): JAL, CMX, NLE… | | `data[].border_zone` | boolean | sí | true si trabaja en la zona libre de la frontera norte (salario mínimo de frontera). | | `data[].unionized` | boolean | sí | true si es sindicalizado (Sindicalizado). | | `data[].department` | string o null | sí | Departamento (Departamento). | | `data[].position` | string o null | sí | Puesto (Puesto). | | `data[].warnings` | arreglo de string | sí | Lo que el SAT podría observar de sus datos, antes de timbrar. | | `data[].created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Error Todos los errores tienen esta forma. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `error` | objeto | sí | Por qué el SAT o el PAC no lo aceptaron: mensaje, código del SAT y texto original. | | `error.type` | `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `idempotency_error`, `rate_limit_error`, `invoice_error`, `balance_error`, `api_error` | sí | La familia del error: invalid_request_error, authentication_error, permission_error, not_found_error, idempotency_error, rate_limit_error, invoice_error, balance_error o api_error. | | `error.code` | string | sí | La razón precisa, estable (ver Errores). | | `error.message` | string | sí | Qué pasó y cómo corregirlo, para una persona. | | `error.param` | string | | El campo con el problema (items.0.unit_price). | | `error.sat_code` | string | | Código del SAT o del PAC (CFDI40xxx). | | `error.message_es` | string | | Con Accept-Language: en, el mensaje original en español cuando message solo dice en inglés qué significa el código. | | `error.doc_url` | string | sí | La explicación de este código en la documentación. | | `error.request_id` | string | sí | El id de la solicitud (req_…). Inclúyalo si nos escribe. | Ejemplo: ```json { "error": { "type": "invoice_error", "code": "invoice_rejected", "message": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital.", "param": "customer", "sat_code": "CFDI40145", "invoice": "inv_roVZuc6krJsw08oPyaiBiYX4fWH2pyEN1x", "doc_url": "https://fiscalisto.com/docs/api/errores#invoice_rejected", "request_id": "req_FfCT3zz9ktKinNLSGMqu" } } ``` --- # Event Algo que pasó en su cuenta: el objeto completo como quedó. Se manda a sus webhooks firmado (Standard Webhooks). | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `api_version` | string | sí | Versión de la API con que se armó el objeto. | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | Ejemplo: ```json { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.stamped", "api_version": "2026-10-24", "livemode": false, "created_at": "2026-10-24T10:15:42-06:00", "data": { "object": { "id": "inv_OufpKqnrvx9p28SINJVjjWKYRhojBTJb8P", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "G03", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Consultoría", "product_key": "80101500", "unit_key": "E48", "unit_name": "Unidad de servicio", "quantity": 1, "unit_price": 1000, "taxability": "02", "iva": "0.16" } ], "subtotal": 1000, "discount": 0, "taxes": { "iva": 160, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 1160, "payment_status": "paid", "amount_due": 0, "amount_paid": 1160, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } } } ``` --- # EventList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `data[].api_version` | string | sí | Versión de la API con que se armó el objeto. | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `data[].data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # FinancialStatements El estado de resultados y el balance general de un mes, leídos de su balanza. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"financial_statements"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `month` | string | sí | El mes (AAAA-MM). | | `income_statement` | objeto | sí | El estado de resultados del mes (month) y del año hasta ese mes (year_to_date). Cada sección suma en positivo lo que aumenta la utilidad (ingresos) y en negativo lo que la reduce (costos y gastos). | | `income_statement.month` | objeto | sí | El estado de resultados del mes. | | `income_statement.month.sections` | arreglo de objeto | sí | Ingresos, costos, gastos, financiamiento y otros, cada uno con su total. | | `income_statement.month.sections[].key` | `ingresos`, `costos`, `gastos`, `financiamiento`, `otros` | sí | El archivo .key en base64 (puede traer el prefijo data:). | | `income_statement.month.sections[].name` | string | sí | Nombre. | | `income_statement.month.sections[].total` | number | sí | Total en la moneda del comprobante (Total). | | `income_statement.month.sections[].groups` | arreglo de objeto | sí | Su monto por el primer nivel del código agrupador. | | `income_statement.month.sections[].groups[].grouping_code` | string | sí | El primer nivel del código agrupador (401, 601…). | | `income_statement.month.sections[].groups[].name` | string | sí | Nombre. | | `income_statement.month.sections[].groups[].amount` | number | sí | Importe. | | `income_statement.month.sections[].groups[].accounts` | arreglo de objeto | sí | Las cuentas del grupo con su monto. | | `income_statement.month.sections[].groups[].accounts[].number` | string | sí | Número de empleado (NumEmpleado). | | `income_statement.month.sections[].groups[].accounts[].name` | string | sí | Nombre. | | `income_statement.month.sections[].groups[].accounts[].amount` | number | sí | Importe. | | `income_statement.month.net_income` | number | sí | La utilidad (o pérdida, negativa) antes de impuestos del mes. | | `income_statement.year_to_date` | objeto | sí | El estado de resultados de enero al mes. | | `income_statement.year_to_date.sections` | arreglo de objeto | sí | Ingresos, costos, gastos, financiamiento y otros, cada uno con su total. | | `income_statement.year_to_date.sections[].key` | `ingresos`, `costos`, `gastos`, `financiamiento`, `otros` | sí | El archivo .key en base64 (puede traer el prefijo data:). | | `income_statement.year_to_date.sections[].name` | string | sí | Nombre. | | `income_statement.year_to_date.sections[].total` | number | sí | Total en la moneda del comprobante (Total). | | `income_statement.year_to_date.sections[].groups` | arreglo de objeto | sí | Su monto por el primer nivel del código agrupador. | | `income_statement.year_to_date.sections[].groups[].grouping_code` | string | sí | El primer nivel del código agrupador (401, 601…). | | `income_statement.year_to_date.sections[].groups[].name` | string | sí | Nombre. | | `income_statement.year_to_date.sections[].groups[].amount` | number | sí | Importe. | | `income_statement.year_to_date.sections[].groups[].accounts` | arreglo de objeto | sí | Las cuentas del grupo con su monto. | | `income_statement.year_to_date.sections[].groups[].accounts[].number` | string | sí | Número de empleado (NumEmpleado). | | `income_statement.year_to_date.sections[].groups[].accounts[].name` | string | sí | Nombre. | | `income_statement.year_to_date.sections[].groups[].accounts[].amount` | number | sí | Importe. | | `income_statement.year_to_date.net_income` | number | sí | La utilidad (o pérdida, negativa) antes de impuestos de enero al mes. | | `balance_sheet` | objeto | sí | El balance general al cierre del mes, agrupado por el primer nivel del código agrupador. difference es activo menos pasivo y capital: 0 cuando los libros cuadran. | | `balance_sheet.assets` | objeto | sí | El activo: su total y sus grupos. | | `balance_sheet.assets.total` | number | sí | Total en la moneda del comprobante (Total). | | `balance_sheet.assets.groups` | arreglo de objeto | sí | Su monto por el primer nivel del código agrupador. | | `balance_sheet.assets.groups[].grouping_code` | string | sí | El primer nivel del código agrupador (401, 601…). | | `balance_sheet.assets.groups[].name` | string | sí | Nombre. | | `balance_sheet.assets.groups[].amount` | number | sí | Importe. | | `balance_sheet.assets.groups[].accounts` | arreglo de objeto | sí | Las cuentas del grupo con su monto. | | `balance_sheet.assets.groups[].accounts[].number` | string | sí | Número de empleado (NumEmpleado). | | `balance_sheet.assets.groups[].accounts[].name` | string | sí | Nombre. | | `balance_sheet.assets.groups[].accounts[].amount` | number | sí | Importe. | | `balance_sheet.liabilities` | objeto | sí | El pasivo: su total y sus grupos. | | `balance_sheet.liabilities.total` | number | sí | Total en la moneda del comprobante (Total). | | `balance_sheet.liabilities.groups` | arreglo de objeto | sí | Su monto por el primer nivel del código agrupador. | | `balance_sheet.liabilities.groups[].grouping_code` | string | sí | El primer nivel del código agrupador (401, 601…). | | `balance_sheet.liabilities.groups[].name` | string | sí | Nombre. | | `balance_sheet.liabilities.groups[].amount` | number | sí | Importe. | | `balance_sheet.liabilities.groups[].accounts` | arreglo de objeto | sí | Las cuentas del grupo con su monto. | | `balance_sheet.liabilities.groups[].accounts[].number` | string | sí | Número de empleado (NumEmpleado). | | `balance_sheet.liabilities.groups[].accounts[].name` | string | sí | Nombre. | | `balance_sheet.liabilities.groups[].accounts[].amount` | number | sí | Importe. | | `balance_sheet.equity` | objeto | sí | El capital contable sin el resultado del ejercicio: su total y sus grupos. | | `balance_sheet.equity.total` | number | sí | Total en la moneda del comprobante (Total). | | `balance_sheet.equity.groups` | arreglo de objeto | sí | Su monto por el primer nivel del código agrupador. | | `balance_sheet.equity.groups[].grouping_code` | string | sí | El primer nivel del código agrupador (401, 601…). | | `balance_sheet.equity.groups[].name` | string | sí | Nombre. | | `balance_sheet.equity.groups[].amount` | number | sí | Importe. | | `balance_sheet.equity.groups[].accounts` | arreglo de objeto | sí | Las cuentas del grupo con su monto. | | `balance_sheet.equity.groups[].accounts[].number` | string | sí | Número de empleado (NumEmpleado). | | `balance_sheet.equity.groups[].accounts[].name` | string | sí | Nombre. | | `balance_sheet.equity.groups[].accounts[].amount` | number | sí | Importe. | | `balance_sheet.net_income_year_to_date` | number | sí | El resultado del ejercicio hasta el mes, parte del capital. | | `balance_sheet.liabilities_and_equity` | number | sí | Pasivo más capital más el resultado del ejercicio. | | `balance_sheet.difference` | number | sí | Activo menos pasivo y capital: 0 cuando los libros cuadran. | | `journal_entries` | integer | sí | Cuántas pólizas tuvo el mes. (de -9007199254740991 a 9007199254740991) | | `opening_balances_date` | string o null | sí | Desde qué fecha cuentan los saldos iniciales capturados en la app; null si no hay. | | `missing_invoices` | integer | sí | Pagos que se refieren a facturas que no están en Fiscalisto y no se registraron. (de -9007199254740991 a 9007199254740991) | Ejemplo: ```json { "object": "financial_statements", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-10", "income_statement": { "month": { "sections": [ { "key": "ingresos", "name": "Ingresos", "total": 5000, "groups": [ { "grouping_code": "401", "name": "Ingresos", "amount": 5000, "accounts": [ { "number": "401-01", "name": "Ventas y/o servicios gravados a la tasa general", "amount": 5000 } ] } ] }, { "key": "costos", "name": "Costo de ventas", "total": 0, "groups": [] }, { "key": "gastos", "name": "Gastos de operación", "total": 0, "groups": [] }, { "key": "financiamiento", "name": "Resultado integral de financiamiento", "total": 0, "groups": [] }, { "key": "otros", "name": "Otros ingresos y gastos", "total": 0, "groups": [] } ], "net_income": 5000 }, "year_to_date": { "sections": [ { "key": "ingresos", "name": "Ingresos", "total": 5000, "groups": [ { "grouping_code": "401", "name": "Ingresos", "amount": 5000, "accounts": [ { "number": "401-01", "name": "Ventas y/o servicios gravados a la tasa general", "amount": 5000 } ] } ] }, { "key": "costos", "name": "Costo de ventas", "total": 0, "groups": [] }, { "key": "gastos", "name": "Gastos de operación", "total": 0, "groups": [] }, { "key": "financiamiento", "name": "Resultado integral de financiamiento", "total": 0, "groups": [] }, { "key": "otros", "name": "Otros ingresos y gastos", "total": 0, "groups": [] } ], "net_income": 5000 } }, "balance_sheet": { "assets": { "total": 5800, "groups": [ { "grouping_code": "105", "name": "Clientes", "amount": 5800, "accounts": [ { "number": "105-01", "name": "Clientes nacionales", "amount": 5800 } ] } ] }, "liabilities": { "total": 800, "groups": [ { "grouping_code": "208", "name": "IVA trasladado cobrado", "amount": 800, "accounts": [ { "number": "208-01", "name": "IVA trasladado cobrado", "amount": 800 } ] } ] }, "equity": { "total": 0, "groups": [] }, "net_income_year_to_date": 5000, "liabilities_and_equity": 5800, "difference": 0 }, "journal_entries": 1, "opening_balances_date": null, "missing_invoices": 0 } ``` --- # Invoice Un CFDI 4.0 emitido: factura de ingreso o global, traslado, nota de crédito (E), complemento de pago (P) o recibo de nómina (N). | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"invoice"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `branch` | string o null | sí | La sucursal (brn_…) desde donde se timbró, o null (el domicilio fiscal del emisor). Sus pagos y notas de crédito la siguen. | | `type` | `I`, `E`, `P`, `T`, `N` | sí | Tipo de comprobante (TipoDeComprobante): I ingreso, E egreso, P pago, T traslado, N nómina. | | `status` | `processing`, `pending`, `valid`, `rejected`, `canceled` | sí | processing: el PAC no ha contestado (se resuelve sola; escuche invoice.stamped). valid: timbrada. pending: timbrada con una cancelación en espera del cliente. rejected: el SAT o el PAC no la aceptaron (ver error). canceled: cancelada. | | `uuid` | string o null | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `series` | string o null | sí | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. | | `folio` | string o null | sí | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. | | `date` | string o null | sí | Fecha de emisión (Fecha), hora de la Ciudad de México. | | `customer` | objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `customer.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `customer.tax_system` | string | | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `customer.zip` | string | | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `use` | string o null | sí | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `payment_form` | string o null | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `payment_method` | string o null | sí | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `currency` | string | sí | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. | | `exchange_rate` | number o null | sí | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. | | `items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. | | `subtotal` | number o null | sí | Suma de los importes antes de impuestos y descuentos (SubTotal). | | `discount` | number o null | sí | Descuento (Descuento). | | `taxes` | objeto o null | sí | Impuestos del comprobante, por tipo. | | `taxes.iva` | number o null | sí | IVA trasladado. | | `taxes.ieps` | number o null | sí | IEPS trasladado. | | `taxes.iva_withheld` | number o null | sí | IVA retenido. | | `taxes.isr_withheld` | number o null | sí | ISR retenido. | | `taxes.local_transferred` | number o null | sí | Impuestos locales trasladados. | | `taxes.local_withheld` | number o null | sí | Impuestos locales retenidos. | | `total` | number o null | sí | Total en la moneda del comprobante (Total). | | `payment_status` | `paid`, `partial`, `unpaid` o null | sí | Si ya se cobró (Cuentas por cobrar): paid, partial (complementos de pago o notas de crédito por una parte) o unpaid. Una PUE está paid, salvo con paid_later hasta POST /v1/invoices/{id}/mark_paid. null si no es de ingreso o no está vigente. | | `amount_due` | number o null | sí | Lo que falta por cobrar, en su moneda, o null. | | `amount_paid` | number o null | sí | Lo cobrado: complementos de pago y notas de crédito vigentes, o todo si es PUE pagada. null si no aplica. | | `related` | objeto o null | sí | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `payment` | objeto o null | | Solo en un complemento de pago (type P). | | `payment.date` | string o null | sí | Fecha del pago (FechaPago). | | `payment.payment_form` | string o null | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `payment.currency` | string | sí | Moneda del pago (MonedaP). | | `payment.exchange_rate` | number o null | sí | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. | | `payment.amount` | number | sí | Monto del pago (Monto). | | `payment.operation_number` | string o null | sí | Número de operación del pago (NumOperacion), como el rastreo SPEI. | | `payment.documents` | arreglo de objeto | sí | Las facturas que paga (DoctoRelacionado). | | `payment.documents[].uuid` | string | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `payment.documents[].series` | string o null | sí | Serie de la factura pagada. | | `payment.documents[].folio` | string o null | sí | Folio de la factura pagada. | | `payment.documents[].currency` | string | sí | Moneda de la factura pagada (MonedaDR). | | `payment.documents[].partiality` | integer | sí | Número de parcialidad (NumParcialidad). (de -9007199254740991 a 9007199254740991) | | `payment.documents[].previous_balance` | number | sí | Saldo anterior de la factura (ImpSaldoAnt). | | `payment.documents[].amount` | number | sí | Lo que se paga de esa factura (ImpPagado), en la moneda de la factura. | | `payment.documents[].remaining` | number | sí | Saldo insoluto después de este pago (ImpSaldoInsoluto). | | `payroll` | objeto | | Solo en un recibo de nómina (type N). | | `payroll.periodicity` | string | sí | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `payroll.period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `payroll.period.start` | string | sí | Primer día del periodo (FechaInicialPago), AAAA-MM-DD. | | `payroll.period.end` | string | sí | Último día del periodo (FechaFinalPago), AAAA-MM-DD. | | `payroll.period.payment_date` | string | sí | Fecha de pago (FechaPago), AAAA-MM-DD. Por omisión el último día del periodo. | | `payroll.days` | number | sí | Días pagados (NumDiasPagados). Por omisión, todos los del periodo que le tocan. | | `payroll.employer` | objeto | sí | El patrón (Emisor del complemento): registro patronal y CURP si es persona física. | | `payroll.employer.employer_registration` | string o null | sí | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `payroll.employer.curp` | string o null | sí | CURP, 18 caracteres. | | `payroll.employee` | objeto | sí | El empleado (emp_…). | | `payroll.employee.id` | string o null | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `payroll.employee.number` | string | sí | Número de empleado (NumEmpleado). | | `payroll.employee.curp` | string | sí | CURP, 18 caracteres. | | `payroll.employee.social_security_number` | string o null | sí | Número de seguridad social (NumSeguridadSocial), 11 dígitos. | | `payroll.employee.start_date` | string | sí | Fecha de inicio de la relación laboral (FechaInicioRelLaboral), AAAA-MM-DD. | | `payroll.employee.contract_type` | string | sí | Tipo de contrato (TipoContrato), c_TipoContrato. Por omisión 01, por tiempo indeterminado. | | `payroll.employee.unionized` | boolean | sí | true si es sindicalizado (Sindicalizado). | | `payroll.employee.workday_type` | string | sí | Tipo de jornada (TipoJornada), c_TipoJornada. Por omisión 01, diurna. | | `payroll.employee.regime_type` | string | sí | Tipo de régimen (TipoRegimen): 02 sueldos y salarios. | | `payroll.employee.department` | string o null | | Departamento (Departamento). | | `payroll.employee.position` | string o null | | Puesto (Puesto). | | `payroll.employee.risk_class` | string o null | sí | Clase de riesgo del puesto ante el IMSS (RiesgoPuesto), 1 a 5. Por omisión 1. | | `payroll.employee.contribution_base_salary` | number o null | sí | Salario base de cotización (SalarioBaseCotApor). | | `payroll.employee.integrated_daily_salary` | number o null | sí | Salario diario integrado (SalarioDiarioIntegrado): no menor al salario diario. | | `payroll.employee.state` | string | sí | Entidad federativa donde trabaja (ClaveEntFed): JAL, CMX, NLE… | | `payroll.perceptions` | arreglo de objeto | sí | Percepciones (Percepcion): sueldo y demás, gravado y exento. | | `payroll.perceptions[].type` | string | sí | Tipo de percepción (TipoPercepcion), c_TipoPercepcion: 001 sueldos… | | `payroll.perceptions[].key` | string | sí | Clave del patrón (Clave). | | `payroll.perceptions[].concept` | string | sí | Concepto (Concepto). | | `payroll.perceptions[].taxed` | number | sí | Importe gravado (ImporteGravado). | | `payroll.perceptions[].exempt` | number | sí | Importe exento (ImporteExento). | | `payroll.deductions` | arreglo de objeto | sí | Deducciones (Deduccion): ISR, cuotas IMSS y demás. | | `payroll.deductions[].type` | string | sí | Tipo de deducción (TipoDeduccion), c_TipoDeduccion: 001 IMSS, 002 ISR… | | `payroll.deductions[].key` | string | sí | Clave del patrón (Clave). | | `payroll.deductions[].concept` | string | sí | Concepto (Concepto). | | `payroll.deductions[].amount` | number | sí | Importe (Importe). | | `payroll.other_payments` | arreglo de objeto | sí | Otros pagos (OtroPago): subsidio para el empleo. | | `payroll.other_payments[].type` | string | sí | Tipo de otro pago (TipoOtroPago), c_TipoOtroPago: 002 subsidio para el empleo. | | `payroll.other_payments[].key` | string | sí | Clave del patrón (Clave). | | `payroll.other_payments[].concept` | string | sí | Concepto (Concepto). | | `payroll.other_payments[].amount` | number | sí | Importe pagado (Importe). | | `payroll.other_payments[].subsidy_caused` | number | | Subsidio causado (SubsidioCausado). | | `payroll.other_payments[].subsidy_applied` | number | | Del subsidio causado, el ISR que evitó (no va en el XML). | | `global` | objeto | | Solo en una factura global. | | `cancellation` | objeto o null | sí | La cancelación, si se pidió: estado, motivo, sustituto y cuándo. | | `cancellation.status` | string o null | sí | pending (espera la respuesta del cliente), accepted (cancelada), rejected (el cliente la rechazó) o lapsed (venció; puede pedirla otra vez). | | `cancellation.motive` | string o null | sí | Motivo de cancelación (c_MotivoCancelacion): 01 con errores con relación (lleva substitution), 02 con errores sin relación (por omisión), 03 no se llevó a cabo, 04 solo en una factura global, cuando una venta incluida se facturó a nombre del cliente. | | `cancellation.substitution` | string o null | sí | El comprobante que sustituye a este (su id o su UUID), con el motivo 01. | | `cancellation.canceled_at` | string (date-time) o null | sí | Cuándo se canceló. | | `email` | objeto o null | sí | El envío por correo: a quién, con copia a quién y cuándo (null si no se ha enviado). | | `email.to` | string o null | sí | A quién (correos separados por comas). | | `email.cc` | string o null | sí | Con copia (correos separados por comas). | | `email.sent_at` | string (date-time) o null | sí | Cuándo se envió. | | `error` | objeto o null | sí | Por qué el SAT o el PAC no lo aceptaron: mensaje, código del SAT y texto original. | | `error.message` | string | sí | Qué pasó y cómo corregirlo, para una persona. | | `error.sat_code` | string o null | sí | Código del SAT o del PAC (CFDI40xxx). | | `error.raw` | string | sí | El texto original del PAC. | | `recurring_invoice` | string o null | sí | La factura recurrente (rin_…) que la timbró sola, o null si se timbró con una llamada. | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `warnings` | arreglo de string | | Avisos que no impidieron timbrar (por ejemplo, el XML se guardará después). | | `email_error` | string | | Por qué no se pudo mandar el correo; el comprobante sí se timbró. | | `substitutes` | objeto | | La factura que esta sustituyó y si quedó cancelada. | | `substitutes.invoice` | string | sí | La factura sustituida (inv_…). | | `substitutes.canceled` | boolean | sí | true si la sustituida ya quedó cancelada. | | `substitutes.cancel_error` | string | | Por qué no se pudo cancelar la sustituida (cancélela con POST /v1/invoices/{id}/cancel). | | `advance_application` | objeto | | La aplicación del anticipo: método, nota de aplicación y su estado. | | `advance_application.advance` | string o null | sí | El anticipo, como factura (inv_…). | | `advance_application.advance_uuid` | string | sí | El folio fiscal del anticipo. | | `advance_application.method` | `egreso`, `remanente` | sí | egreso (la factura completa y después una nota de crédito por el anticipo, opción A) o remanente (la factura solo por lo que falta, opción B). | | `advance_application.status` | `done`, `credit_note_pending` | sí | done (listo) o credit_note_pending (falta timbrar la nota de aplicación: repita la solicitud con la misma Idempotency-Key). | | `advance_application.credit_note` | string o null | sí | La nota de aplicación del anticipo (inv_…), con method egreso. | | `advance_application.credit_note_error` | string | | Por qué no se timbró la nota de aplicación (repita con la misma Idempotency-Key). | Ejemplo: ```json { "id": "inv_OufpKqnrvx9p0Nfbydl9jG4UNGeTYcvdrg", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "I04", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "quantity": 2, "unit_price": 2500, "taxability": "02", "iva": "0.16" } ], "subtotal": 5000, "discount": 0, "taxes": { "iva": 800, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 5800, "payment_status": "paid", "amount_due": 0, "amount_paid": 5800, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } ``` --- # InvoiceBatch Un lote de hasta 1,000 facturas timbradas en segundo plano. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"invoice_batch"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `name` | string | sí | Nombre del lote, para usted. | | `status` | `processing`, `completed` | sí | processing: timbrando. completed: ya no queda ninguna por timbrar. | | `total` | integer | sí | Cuántas facturas tiene el lote. (de -9007199254740991 a 9007199254740991) | | `stamped` | integer | sí | Cuántas se timbraron. (de -9007199254740991 a 9007199254740991) | | `failed` | integer | sí | Cuántas no se timbraron (quedan con su motivo). (de -9007199254740991 a 9007199254740991) | | `processing` | integer | | Cuántas siguen en proceso. (de -9007199254740991 a 9007199254740991) | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `completed_at` | string (date-time) o null | sí | Cuándo terminó. | | `invoices` | arreglo de objeto | | Cada factura del lote (no viene en listas ni en eventos). | | `invoices[].index` | integer | sí | Su posición en invoices, desde 0. (de -9007199254740991 a 9007199254740991) | | `invoices[].reference` | string o null | sí | Su identificador de esta factura (su número de pedido), hasta 100 caracteres. | | `invoices[].status` | `processing`, `valid`, `failed` | sí | processing, valid (timbrada) o failed (no se timbró; ver error). | | `invoices[].invoice` | string o null | sí | La factura timbrada (inv_…). | | `invoices[].uuid` | string o null | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `invoices[].total` | number | sí | Total en la moneda del comprobante (Total). | | `invoices[].error` | string o null | sí | Por qué no se timbró. | Ejemplo: ```json { "id": "bat_OufpKqnrvx9p4hMdcoapAo5aaGJEme7mXj", "object": "invoice_batch", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Cuotas de octubre", "status": "completed", "total": 2, "stamped": 1, "failed": 1, "created_at": "2026-10-24T10:15:42-06:00", "completed_at": "2026-10-24T10:15:42-06:00", "processing": 0, "invoices": [ { "index": 0, "reference": "depto-101", "status": "valid", "invoice": "inv_OufpKqnrvx9p0mn7VjQ6JlfipKAkHWstOK", "uuid": "DB494B07-D6DE-4F70-8ED0-C8B864FF8717", "total": 1740, "error": null }, { "index": 1, "reference": "depto-102", "status": "failed", "invoice": null, "uuid": null, "total": 1740, "error": "El SAT no reconoce el nombre del cliente. Escríbalo exactamente como en su constancia de situación fiscal, en mayúsculas y sin «S.A. de C.V.» ni otro régimen de capital." } ] } ``` --- # InvoiceBatchList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"invoice_batch"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].name` | string | sí | Nombre del lote, para usted. | | `data[].status` | `processing`, `completed` | sí | processing: timbrando. completed: ya no queda ninguna por timbrar. | | `data[].total` | integer | sí | Cuántas facturas tiene el lote. (de -9007199254740991 a 9007199254740991) | | `data[].stamped` | integer | sí | Cuántas se timbraron. (de -9007199254740991 a 9007199254740991) | | `data[].failed` | integer | sí | Cuántas no se timbraron (quedan con su motivo). (de -9007199254740991 a 9007199254740991) | | `data[].processing` | integer | | Cuántas siguen en proceso. (de -9007199254740991 a 9007199254740991) | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].completed_at` | string (date-time) o null | sí | Cuándo terminó. | | `data[].invoices` | arreglo de objeto | | Cada factura del lote (no viene en listas ni en eventos). | | `data[].invoices[].index` | integer | sí | Su posición en invoices, desde 0. (de -9007199254740991 a 9007199254740991) | | `data[].invoices[].reference` | string o null | sí | Su identificador de esta factura (su número de pedido), hasta 100 caracteres. | | `data[].invoices[].status` | `processing`, `valid`, `failed` | sí | processing, valid (timbrada) o failed (no se timbró; ver error). | | `data[].invoices[].invoice` | string o null | sí | La factura timbrada (inv_…). | | `data[].invoices[].uuid` | string o null | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `data[].invoices[].total` | number | sí | Total en la moneda del comprobante (Total). | | `data[].invoices[].error` | string o null | sí | Por qué no se timbró. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # InvoiceCreate Una factura (CFDI 4.0). Un campo que no existe se rechaza con parameter_unknown, nunca se ignora. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `issuer` | string (iss_…) | | Emisor (iss_…). Si la llave tiene un solo emisor o la cuenta tiene uno solo, puede omitirse. | | `branch` | string (brn_…) | | Sucursal (brn_…) desde donde se factura: su código postal va en LugarExpedicion, su dirección en el PDF y, si las tiene, sus series. Sin ella, el domicilio fiscal del emisor (la Matriz). | | `warehouse` | string (whs_…) | | Almacén (whs_…) de donde salen sus productos con track_stock. Por omisión, el de su sucursal (o el de la Matriz). | | `customer` | string (cus_…) o objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). (hasta 300 caracteres) | | `customer.tax_system` | string | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `customer.zip` | string | sí | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `customer.email` | string o null | | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `customer.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `customer.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `customer.ieps_taxpayer` | boolean | | true si el cliente es contribuyente de IEPS: el IEPS va desglosado en vez de sumado al precio. | | `items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. (hasta 200) | | `items[].product` | string (prd_…) | | Un producto de su catálogo (prd_…): toma su clave, unidad, precio e impuestos. | | `items[].description` | string | | Descripción del concepto (Descripcion), hasta 1,000 caracteres. (hasta 1000 caracteres) | | `items[].product_key` | string | | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `items[].unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… (hasta 5 caracteres) | | `items[].unit_name` | string o null | | Nombre de la unidad (Unidad), hasta 20 caracteres. | | `items[].sku` | string o null | | Número de identificación o SKU (NoIdentificacion). | | `items[].quantity` | number | | Cantidad (Cantidad). Por omisión 1. (de -∞ a 1000000000) | | `items[].unit_price` | number | | Precio unitario antes de impuestos (ValorUnitario). (de 0 a 1000000000000) | | `items[].discount` | number | | Descuento (Descuento). (de 0 a 1000000000000) | | `items[].taxability` | `01`, `02`, `03`, `04`, `05`, `06`, `07`, `08` | | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… | | `items[].iva` | `0`, `0.16`, `0.08`, `exento` | | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `items[].ieps` | number | | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. (de 0 a 72.1605) | | `items[].ieps_factor` | `Tasa`, `Cuota` | | Tasa o Cuota (TipoFactor del IEPS). | | `items[].ieps_units` | number | | Unidades de IEPS a cuota (litros, cigarros…), si no son la cantidad. (de -∞ a 1000000000) | | `items[].iva_retention` | number | | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). (de 0 a 0.16) | | `items[].isr_retention` | number | | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). (de 0 a 0.35) | | `items[].predial` | arreglo de string | | Números de cuenta predial (CuentaPredial) en arrendamiento. (hasta 10) | | `items[].pedimentos` | arreglo de string | | Números de pedimento (InformacionAduanera) de mercancía importada. (hasta 50) | | `items[].third_party` | objeto | | A cuenta de terceros (ACuentaTerceros): RFC, nombre, régimen y código postal. | | `items[].third_party.tax_id` | string | sí | RFC de a cuenta de quién (RfcACuentaTerceros). (hasta 13 caracteres) | | `items[].third_party.name` | string | sí | Nombre (NombreACuentaTerceros). (hasta 300 caracteres) | | `items[].third_party.tax_system` | string | sí | Régimen fiscal (RegimenFiscalACuentaTerceros), c_RegimenFiscal. | | `items[].third_party.zip` | string | sí | Código postal de su domicilio fiscal (DomicilioFiscalACuentaTerceros). | | `items[].iedu` | objeto | | Complemento de instituciones educativas privadas (IEDU): alumno, CURP, nivel y RVOE. | | `items[].iedu.student_name` | string | sí | Nombre del alumno (nombreAlumno). (hasta 300 caracteres) | | `items[].iedu.curp` | string | sí | CURP, 18 caracteres. (18 caracteres) | | `items[].iedu.level` | `Preescolar`, `Primaria`, `Secundaria`, `Profesional técnico`, `Bachillerato o su equivalente` | sí | Nivel educativo (nivelEducativo). | | `items[].iedu.rvoe` | string | sí | Reconocimiento de validez oficial de estudios (autRVOE). (hasta 30 caracteres) | | `items[].iedu.payer_tax_id` | string o null | | RFC de quien paga, si no es el receptor (rfcPago). | | `items[].used_vehicle` | objeto | | Complemento de vehículo usado. | | `items[].used_vehicle.purchase_amount` | number | sí | Monto de adquisición (montoAdquisicion). (de -∞ a 1000000000000) | | `items[].used_vehicle.sale_amount` | number | sí | Monto de enajenación (montoEnajenacion). (de -∞ a 1000000000000) | | `items[].used_vehicle.vehicle_key` | string | sí | Clave vehicular (claveVehicular). (hasta 7 caracteres) | | `items[].used_vehicle.make` | string | sí | Marca (marca). (hasta 50 caracteres) | | `items[].used_vehicle.type` | string | sí | Tipo o versión (tipo). (hasta 50 caracteres) | | `items[].used_vehicle.model` | string | sí | Año del modelo (modelo), 4 dígitos. | | `items[].used_vehicle.engine_number` | string | | Número de motor (numeroMotor). (hasta 17 caracteres) | | `items[].used_vehicle.serial_number` | string | | Número de serie (numeroSerie). (hasta 17 caracteres) | | `items[].used_vehicle.niv` | string | | Número de identificación vehicular (NIV). (hasta 17 caracteres) | | `items[].used_vehicle.guide_value` | number | sí | Valor según la guía EBC o Libro Azul (valor). (de -∞ a 1000000000000) | | `items[].used_vehicle.customs` | arreglo de objeto | | Información aduanera de la importación del vehículo (InformacionAduanera). (hasta 10) | | `items[].used_vehicle.customs[].number` | string | sí | Número del documento de importación (numero). (hasta 100 caracteres) | | `items[].used_vehicle.customs[].date` | string | sí | Fecha del documento (fecha), AAAA-MM-DD. | | `items[].used_vehicle.customs[].office` | string | | Aduana (aduana). (hasta 100 caracteres) | | `items[].addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `type` | `I`, `T` | | I ingreso (por omisión; con global, factura global) o T traslado. Notas de crédito: POST /v1/credit_notes. Pagos: POST /v1/payments. (por omisión "I") | | `carta_porte` | objeto | | Complemento Carta Porte 3.1 (traslado de mercancías). | | `carta_porte.modes` | arreglo de `01`, `02`, `03`, `04` | | Cómo viajan las mercancías (c_CveTransporte): 01 autotransporte, 02 marítimo, 03 aéreo, 04 ferroviario; varios si es intermodal. Por omisión 01. (hasta 4) | | `carta_porte.international` | objeto | | Transporte internacional (TranspInternac Sí): entrada o salida, país, vía y regímenes aduaneros. | | `carta_porte.international.direction` | `Entrada`, `Salida` | sí | EntradaSalidaMerc: Entrada o Salida. | | `carta_porte.international.country` | string | sí | País de origen o destino (PaisOrigenDestino), c_Pais. (3 caracteres) | | `carta_porte.international.via` | string | sí | Vía de entrada o salida (ViaEntradaSalida), c_CveTransporte (05 ducto incluido). (hasta 2 caracteres) | | `carta_porte.international.regimes` | arreglo de string | sí | Regímenes aduaneros (RegimenAduanero), c_RegimenAduanero. (hasta 10) | | `carta_porte.istmo` | objeto | | Corredor Interoceánico del Istmo de Tehuantepec: polos de origen y destino (c_RegistroISTMO). | | `carta_porte.istmo.origin` | string | sí | Polo de origen (UbicacionPoloOrigen). (hasta 2 caracteres) | | `carta_porte.istmo.destination` | string | sí | Polo de destino (UbicacionPoloDestino). (hasta 2 caracteres) | | `carta_porte.reverse_logistics` | boolean | | Autotransporte: true si es logística inversa, recolección o devolución. | | `carta_porte.origin` | objeto | sí | Ubicación de origen (Ubicacion TipoUbicacion Origen): remitente, fecha y hora de salida y domicilio. | | `carta_porte.origin.tax_id` | string | sí | RFC del remitente (RFCRemitenteDestinatario); XEXX010101000 si es extranjero. | | `carta_porte.origin.name` | string | | Nombre del remitente (NombreRemitenteDestinatario). (hasta 254 caracteres) | | `carta_porte.origin.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.origin.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.origin.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.origin.date_time` | string | sí | Fecha y hora de salida (origen) o llegada (destino), AAAA-MM-DDTHH:MM, hora local. | | `carta_porte.origin.distance_km` | number | | Distancia recorrida desde la ubicación anterior, en km (DistanciaRecorrida). Autotransporte y ferroviario. (de -∞ a 99999) | | `carta_porte.origin.station` | objeto | | Estación, puerto o aeropuerto (TipoEstacion y NumEstacion de c_Estaciones). Solo sin autotransporte. | | `carta_porte.origin.station.type` | string | | Tipo de estación (TipoEstacion): 01 origen, 02 intermedia, 03 destino final. (hasta 2 caracteres) | | `carta_porte.origin.station.number` | string | | Clave de la estación, puerto o aeropuerto (NumEstacion), c_Estaciones. (hasta 12 caracteres) | | `carta_porte.origin.station.name` | string | | Nombre de la estación (NombreEstacion). (hasta 50 caracteres) | | `carta_porte.origin.station.foreign` | boolean | | true si la estación está en el extranjero (va sin TipoEstacion). | | `carta_porte.origin.navigation` | `Altura`, `Cabotaje` | | Marítimo: tipo de tráfico (NavegacionTrafico), Altura o Cabotaje. | | `carta_porte.origin.address` | objeto | sí | Domicilio (Domicilio). En México basta el código postal: estado, municipio y localidad se toman de c_CodigoPostal. | | `carta_porte.origin.address.street` | string | | Calle (Calle). (hasta 100 caracteres) | | `carta_porte.origin.address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `carta_porte.origin.address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `carta_porte.origin.address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `carta_porte.origin.address.country` | string | | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `carta_porte.origin.address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `carta_porte.origin.address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.origin.address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.origin.address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.destinations` | arreglo de objeto | sí | Ubicaciones de destino (TipoUbicacion Destino), en orden: destinatario, llegada, distancia y domicilio. (hasta 20) | | `carta_porte.destinations[].tax_id` | string | sí | RFC del destinatario (RFCRemitenteDestinatario); XEXX010101000 si es extranjero. | | `carta_porte.destinations[].name` | string | | Nombre del destinatario (NombreRemitenteDestinatario). (hasta 254 caracteres) | | `carta_porte.destinations[].foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.destinations[].foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.destinations[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.destinations[].date_time` | string | sí | Fecha y hora de salida (origen) o llegada (destino), AAAA-MM-DDTHH:MM, hora local. | | `carta_porte.destinations[].distance_km` | number | | Distancia recorrida desde la ubicación anterior, en km (DistanciaRecorrida). Autotransporte y ferroviario. (de -∞ a 99999) | | `carta_porte.destinations[].station` | objeto | | Estación, puerto o aeropuerto (TipoEstacion y NumEstacion de c_Estaciones). Solo sin autotransporte. | | `carta_porte.destinations[].station.type` | string | | Tipo de estación (TipoEstacion): 01 origen, 02 intermedia, 03 destino final. (hasta 2 caracteres) | | `carta_porte.destinations[].station.number` | string | | Clave de la estación, puerto o aeropuerto (NumEstacion), c_Estaciones. (hasta 12 caracteres) | | `carta_porte.destinations[].station.name` | string | | Nombre de la estación (NombreEstacion). (hasta 50 caracteres) | | `carta_porte.destinations[].station.foreign` | boolean | | true si la estación está en el extranjero (va sin TipoEstacion). | | `carta_porte.destinations[].navigation` | `Altura`, `Cabotaje` | | Marítimo: tipo de tráfico (NavegacionTrafico), Altura o Cabotaje. | | `carta_porte.destinations[].address` | objeto | sí | Domicilio (Domicilio). En México basta el código postal: estado, municipio y localidad se toman de c_CodigoPostal. | | `carta_porte.destinations[].address.street` | string | | Calle (Calle). (hasta 100 caracteres) | | `carta_porte.destinations[].address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `carta_porte.destinations[].address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `carta_porte.destinations[].address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `carta_porte.destinations[].address.country` | string | | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `carta_porte.destinations[].address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `carta_porte.destinations[].address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.destinations[].address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.destinations[].address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.goods` | arreglo de objeto | sí | Mercancías (Mercancia) que se transportan. (hasta 200) | | `carta_porte.goods[].product_key` | string | sí | Clave del bien transportado (BienesTransp), c_ClaveProdServCP. | | `carta_porte.goods[].description` | string | sí | Descripción de la mercancía (Descripcion). (hasta 1000 caracteres) | | `carta_porte.goods[].quantity` | number | sí | Cantidad (Cantidad). (de -∞ a 1000000000) | | `carta_porte.goods[].unit_key` | string | sí | Clave de unidad (ClaveUnidad), c_ClaveUnidad. (hasta 5 caracteres) | | `carta_porte.goods[].weight_kg` | number | sí | Peso en kilogramos (PesoEnKg). (de 0.001 a 100000000) | | `carta_porte.goods[].dimensions` | string | | Dimensiones (Dimensiones), como 30/40/30cm o en plg. (hasta 20 caracteres) | | `carta_porte.goods[].hazmat` | objeto | | Material peligroso (MaterialPeligroso Sí): clave UN y embalaje. | | `carta_porte.goods[].hazmat.key` | string | sí | Clave del material peligroso (CveMaterialPeligroso), c_MaterialPeligroso (número UN). (hasta 4 caracteres) | | `carta_porte.goods[].hazmat.packaging` | string | sí | Tipo de embalaje (Embalaje), c_TipoEmbalaje. (hasta 4 caracteres) | | `carta_porte.goods[].hazmat.packaging_description` | string | | Descripción del embalaje (DescripEmbalaje). (hasta 100 caracteres) | | `carta_porte.goods[].cofepris` | objeto | | Datos COFEPRIS de la mercancía: sector y lo que pide cada uno (medicamentos, precursores, psicotrópicos, sustancias tóxicas, plaguicidas). | | `carta_porte.goods[].cofepris.sector` | string | sí | Sector COFEPRIS (SectorCOFEPRIS): 01 medicamento, 02 precursores y químicos de uso dual, 03 psicotrópicos y estupefacientes, 04 sustancias tóxicas, 05 plaguicidas y fertilizantes. (hasta 2 caracteres) | | `carta_porte.goods[].cofepris.active_ingredient` | string | | Nombre del ingrediente activo (NombreIngredienteActivo). (hasta 1000 caracteres) | | `carta_porte.goods[].cofepris.chemical_name` | string | | Nombre químico (NomQuimico). (hasta 150 caracteres) | | `carta_porte.goods[].cofepris.generic_name` | string | | Denominación genérica del producto (DenominacionGenericaProd). (hasta 50 caracteres) | | `carta_porte.goods[].cofepris.brand_name` | string | | Denominación distintiva o marca (DenominacionDistintivaProd). (hasta 50 caracteres) | | `carta_porte.goods[].cofepris.manufacturer` | string | | Fabricante (Fabricante). (hasta 240 caracteres) | | `carta_porte.goods[].cofepris.expiry` | string | | Fecha de caducidad (FechaCaducidad), AAAA-MM-DD. (hasta 10 caracteres) | | `carta_porte.goods[].cofepris.lot` | string | | Lote del medicamento (LoteMedicamento). (hasta 10 caracteres) | | `carta_porte.goods[].cofepris.form` | string | | Forma farmacéutica (FormaFarmaceutica), c_FormaFarmaceutica. (hasta 2 caracteres) | | `carta_porte.goods[].cofepris.conditions` | string | | Condiciones especiales de transporte (CondicionesEspTransp), c_CondicionesEspeciales. (hasta 2 caracteres) | | `carta_porte.goods[].cofepris.sanitary_registry` | string | | Registro sanitario o folio de autorización (RegistroSanitarioFolioAutorizacion). (hasta 15 caracteres) | | `carta_porte.goods[].cofepris.import_permit` | string | | Permiso de importación (PermisoImportacion). (hasta 6 caracteres) | | `carta_porte.goods[].cofepris.vucem_folio` | string | | Folio de importación VUCEM (FolioImpoVUCEM). (hasta 25 caracteres) | | `carta_porte.goods[].cofepris.cas` | string | | Número CAS de la sustancia (NumCAS). (hasta 15 caracteres) | | `carta_porte.goods[].cofepris.importer` | string | | Razón social de la empresa importadora (RazonSocialEmpImp). (hasta 80 caracteres) | | `carta_porte.goods[].cofepris.pesticide_registry` | string | | Registro sanitario de plaguicidas COFEPRIS (NumRegSanPlagCOFEPRIS). (hasta 60 caracteres) | | `carta_porte.goods[].cofepris.manufacturer_data` | string | | Datos del fabricante (DatosFabricante). (hasta 600 caracteres) | | `carta_porte.goods[].cofepris.formulator_data` | string | | Datos del formulador (DatosFormulador). (hasta 600 caracteres) | | `carta_porte.goods[].cofepris.maquilador_data` | string | | Datos del maquilador (DatosMaquilador). (hasta 600 caracteres) | | `carta_porte.goods[].cofepris.authorized_use` | string | | Uso autorizado (UsoAutorizado). (hasta 1000 caracteres) | | `carta_porte.goods[].value` | number | | Valor de la mercancía (ValorMercancia); obligatorio en transporte aéreo. (de 0 a 1000000000000000) | | `carta_porte.goods[].currency` | string | | Moneda del valor de la mercancía (Moneda), c_Moneda. (3 caracteres) | | `carta_porte.goods[].tariff` | string | | Fracción arancelaria (FraccionArancelaria), 10 dígitos. Transporte internacional. (hasta 10 caracteres) | | `carta_porte.goods[].cce_uuid` | string | | UUID del CFDI con complemento de comercio exterior (UUIDComercioExt). (hasta 36 caracteres) | | `carta_porte.goods[].material` | objeto | | Tipo de materia (TipoMateria, c_TipoMateria) y su descripción si es «otra». | | `carta_porte.goods[].material.type` | string | sí | Tipo de materia (TipoMateria), c_TipoMateria. (hasta 2 caracteres) | | `carta_porte.goods[].material.description` | string | | Descripción de la materia (DescripcionMateria), con el tipo 05 otra. (hasta 50 caracteres) | | `carta_porte.goods[].customs_docs` | arreglo de objeto | | Documentación aduanera (DocumentacionAduanera) de la mercancía en transporte internacional. (hasta 20) | | `carta_porte.goods[].customs_docs[].type` | string | sí | Tipo de documento (TipoDocumento), c_DocumentoAduanero: 01 pedimento… (hasta 2 caracteres) | | `carta_porte.goods[].customs_docs[].pedimento` | string | | Número de pedimento (NumPedimento), con el formato «NN NN NNNN NNNNNNN». Solo con el tipo 01. (hasta 21 caracteres) | | `carta_porte.goods[].customs_docs[].doc_id` | string | | Identificador del documento aduanero (IdentDocAduanero), con un tipo distinto de 01. (hasta 150 caracteres) | | `carta_porte.goods[].customs_docs[].importer_tax_id` | string | | RFC del importador (RFCImpo), con un pedimento. (hasta 13 caracteres) | | `carta_porte.goods[].guides` | arreglo de objeto | | Paquetería: guías de los paquetes o tarimas (GuiasIdentificacion). (hasta 100) | | `carta_porte.goods[].guides[].number` | string | sí | Número de guía (NumeroGuiaIdentificacion). (hasta 30 caracteres) | | `carta_porte.goods[].guides[].description` | string | sí | Descripción del contenido (DescripGuiaIdentificacion). (hasta 1000 caracteres) | | `carta_porte.goods[].guides[].weight_kg` | number | sí | Peso en kilogramos (PesoEnKg). (de 0 a 100000000) | | `carta_porte.goods[].quantities` | arreglo de objeto | | Cuánto va a cada destino (CantidadTransporta), por destino. (hasta 40) | | `carta_porte.goods[].quantities[].quantity` | number | sí | Cantidad que va a ese destino. (de -∞ a 1000000000) | | `carta_porte.goods[].quantities[].destination` | integer | sí | El destino, por su posición en destinations (desde 0). (de 0 a 19) | | `carta_porte.goods[].quantities[].mode` | `01`, `02`, `03`, `04` | | Medio en que va esa cantidad (CvesTransporte), c_CveTransporte. | | `carta_porte.goods[].detail` | objeto | | Marítimo: detalle de la mercancía (DetalleMercancia): kilos netos, tara y piezas. | | `carta_porte.goods[].detail.net_kg` | number | sí | Peso neto en kg (PesoNeto). (de 0 a 100000000) | | `carta_porte.goods[].detail.tare_kg` | number | sí | Tara en kg (PesoTara). (de 0 a 100000000) | | `carta_porte.goods[].detail.pieces` | integer | | Número de piezas (NumPiezas). (de 0 a 1000000000) | | `carta_porte.goods[].stcc` | string | | Ferroviario: clave STCC (ClaveSTCC). (hasta 7 caracteres) | | `carta_porte.vehicle` | objeto | | Autotransporte (Autotransporte): vehículo, permiso SCT, seguros y remolques. | | `carta_porte.vehicle.config` | string | sí | Configuración vehicular (ConfigVehicular), c_ConfigAutotransporte: C2, T3S2… (hasta 10 caracteres) | | `carta_porte.vehicle.plate` | string | sí | Placa (PlacaVM), sin guiones ni espacios. (hasta 10 caracteres) | | `carta_porte.vehicle.year` | integer | sí | Año del modelo (AnioModeloVM). (de 0 a 2100) | | `carta_porte.vehicle.gross_weight` | number | sí | Peso bruto vehicular en toneladas (PesoBrutoVehicular). (de 0 a 999) | | `carta_porte.vehicle.permit_type` | string | sí | Tipo de permiso SCT (PermSCT), c_TipoPermiso. (hasta 50 caracteres) | | `carta_porte.vehicle.permit_number` | string | sí | Número de permiso SCT (NumPermisoSCT). (hasta 50 caracteres) | | `carta_porte.vehicle.insurer` | string | sí | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.vehicle.policy` | string | sí | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.vehicle.env_insurer` | string | | Aseguradora de daños al medio ambiente (AseguraMedAmbiente); obligatoria con material peligroso. (hasta 50 caracteres) | | `carta_porte.vehicle.env_policy` | string | | Póliza de daños al medio ambiente (PolizaMedAmbiente). (hasta 30 caracteres) | | `carta_porte.vehicle.cargo_insurer` | string | | Aseguradora de la carga (AseguraCarga). (hasta 50 caracteres) | | `carta_porte.vehicle.cargo_policy` | string | | Póliza de la carga (PolizaCarga). (hasta 30 caracteres) | | `carta_porte.vehicle.premium` | number | | Prima del seguro (PrimaSeguro). (de 0 a 1000000000000) | | `carta_porte.vehicle.trailers` | arreglo de objeto | | Remolques o semirremolques (Remolque): tipo (c_SubTipoRem) y placa. (hasta 2) | | `carta_porte.vehicle.trailers[].type` | string | sí | Subtipo de remolque (SubTipoRem), c_SubTipoRem. (hasta 10 caracteres) | | `carta_porte.vehicle.trailers[].plate` | string | sí | Placa del remolque (Placa). (hasta 10 caracteres) | | `carta_porte.sea` | objeto | | Transporte marítimo (TransporteMaritimo): embarcación, naviero y contenedores. | | `carta_porte.sea.permit_type` | string | | Tipo de permiso SCT (PermSCT), c_TipoPermiso. (hasta 10 caracteres) | | `carta_porte.sea.permit_number` | string | | Número de permiso SCT (NumPermisoSCT). (hasta 30 caracteres) | | `carta_porte.sea.insurer` | string | | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.sea.policy` | string | | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.sea.vessel_type` | string | sí | Tipo de embarcación (TipoEmbarcacion), c_ConfigMaritima. (hasta 4 caracteres) | | `carta_porte.sea.registration` | string | sí | Matrícula (Matricula). (hasta 30 caracteres) | | `carta_porte.sea.imo` | string | sí | Número IMO (NumeroOMI): IMO y 7 dígitos. (hasta 10 caracteres) | | `carta_porte.sea.year` | integer | | Año de la embarcación (AnioEmbarcacion). (de 0 a 2100) | | `carta_porte.sea.vessel_name` | string | | Nombre de la embarcación (NombreEmbarc). (hasta 50 caracteres) | | `carta_porte.sea.flag` | string | sí | Nacionalidad de la embarcación (NacionalidadEmbarc), c_Pais. (3 caracteres) | | `carta_porte.sea.gross_tonnage` | number | sí | Unidades de arqueo bruto (UnidadesDeArqBruto). (de 0 a 1000000000) | | `carta_porte.sea.cargo_type` | string | sí | Tipo de carga (TipoCarga), c_ClaveTipoCarga. (hasta 4 caracteres) | | `carta_porte.sea.length` | number | | Eslora en metros (Eslora). (de 0 a 100000) | | `carta_porte.sea.beam` | number | | Manga en metros (Manga). (de 0 a 100000) | | `carta_porte.sea.draft` | number | | Calado en metros (Calado). (de 0 a 100000) | | `carta_porte.sea.depth` | number | | Puntal en metros (Puntal). (de 0 a 100000) | | `carta_porte.sea.line` | string | | Línea naviera (LineaNaviera). (hasta 50 caracteres) | | `carta_porte.sea.agent_name` | string | sí | Nombre del agente naviero consignatario (NombreAgenteNaviero). (hasta 100 caracteres) | | `carta_porte.sea.agent_authorization` | string | sí | Autorización del agente naviero (NumAutorizacionNaviero), c_NumAutorizacionNaviero. (hasta 20 caracteres) | | `carta_porte.sea.voyage` | string | | Número de viaje (NumViaje). (hasta 30 caracteres) | | `carta_porte.sea.bill_of_lading` | string | | Número de conocimiento de embarque (NumConocEmbarc). (hasta 30 caracteres) | | `carta_porte.sea.temp_permit` | string | | Permiso temporal de navegación (PermisoTempNavegacion), para una embarcación extranjera. (hasta 19 caracteres) | | `carta_porte.sea.containers` | arreglo de objeto | | Contenedores (Contenedor). (hasta 100) | | `carta_porte.sea.containers[].type` | string | sí | Tipo de contenedor (TipoContenedor), c_ContenedorMaritimo o c_Contenedor en ferroviario. (hasta 6 caracteres) | | `carta_porte.sea.containers[].registration` | string | | Matrícula (Matricula). (hasta 15 caracteres) | | `carta_porte.sea.containers[].seal` | string | | Número de precinto (NumPrecinto). (hasta 20 caracteres) | | `carta_porte.sea.containers[].related_id_ccp` | string | | Ferri: el IdCCP de la Carta Porte del autotransporte a bordo (IdCCPRelacionado). (hasta 36 caracteres) | | `carta_porte.sea.containers[].truck_plate` | string | | Ferri: placa del vehículo a bordo (PlacaVMCCP). (hasta 10 caracteres) | | `carta_porte.sea.containers[].certified_at` | string | | Ferri: fecha de certificación de la Carta Porte del vehículo (FechaCertificacionCCP). (hasta 19 caracteres) | | `carta_porte.sea.containers[].trailers` | arreglo de objeto | | Remolques o semirremolques (Remolque): tipo (c_SubTipoRem) y placa. (hasta 2) | | `carta_porte.sea.containers[].trailers[].type` | string | sí | Subtipo de remolque (SubTipoRem), c_SubTipoRem. (hasta 10 caracteres) | | `carta_porte.sea.containers[].trailers[].plate` | string | sí | Placa del remolque (Placa). (hasta 10 caracteres) | | `carta_porte.air` | objeto | | Transporte aéreo (TransporteAereo): permiso, guía y transportista. | | `carta_porte.air.permit_type` | string | sí | Tipo de permiso SCT (PermSCT), c_TipoPermiso. (hasta 10 caracteres) | | `carta_porte.air.permit_number` | string | sí | Número de permiso SCT (NumPermisoSCT). (hasta 50 caracteres) | | `carta_porte.air.aircraft` | string | | Matrícula de la aeronave (MatriculaAeronave). (hasta 11 caracteres) | | `carta_porte.air.insurer` | string | | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.air.policy` | string | | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.air.waybill` | string | sí | Número de guía aérea (NumeroGuia), de 12 a 15 caracteres. (hasta 15 caracteres) | | `carta_porte.air.contract_place` | string | | Lugar del contrato (LugarContrato). (hasta 120 caracteres) | | `carta_porte.air.carrier` | string | sí | Código del transportista (CodigoTransportista), c_CodigoTransporteAereo. (hasta 6 caracteres) | | `carta_porte.air.shipper` | objeto | | Embarcador (RFCEmbarcador o NumRegIdTribEmbarc y ResidenciaFiscalEmbarc, NombreEmbarcador). | | `carta_porte.air.shipper.tax_id` | string | | RFC del embarcador (RFCEmbarcador). (hasta 13 caracteres) | | `carta_porte.air.shipper.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.air.shipper.foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.air.shipper.foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.air.shipper.name` | string | | Nombre del embarcador (NombreEmbarcador). (hasta 254 caracteres) | | `carta_porte.air.rate_charge` | number | | Cargo por tasación (CargoPorTasacion). (de 0 a 1000000000000000) | | `carta_porte.rail` | objeto | | Transporte ferroviario (TransporteFerroviario): servicio, tráfico, derechos de paso y carros. | | `carta_porte.rail.service` | string | sí | Tipo de servicio (TipoDeServicio), c_TipoDeServicio. (hasta 4 caracteres) | | `carta_porte.rail.traffic` | string | sí | Tipo de tráfico (TipoDeTrafico), c_TipoDeTrafico. (hasta 4 caracteres) | | `carta_porte.rail.insurer` | string | | Aseguradora de responsabilidad civil (AseguraRespCivil). (hasta 50 caracteres) | | `carta_porte.rail.policy` | string | | Póliza de responsabilidad civil (PolizaRespCivil). (hasta 30 caracteres) | | `carta_porte.rail.rights` | arreglo de objeto | | Derechos de paso (DerechosDePaso): tipo y kilómetros. (hasta 50) | | `carta_porte.rail.rights[].type` | string | sí | Tipo de derecho de paso (TipoDerechoDePaso), c_DerechosDePaso. (hasta 6 caracteres) | | `carta_porte.rail.rights[].km` | number | sí | Kilometraje pagado (KilometrajePagado). (de 0 a 100000) | | `carta_porte.rail.cars` | arreglo de objeto | sí | Carros (Carro). (hasta 200) | | `carta_porte.rail.cars[].type` | string | sí | Tipo de carro (TipoCarro), c_TipoCarro. (hasta 4 caracteres) | | `carta_porte.rail.cars[].registration` | string | sí | Matrícula (Matricula). (hasta 15 caracteres) | | `carta_porte.rail.cars[].guide` | string | sí | Guía del carro (GuiaCarro). (hasta 15 caracteres) | | `carta_porte.rail.cars[].net_tons` | number | sí | Toneladas netas del carro (ToneladasNetasCarro). (de 0 a 1000000) | | `carta_porte.rail.cars[].containers` | arreglo de objeto | | Contenedores (Contenedor). (hasta 10) | | `carta_porte.rail.cars[].containers[].type` | string | sí | Tipo de contenedor (TipoContenedor), c_ContenedorMaritimo o c_Contenedor en ferroviario. (hasta 4 caracteres) | | `carta_porte.rail.cars[].containers[].empty_kg` | number | sí | Peso del contenedor vacío en kg (PesoContenedorVacio). (de 0 a 100000000) | | `carta_porte.rail.cars[].containers[].net_kg` | number | sí | Peso neto en kg (PesoNeto). (de 0 a 100000000) | | `carta_porte.drivers` | arreglo de objeto | sí | Operadores (FiguraTransporte tipo 01): RFC, licencia y nombre. (hasta 5) | | `carta_porte.drivers[].tax_id` | string o `""` | sí | RFC del operador (RFCFigura); vacío si es extranjero (con foreign). | | `carta_porte.drivers[].license` | string | sí | Número de licencia (NumLicencia). (hasta 16 caracteres) | | `carta_porte.drivers[].name` | string | sí | Nombre del operador (NombreFigura). (hasta 254 caracteres) | | `carta_porte.drivers[].foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.drivers[].foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.drivers[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.figures` | arreglo de objeto | | Otras figuras de transporte: 02 propietario, 03 arrendador, 04 notificado, 05 integrante de coordinados. (hasta 10) | | `carta_porte.figures[].type` | `02`, `03`, `04`, `05` | sí | Tipo de figura (TipoFigura): 02 propietario, 03 arrendador, 04 notificado, 05 integrante. | | `carta_porte.figures[].tax_id` | string | | RFC de la figura (RFCFigura). | | `carta_porte.figures[].foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `carta_porte.figures[].foreign.tax_id` | string | sí | Número de registro de identidad fiscal en su país (NumRegIdTrib). (hasta 40 caracteres) | | `carta_porte.figures[].foreign.country` | string | sí | País de residencia fiscal, clave de 3 letras de c_Pais (USA, CAN…). (3 caracteres) | | `carta_porte.figures[].name` | string | sí | Nombre de la figura (NombreFigura). (hasta 254 caracteres) | | `carta_porte.figures[].parts` | arreglo de string | | Partes del transporte que posee o arrienda (ParteTransporte), c_ParteTransporte. (hasta 20) | | `carta_porte.figures[].address` | objeto | | Domicilio (Domicilio). En México basta el código postal: estado, municipio y localidad se toman de c_CodigoPostal. | | `carta_porte.figures[].address.street` | string | | Calle (Calle). (hasta 100 caracteres) | | `carta_porte.figures[].address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `carta_porte.figures[].address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `carta_porte.figures[].address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `carta_porte.figures[].address.country` | string | | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `carta_porte.figures[].address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `carta_porte.figures[].address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.figures[].address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `carta_porte.figures[].address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `ine` | objeto | | Complemento INE (procesos electorales). | | `ine.process` | `Ordinario`, `Precampaña`, `Campaña` | sí | Tipo de proceso (TipoProceso): Ordinario, Precampaña o Campaña. | | `ine.committee` | `Ejecutivo Nacional`, `Ejecutivo Estatal`, `Directivo Estatal` | | Tipo de comité (TipoComite), con el proceso Ordinario. | | `ine.accounting_id` | string | | Clave de contabilidad (IdContabilidad), hasta 6 dígitos. | | `ine.entities` | arreglo de objeto | | Entidades (Entidad): clave del estado, ámbito y claves de contabilidad. (hasta 64) | | `ine.entities[].key` | string | sí | Clave de la entidad (ClaveEntidad), 3 letras. (3 caracteres) | | `ine.entities[].scope` | `Local`, `Federal` | | Ámbito (Ambito): Local o Federal. | | `ine.entities[].accounting_ids` | arreglo de string | | Claves de contabilidad (IdContabilidad). (hasta 50) | | `leyendas` | arreglo de objeto | | Complemento de leyendas fiscales. (hasta 10) | | `leyendas[].text` | string | sí | Texto de la leyenda (textoLeyenda). (hasta 1000 caracteres) | | `leyendas[].disposition` | string | | Disposición fiscal (disposicionFiscal), por ejemplo LISR. (hasta 30 caracteres) | | `leyendas[].norm` | string | | Norma o artículo (norma). (hasta 100 caracteres) | | `donation` | objeto | | Complemento de donatarias. | | `donation.in_kind` | boolean | | true si el donativo es en especie (va sin forma de pago en efectivo). | | `donation.deducted` | boolean | | true si el donativo es deducible para quien lo da. | | `local_taxes` | arreglo de objeto | | Impuestos locales (complemento implocal): traslados y retenciones estatales. (hasta 20) | | `local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. (hasta 100 caracteres) | | `local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). (de 0 a 100) | | `local_taxes[].amount` | number | sí | Importe del impuesto local. (de 0 a 1000000000000) | | `export` | `01`, `02`, `03`, `04` | | Exportación (Exportacion), c_Exportacion: 01 no aplica (por omisión), 02 definitiva… | | `comercio_exterior` | objeto | | Complemento de comercio exterior 2.0. | | `comercio_exterior.motive` | `"05"` | | Motivo de traslado (MotivoTraslado): 05 envío de mercancías propias. | | `comercio_exterior.owners` | arreglo de objeto | | Propietarios de la mercancía si no es el emisor (Propietario), con motive 05. (hasta 10) | | `comercio_exterior.owners[].tax_id` | string | sí | Número de registro tributario del propietario (NumRegIdTrib). (hasta 40 caracteres) | | `comercio_exterior.owners[].country` | string | sí | País de residencia del propietario (ResidenciaFiscal), c_Pais. (3 caracteres) | | `comercio_exterior.incoterm` | string | | Incoterm (Incoterm), c_INCOTERM: EXW, FOB, CIF, DAP, DDP… (3 caracteres) | | `comercio_exterior.certificate` | string | | Número de certificado de origen (NumCertificadoOrigen). (hasta 40 caracteres) | | `comercio_exterior.usd_rate` | number | sí | Tipo de cambio a dólares (TipoCambioUSD). (de -∞ a 1000000) | | `comercio_exterior.issuer_curp` | string | | CURP del emisor persona física (Emisor Curp). (18 caracteres) | | `comercio_exterior.issuer_address` | objeto | | Domicilio del emisor (Emisor Domicilio): calle y números; lo demás sale de su código postal. | | `comercio_exterior.issuer_address.street` | string | sí | Calle (Calle). (hasta 100 caracteres) | | `comercio_exterior.issuer_address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `comercio_exterior.issuer_address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `comercio_exterior.issuer_address.zip` | string | | Código postal del emisor (CodigoPostal). | | `comercio_exterior.receiver_address` | objeto | | Domicilio del receptor en el extranjero (Receptor Domicilio). | | `comercio_exterior.receiver_address.street` | string | sí | Calle (Calle). (hasta 100 caracteres) | | `comercio_exterior.receiver_address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `comercio_exterior.receiver_address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `comercio_exterior.receiver_address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.receiver_address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.receiver_address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.receiver_address.state` | string | | Estado (Estado), c_Estado para USA y CAN. (hasta 30 caracteres) | | `comercio_exterior.receiver_address.country` | string | sí | País (Pais), c_Pais. (3 caracteres) | | `comercio_exterior.receiver_address.zip` | string | sí | Código postal en su país (CodigoPostal). (hasta 12 caracteres) | | `comercio_exterior.recipient` | objeto | | Destinatario, si no es el receptor (Destinatario). | | `comercio_exterior.recipient.name` | string | | Nombre del destinatario (Nombre). (hasta 300 caracteres) | | `comercio_exterior.recipient.tax_id` | string | | Número de registro tributario del destinatario (NumRegIdTrib). (hasta 40 caracteres) | | `comercio_exterior.recipient.address` | objeto | sí | Domicilio del destinatario (Domicilio). | | `comercio_exterior.recipient.address.street` | string | sí | Calle (Calle). (hasta 100 caracteres) | | `comercio_exterior.recipient.address.exterior` | string | | Número exterior (NumeroExterior). (hasta 55 caracteres) | | `comercio_exterior.recipient.address.interior` | string | | Número interior (NumeroInterior). (hasta 55 caracteres) | | `comercio_exterior.recipient.address.colony` | string | | Colonia (Colonia), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.recipient.address.locality` | string | | Localidad (Localidad), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.recipient.address.municipality` | string | | Municipio (Municipio), en el extranjero como texto. (hasta 120 caracteres) | | `comercio_exterior.recipient.address.state` | string | | Estado (Estado): en el extranjero c_Estado para USA y CAN, o texto. (hasta 30 caracteres) | | `comercio_exterior.recipient.address.country` | string | sí | País (Pais), c_Pais. Por omisión MEX. (3 caracteres) | | `comercio_exterior.recipient.address.zip` | string | sí | Código postal (CodigoPostal). En México se deducen de él estado, municipio y localidad. (hasta 12 caracteres) | | `comercio_exterior.goods` | arreglo de objeto | sí | Mercancías (Mercancia), una por concepto exportado. (hasta 200) | | `comercio_exterior.goods[].id` | string | | Número de identificación de la mercancía (NoIdentificacion): el sku de su concepto. (hasta 100 caracteres) | | `comercio_exterior.goods[].tariff` | string | | Fracción arancelaria (FraccionArancelaria), 10 dígitos. Transporte internacional. | | `comercio_exterior.goods[].unit` | string | sí | Unidad aduana (UnidadAduana), c_UnidadAduana. | | `comercio_exterior.goods[].quantity` | number | | Cantidad en la unidad aduana (CantidadAduana). (de -∞ a 100000000000000) | | `comercio_exterior.goods[].unit_value_usd` | number | | Valor unitario en dólares (ValorUnitarioAduana). (de 0 a 10000000000000000) | | `comercio_exterior.notes` | string | | Observaciones del complemento (Observaciones). (hasta 300 caracteres) | | `global` | objeto | | Factura global a público en general (InformacionGlobal): periodicidad, meses y año. | | `global.periodicity` | string | sí | Periodicidad (Periodicidad), c_Periodicidad: 01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral. | | `global.months` | string | sí | Meses (Meses), c_Meses: 01 a 12, o 13 a 18 bimestres. | | `global.year` | integer | sí | Año (Año). (de 2022 a 2100) | | `use` | string | | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `payment_form` | string | | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `payment_method` | `PUE`, `PPD` | | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `paid_later` | boolean | | Con PUE: true si aún no le pagan («Por cobrar» en la app) aunque se pagará este mes. | | `currency` | string | | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. (3 caracteres) | | `series` | string | | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. (hasta 25 caracteres) | | `notes` | string | | Observaciones que aparecen en el PDF (no van en el XML). (hasta 1000 caracteres) | | `pdf_language` | `es`, `en`, `bi` | | Idioma del PDF: es (por omisión) o en. | | `addenda` | objeto | | Addenda del receptor (Walmart, Soriana…), con sus propios campos. | | `related` | objeto | | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `related.relationship` | string | sí | Tipo de relación (TipoRelacion): 01 nota de crédito, 03 devolución, 04 sustitución, 07 anticipo… | | `related.uuids` | arreglo de string | sí | Folios fiscales relacionados. (hasta 20) | | `exchange_rate` | number | | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. (de -∞ a 1000000) | | `folio` | integer | | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. (de 1 a 2000000000) | | `continue_sequence` | boolean | | Con un folio elegido: true para que los siguientes sigan desde ese número. | | `date` | string | | Fecha de emisión (Fecha) si no es ahora, AAAA-MM-DDTHH:MM:SS hora de la Ciudad de México: hasta 72 horas antes. | | `send_email` | boolean | | true para mandar el PDF y el XML por correo al timbrar, con el diseño de Fiscalisto. Por omisión no se envía. (por omisión false) | | `email_cc` | string o null | | Correos con copia, separados por comas. | | `metadata` | objeto | | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | --- # InvoiceList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"invoice"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].branch` | string o null | sí | La sucursal (brn_…) desde donde se timbró, o null (el domicilio fiscal del emisor). Sus pagos y notas de crédito la siguen. | | `data[].type` | `I`, `E`, `P`, `T`, `N` | sí | Tipo de comprobante (TipoDeComprobante): I ingreso, E egreso, P pago, T traslado, N nómina. | | `data[].status` | `processing`, `pending`, `valid`, `rejected`, `canceled` | sí | processing: el PAC no ha contestado (se resuelve sola; escuche invoice.stamped). valid: timbrada. pending: timbrada con una cancelación en espera del cliente. rejected: el SAT o el PAC no la aceptaron (ver error). canceled: cancelada. | | `data[].uuid` | string o null | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `data[].series` | string o null | sí | Serie (Serie), hasta 25 caracteres. Cada serie lleva sus propios folios. | | `data[].folio` | string o null | sí | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. | | `data[].date` | string o null | sí | Fecha de emisión (Fecha), hora de la Ciudad de México. | | `data[].customer` | objeto | sí | Cliente (Receptor): el id de uno de su catálogo (cus_…) o sus datos en un objeto. | | `data[].customer.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].customer.tax_system` | string | | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `data[].customer.zip` | string | | Código postal del domicilio fiscal del cliente (DomicilioFiscalReceptor), 5 dígitos. | | `data[].customer.foreign` | objeto | | Si es extranjero: su país de residencia (ResidenciaFiscal, c_Pais) y su número de registro tributario (NumRegIdTrib), con el RFC genérico XEXX010101000. | | `data[].use` | string o null | sí | Uso del CFDI (UsoCFDI), clave de c_UsoCFDI. Por omisión G03, o S01 con régimen 616. | | `data[].payment_form` | string o null | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `data[].payment_method` | string o null | sí | Método de pago (MetodoPago): PUE (una sola exhibición, por omisión) o PPD (en parcialidades o diferido, se cobra con complementos de pago). | | `data[].currency` | string | sí | Moneda, clave ISO 4217 de c_Moneda (MXN, USD, EUR…). Por omisión MXN. | | `data[].exchange_rate` | number o null | sí | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. | | `data[].items` | arreglo de objeto | sí | Conceptos (Conceptos), al menos uno. | | `data[].subtotal` | number o null | sí | Suma de los importes antes de impuestos y descuentos (SubTotal). | | `data[].discount` | number o null | sí | Descuento (Descuento). | | `data[].taxes` | objeto o null | sí | Impuestos del comprobante, por tipo. | | `data[].taxes.iva` | number o null | sí | IVA trasladado. | | `data[].taxes.ieps` | number o null | sí | IEPS trasladado. | | `data[].taxes.iva_withheld` | number o null | sí | IVA retenido. | | `data[].taxes.isr_withheld` | number o null | sí | ISR retenido. | | `data[].taxes.local_transferred` | number o null | sí | Impuestos locales trasladados. | | `data[].taxes.local_withheld` | number o null | sí | Impuestos locales retenidos. | | `data[].total` | number o null | sí | Total en la moneda del comprobante (Total). | | `data[].payment_status` | `paid`, `partial`, `unpaid` o null | sí | Si ya se cobró (Cuentas por cobrar): paid, partial (complementos de pago o notas de crédito por una parte) o unpaid. Una PUE está paid, salvo con paid_later hasta POST /v1/invoices/{id}/mark_paid. null si no es de ingreso o no está vigente. | | `data[].amount_due` | number o null | sí | Lo que falta por cobrar, en su moneda, o null. | | `data[].amount_paid` | number o null | sí | Lo cobrado: complementos de pago y notas de crédito vigentes, o todo si es PUE pagada. null si no aplica. | | `data[].related` | objeto o null | sí | CFDI relacionados (CfdiRelacionados): tipo de relación de c_TipoRelacion y los UUID. | | `data[].payment` | objeto o null | | Solo en un complemento de pago (type P). | | `data[].payment.date` | string o null | sí | Fecha del pago (FechaPago). | | `data[].payment.payment_form` | string o null | sí | Forma de pago (FormaPago), clave de c_FormaPago: 01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 débito, 99 por definir… Obligatoria con PUE; con PPD es 99. | | `data[].payment.currency` | string | sí | Moneda del pago (MonedaP). | | `data[].payment.exchange_rate` | number o null | sí | Tipo de cambio a pesos (TipoCambio). Obligatorio si la moneda no es MXN; use el del DOF del día anterior. | | `data[].payment.amount` | number | sí | Monto del pago (Monto). | | `data[].payment.operation_number` | string o null | sí | Número de operación del pago (NumOperacion), como el rastreo SPEI. | | `data[].payment.documents` | arreglo de objeto | sí | Las facturas que paga (DoctoRelacionado). | | `data[].payment.documents[].uuid` | string | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `data[].payment.documents[].series` | string o null | sí | Serie de la factura pagada. | | `data[].payment.documents[].folio` | string o null | sí | Folio de la factura pagada. | | `data[].payment.documents[].currency` | string | sí | Moneda de la factura pagada (MonedaDR). | | `data[].payment.documents[].partiality` | integer | sí | Número de parcialidad (NumParcialidad). (de -9007199254740991 a 9007199254740991) | | `data[].payment.documents[].previous_balance` | number | sí | Saldo anterior de la factura (ImpSaldoAnt). | | `data[].payment.documents[].amount` | number | sí | Lo que se paga de esa factura (ImpPagado), en la moneda de la factura. | | `data[].payment.documents[].remaining` | number | sí | Saldo insoluto después de este pago (ImpSaldoInsoluto). | | `data[].payroll` | objeto | | Solo en un recibo de nómina (type N). | | `data[].payroll.periodicity` | string | sí | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `data[].payroll.period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `data[].payroll.period.start` | string | sí | Primer día del periodo (FechaInicialPago), AAAA-MM-DD. | | `data[].payroll.period.end` | string | sí | Último día del periodo (FechaFinalPago), AAAA-MM-DD. | | `data[].payroll.period.payment_date` | string | sí | Fecha de pago (FechaPago), AAAA-MM-DD. Por omisión el último día del periodo. | | `data[].payroll.days` | number | sí | Días pagados (NumDiasPagados). Por omisión, todos los del periodo que le tocan. | | `data[].payroll.employer` | objeto | sí | El patrón (Emisor del complemento): registro patronal y CURP si es persona física. | | `data[].payroll.employer.employer_registration` | string o null | sí | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `data[].payroll.employer.curp` | string o null | sí | CURP, 18 caracteres. | | `data[].payroll.employee` | objeto | sí | El empleado (emp_…). | | `data[].payroll.employee.id` | string o null | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].payroll.employee.number` | string | sí | Número de empleado (NumEmpleado). | | `data[].payroll.employee.curp` | string | sí | CURP, 18 caracteres. | | `data[].payroll.employee.social_security_number` | string o null | sí | Número de seguridad social (NumSeguridadSocial), 11 dígitos. | | `data[].payroll.employee.start_date` | string | sí | Fecha de inicio de la relación laboral (FechaInicioRelLaboral), AAAA-MM-DD. | | `data[].payroll.employee.contract_type` | string | sí | Tipo de contrato (TipoContrato), c_TipoContrato. Por omisión 01, por tiempo indeterminado. | | `data[].payroll.employee.unionized` | boolean | sí | true si es sindicalizado (Sindicalizado). | | `data[].payroll.employee.workday_type` | string | sí | Tipo de jornada (TipoJornada), c_TipoJornada. Por omisión 01, diurna. | | `data[].payroll.employee.regime_type` | string | sí | Tipo de régimen (TipoRegimen): 02 sueldos y salarios. | | `data[].payroll.employee.department` | string o null | | Departamento (Departamento). | | `data[].payroll.employee.position` | string o null | | Puesto (Puesto). | | `data[].payroll.employee.risk_class` | string o null | sí | Clase de riesgo del puesto ante el IMSS (RiesgoPuesto), 1 a 5. Por omisión 1. | | `data[].payroll.employee.contribution_base_salary` | number o null | sí | Salario base de cotización (SalarioBaseCotApor). | | `data[].payroll.employee.integrated_daily_salary` | number o null | sí | Salario diario integrado (SalarioDiarioIntegrado): no menor al salario diario. | | `data[].payroll.employee.state` | string | sí | Entidad federativa donde trabaja (ClaveEntFed): JAL, CMX, NLE… | | `data[].payroll.perceptions` | arreglo de objeto | sí | Percepciones (Percepcion): sueldo y demás, gravado y exento. | | `data[].payroll.perceptions[].type` | string | sí | Tipo de percepción (TipoPercepcion), c_TipoPercepcion: 001 sueldos… | | `data[].payroll.perceptions[].key` | string | sí | Clave del patrón (Clave). | | `data[].payroll.perceptions[].concept` | string | sí | Concepto (Concepto). | | `data[].payroll.perceptions[].taxed` | number | sí | Importe gravado (ImporteGravado). | | `data[].payroll.perceptions[].exempt` | number | sí | Importe exento (ImporteExento). | | `data[].payroll.deductions` | arreglo de objeto | sí | Deducciones (Deduccion): ISR, cuotas IMSS y demás. | | `data[].payroll.deductions[].type` | string | sí | Tipo de deducción (TipoDeduccion), c_TipoDeduccion: 001 IMSS, 002 ISR… | | `data[].payroll.deductions[].key` | string | sí | Clave del patrón (Clave). | | `data[].payroll.deductions[].concept` | string | sí | Concepto (Concepto). | | `data[].payroll.deductions[].amount` | number | sí | Importe (Importe). | | `data[].payroll.other_payments` | arreglo de objeto | sí | Otros pagos (OtroPago): subsidio para el empleo. | | `data[].payroll.other_payments[].type` | string | sí | Tipo de otro pago (TipoOtroPago), c_TipoOtroPago: 002 subsidio para el empleo. | | `data[].payroll.other_payments[].key` | string | sí | Clave del patrón (Clave). | | `data[].payroll.other_payments[].concept` | string | sí | Concepto (Concepto). | | `data[].payroll.other_payments[].amount` | number | sí | Importe pagado (Importe). | | `data[].payroll.other_payments[].subsidy_caused` | number | | Subsidio causado (SubsidioCausado). | | `data[].payroll.other_payments[].subsidy_applied` | number | | Del subsidio causado, el ISR que evitó (no va en el XML). | | `data[].global` | objeto | | Solo en una factura global. | | `data[].cancellation` | objeto o null | sí | La cancelación, si se pidió: estado, motivo, sustituto y cuándo. | | `data[].cancellation.status` | string o null | sí | pending (espera la respuesta del cliente), accepted (cancelada), rejected (el cliente la rechazó) o lapsed (venció; puede pedirla otra vez). | | `data[].cancellation.motive` | string o null | sí | Motivo de cancelación (c_MotivoCancelacion): 01 con errores con relación (lleva substitution), 02 con errores sin relación (por omisión), 03 no se llevó a cabo, 04 solo en una factura global, cuando una venta incluida se facturó a nombre del cliente. | | `data[].cancellation.substitution` | string o null | sí | El comprobante que sustituye a este (su id o su UUID), con el motivo 01. | | `data[].cancellation.canceled_at` | string (date-time) o null | sí | Cuándo se canceló. | | `data[].email` | objeto o null | sí | El envío por correo: a quién, con copia a quién y cuándo (null si no se ha enviado). | | `data[].email.to` | string o null | sí | A quién (correos separados por comas). | | `data[].email.cc` | string o null | sí | Con copia (correos separados por comas). | | `data[].email.sent_at` | string (date-time) o null | sí | Cuándo se envió. | | `data[].error` | objeto o null | sí | Por qué el SAT o el PAC no lo aceptaron: mensaje, código del SAT y texto original. | | `data[].error.message` | string | sí | Qué pasó y cómo corregirlo, para una persona. | | `data[].error.sat_code` | string o null | sí | Código del SAT o del PAC (CFDI40xxx). | | `data[].error.raw` | string | sí | El texto original del PAC. | | `data[].recurring_invoice` | string o null | sí | La factura recurrente (rin_…) que la timbró sola, o null si se timbró con una llamada. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `data[].warnings` | arreglo de string | | Avisos que no impidieron timbrar (por ejemplo, el XML se guardará después). | | `data[].email_error` | string | | Por qué no se pudo mandar el correo; el comprobante sí se timbró. | | `data[].substitutes` | objeto | | La factura que esta sustituyó y si quedó cancelada. | | `data[].substitutes.invoice` | string | sí | La factura sustituida (inv_…). | | `data[].substitutes.canceled` | boolean | sí | true si la sustituida ya quedó cancelada. | | `data[].substitutes.cancel_error` | string | | Por qué no se pudo cancelar la sustituida (cancélela con POST /v1/invoices/{id}/cancel). | | `data[].advance_application` | objeto | | La aplicación del anticipo: método, nota de aplicación y su estado. | | `data[].advance_application.advance` | string o null | sí | El anticipo, como factura (inv_…). | | `data[].advance_application.advance_uuid` | string | sí | El folio fiscal del anticipo. | | `data[].advance_application.method` | `egreso`, `remanente` | sí | egreso (la factura completa y después una nota de crédito por el anticipo, opción A) o remanente (la factura solo por lo que falta, opción B). | | `data[].advance_application.status` | `done`, `credit_note_pending` | sí | done (listo) o credit_note_pending (falta timbrar la nota de aplicación: repita la solicitud con la misma Idempotency-Key). | | `data[].advance_application.credit_note` | string o null | sí | La nota de aplicación del anticipo (inv_…), con method egreso. | | `data[].advance_application.credit_note_error` | string | | Por qué no se timbró la nota de aplicación (repita con la misma Idempotency-Key). | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Issuer Un emisor: un RFC que timbra desde su cuenta API. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"issuer"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `tax_system` | string o null | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `zip` | string o null | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `status` | `active`, `needs_certificate`, `certificate_expired`, `needs_efirma`, `archived` | sí | active: puede timbrar. needs_certificate: falta su CSD. needs_efirma: falta firmar una vez el contrato con el PAC con su e.firma (solo en producción). certificate_expired: su CSD venció. archived: archivado. | | `requirement` | string o null | sí | Qué le falta al emisor para timbrar, y cómo dárselo. | | `certificate` | objeto o null | sí | El sello digital (CSD) cargado: número de serie y vencimiento. | | `certificate.serial_number` | string o null | sí | Número de serie (NoCertificado). | | `certificate.expires_at` | string (date-time) o null | sí | Cuándo vence. | | `sat_downloads` | objeto | sí | Si puede pedirle al SAT sus facturas (POST /v1/sat_downloads): available es true en pruebas, y en producción mientras su e.firma guardada esté vigente (efirma_expires_at). | | `sat_downloads.available` | boolean | sí | true si puede pedir descargas al SAT ahora: siempre en pruebas; en producción con su e.firma guardada y vigente. | | `sat_downloads.efirma_expires_at` | string (date-time) o null | sí | Cuándo vence la e.firma guardada para las descargas, o null si no hay una. | | `employer_registration` | string o null | sí | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `curp` | string o null | sí | CURP, 18 caracteres. | | `stamps_as` | string | sí | RFC con el que se timbra: el del emisor en producción y el de pruebas del SAT (EKU9003173C9) en pruebas. | | `sandbox` | string o null | sí | El entorno de prueba (sbx_…) al que pertenece, o null si es de la cuenta. Vea /v1/sandboxes. | | `test_clock` | objeto o null | sí | Su reloj de prueba (solo emisores de prueba): la hora que es «hoy» para él, o null si va con la hora real. Vea POST /v1/issuers/{id}/test_clock. | | `test_clock.frozen_time` | string (date-time) | sí | La hora del reloj. | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `archived_at` | string (date-time) o null | sí | Cuándo se archivó (ya no timbra), o null. | | `metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | Ejemplo: ```json { "id": "iss_OufpKqnrvx9p", "object": "issuer", "livemode": false, "tax_id": "TOL190312KT4", "legal_name": "TOLPI", "tax_system": "601", "zip": "64000", "status": "active", "requirement": null, "certificate": { "serial_number": "30001000000500003416", "expires_at": "2027-05-18T05:43:51-06:00" }, "sat_downloads": { "available": true, "efirma_expires_at": null }, "employer_registration": null, "curp": null, "stamps_as": "EKU9003173C9", "sandbox": null, "test_clock": null, "created_at": "2026-10-24T10:15:42-06:00", "archived_at": null, "metadata": { "cliente": "42" } } ``` --- # IssuerList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"issuer"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].tax_system` | string o null | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `data[].zip` | string o null | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `data[].status` | `active`, `needs_certificate`, `certificate_expired`, `needs_efirma`, `archived` | sí | active: puede timbrar. needs_certificate: falta su CSD. needs_efirma: falta firmar una vez el contrato con el PAC con su e.firma (solo en producción). certificate_expired: su CSD venció. archived: archivado. | | `data[].requirement` | string o null | sí | Qué le falta al emisor para timbrar, y cómo dárselo. | | `data[].certificate` | objeto o null | sí | El sello digital (CSD) cargado: número de serie y vencimiento. | | `data[].certificate.serial_number` | string o null | sí | Número de serie (NoCertificado). | | `data[].certificate.expires_at` | string (date-time) o null | sí | Cuándo vence. | | `data[].sat_downloads` | objeto | sí | Si puede pedirle al SAT sus facturas (POST /v1/sat_downloads): available es true en pruebas, y en producción mientras su e.firma guardada esté vigente (efirma_expires_at). | | `data[].sat_downloads.available` | boolean | sí | true si puede pedir descargas al SAT ahora: siempre en pruebas; en producción con su e.firma guardada y vigente. | | `data[].sat_downloads.efirma_expires_at` | string (date-time) o null | sí | Cuándo vence la e.firma guardada para las descargas, o null si no hay una. | | `data[].employer_registration` | string o null | sí | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `data[].curp` | string o null | sí | CURP, 18 caracteres. | | `data[].stamps_as` | string | sí | RFC con el que se timbra: el del emisor en producción y el de pruebas del SAT (EKU9003173C9) en pruebas. | | `data[].sandbox` | string o null | sí | El entorno de prueba (sbx_…) al que pertenece, o null si es de la cuenta. Vea /v1/sandboxes. | | `data[].test_clock` | objeto o null | sí | Su reloj de prueba (solo emisores de prueba): la hora que es «hoy» para él, o null si va con la hora real. Vea POST /v1/issuers/{id}/test_clock. | | `data[].test_clock.frozen_time` | string (date-time) | sí | La hora del reloj. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].archived_at` | string (date-time) o null | sí | Cuándo se archivó (ya no timbra), o null. | | `data[].metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # IssuerOnboardingLink Una liga para que el dueño de un emisor suba su CSD y firme con su e.firma. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"issuer_onboarding_link"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor que se da de alta. | | `url` | string | sí | La página para el dueño del emisor, en fiscalisto.com. Ábrala en el navegador de su cliente; no la incruste en un iframe. | | `return_url` | string o null | sí | A dónde regresa al terminar. | | `expires_at` | string (date-time) o null | sí | Cuándo deja de servir la liga. Pida otra cuando quiera. | | `created_at` | string (date-time) o null | sí | Cuándo se creó. | Ejemplo: ```json { "object": "issuer_onboarding_link", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "url": "https://fiscalisto.com/alta/fko_8Xc1nQ2VbT0aLm3Rk7Yp9WdZs4Hf6Jg5Ue1Oi2Ky", "return_url": "https://miplataforma.mx/configuracion/facturas", "expires_at": "2026-10-25T10:15:00-06:00", "created_at": "2026-10-24T10:15:00-06:00" } ``` --- # JournalEntryList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | El número de la póliza: D-AAAAMM-NNNNN, en orden de fecha dentro del mes. | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"journal_entry"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].type` | `invoice`, `payment_received`, `payment_made`, `payroll`, `depreciation` | sí | De qué es: invoice (un CFDI de ingreso o egreso), payment_received o payment_made (un pago de un complemento), payroll (un recibo de nómina) o depreciation (la depreciación del mes de los activos fijos). | | `data[].date` | string | sí | La fecha del CFDI o del pago; en la depreciación, el último día del mes. | | `data[].description` | string | sí | El documento, su folio y la contraparte. | | `data[].invoice` | string o null | sí | La factura (inv_…) si es la póliza de un CFDI que timbró este emisor; null en los demás casos. | | `data[].uuid` | string o null | sí | El folio fiscal del CFDI (o del complemento de pago); null en la depreciación. | | `data[].tax_id` | string o null | sí | El RFC de la contraparte (cliente, proveedor o empleado); null en la depreciación. | | `data[].total` | number | sí | El total del documento, en su moneda. | | `data[].currency` | string | sí | La moneda del documento. | | `data[].exchange_rate` | number o null | sí | El tipo de cambio, si no es en pesos; null si es en pesos. | | `data[].lines` | arreglo de objeto | sí | Los movimientos, en pesos: el cargo (debit) o abono (credit) a cada cuenta. | | `data[].lines[].account` | string | sí | El número de cuenta. | | `data[].lines[].account_name` | string | sí | El nombre de la cuenta. | | `data[].lines[].grouping_code` | string | sí | Su código agrupador del SAT. | | `data[].lines[].debit` | number | sí | El cargo, en pesos. | | `data[].lines[].credit` | number | sí | El abono, en pesos. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # LedgerAccountList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].object` | `"ledger_account"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].number` | string | sí | El número de cuenta (NumCta). | | `data[].name` | string | sí | Su nombre. | | `data[].grouping_code` | string | sí | El código agrupador del SAT (Anexo 24). | | `data[].nature` | `debit`, `credit` | sí | debit (deudora) o credit (acreedora). | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # PayrollReceiptPreview El recibo como se timbraría: percepciones, deducciones (ISR, IMSS), otros pagos (subsidio) y el neto. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"payroll_receipt_preview"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `employee` | string | sí | El empleado (emp_…). | | `periodicity` | string | sí | Periodicidad del pago (PeriodicidadPago): 02 semanal, 03 catorcenal, 04 quincenal (por omisión), 05 mensual. | | `period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `period.start` | string | sí | Primer día del periodo (FechaInicialPago), AAAA-MM-DD. | | `period.end` | string | sí | Último día del periodo (FechaFinalPago), AAAA-MM-DD. | | `period.payment_date` | string | sí | Fecha de pago (FechaPago), AAAA-MM-DD. Por omisión el último día del periodo. | | `days` | number | sí | Días pagados (NumDiasPagados). Por omisión, todos los del periodo que le tocan. | | `contribution_base_salary` | number | sí | Salario base de cotización (SalarioBaseCotApor). | | `perceptions` | arreglo de objeto | sí | Percepciones (Percepcion): sueldo y demás, gravado y exento. | | `perceptions[].type` | string | sí | Tipo de percepción (TipoPercepcion), c_TipoPercepcion: 001 sueldos… | | `perceptions[].key` | string | sí | Clave del patrón (Clave). | | `perceptions[].concept` | string | sí | Concepto (Concepto). | | `perceptions[].taxed` | number | sí | Importe gravado (ImporteGravado). | | `perceptions[].exempt` | number | sí | Importe exento (ImporteExento). | | `deductions` | arreglo de objeto | sí | Deducciones (Deduccion): ISR, cuotas IMSS y demás. | | `deductions[].type` | string | sí | Tipo de deducción (TipoDeduccion), c_TipoDeduccion: 001 IMSS, 002 ISR… | | `deductions[].key` | string | sí | Clave del patrón (Clave). | | `deductions[].concept` | string | sí | Concepto (Concepto). | | `deductions[].amount` | number | sí | Importe (Importe). | | `other_payments` | arreglo de objeto | sí | Otros pagos (OtroPago): subsidio para el empleo. | | `other_payments[].type` | string | sí | Tipo de otro pago (TipoOtroPago), c_TipoOtroPago: 002 subsidio para el empleo. | | `other_payments[].key` | string | sí | Clave del patrón (Clave). | | `other_payments[].concept` | string | sí | Concepto (Concepto). | | `other_payments[].amount` | number | sí | Importe pagado (Importe). | | `other_payments[].subsidy_caused` | number | | Subsidio causado (SubsidioCausado). | | `other_payments[].subsidy_applied` | number | | Del subsidio causado, el ISR que evitó (no va en el XML). | | `isr` | objeto | sí | El cálculo del ISR del periodo: base, tarifa, subsidio y resultado. | | `isr.tariff` | number | sí | ISR según la tarifa del periodo. | | `isr.subsidy` | number | sí | Subsidio para el empleo causado. | | `isr.withheld` | number | sí | ISR retenido (tarifa menos subsidio, no menos de 0). | | `isr.exempt_minimum` | boolean | sí | true si gana el salario mínimo: no se le retiene ISR. | | `isr.adjustment` | objeto | | El ajuste de fin de mes: subsidio revertido y el ISR que retiene. | | `isr.adjustment.reversed` | number | sí | Subsidio causado antes en el mes que se revierte. | | `isr.adjustment.isr` | number | sí | El cálculo del ISR del periodo: base, tarifa, subsidio y resultado. | | `totals` | objeto | sí | Totales: percepciones, deducciones, otros pagos y el neto a pagar. | | `totals.salaries` | number | sí | Total de sueldos, salarios y asimilados (TotalSueldos). | | `totals.taxed` | number | sí | Total gravado (TotalGravado). | | `totals.exempt` | number | sí | Total exento (TotalExento). | | `totals.perceptions` | number | sí | Total de percepciones (TotalPercepciones). | | `totals.other_deductions` | number | sí | Otras deducciones (TotalOtrasDeducciones). | | `totals.taxes_withheld` | number | sí | Impuestos retenidos (TotalImpuestosRetenidos). | | `totals.deductions` | number | sí | Total de deducciones (TotalDeducciones). | | `totals.other_payments` | number | sí | Total de otros pagos (TotalOtrosPagos). | | `totals.subtotal` | number | sí | Subtotal del comprobante: percepciones más otros pagos. | | `totals.net` | number | sí | Neto a pagar al empleado. | Ejemplo: ```json { "object": "payroll_receipt_preview", "employee": "emp_OufpKqnrvx9p3RxmD1RIDgEHzKYPYnYBlV", "periodicity": "02", "period": { "start": "2026-10-10", "end": "2026-10-16", "payment_date": "2026-10-16" }, "days": 7, "contribution_base_salary": 334.66, "perceptions": [ { "type": "001", "key": "001", "concept": "Sueldo", "taxed": 2240, "exempt": 0 } ], "deductions": [ { "type": "001", "key": "001", "concept": "Cuotas IMSS", "amount": 55.63 }, { "type": "002", "key": "002", "concept": "ISR", "amount": 37.73 } ], "other_payments": [ { "type": "002", "key": "002", "concept": "Subsidio para el empleo del Decreto que otorga el subsidio para el empleo (DOF 1 de mayo de 2024)", "amount": 0, "subsidy_caused": 123.34, "subsidy_applied": 123.34 } ], "isr": { "tariff": 161.07, "subsidy": 123.34, "withheld": 37.73, "exempt_minimum": false }, "totals": { "salaries": 2240, "taxed": 2240, "exempt": 0, "perceptions": 2240, "other_deductions": 55.63, "taxes_withheld": 37.73, "deductions": 93.36, "other_payments": 0, "subtotal": 2240, "net": 2146.64 } } ``` --- # Product Un producto o servicio del catálogo de un emisor. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"product"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `name` | string o null | sí | Nombre corto para encontrarlo, solo para usted. | | `description` | string | sí | Descripción que va en el CFDI (Descripcion). | | `product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `unit_key` | string | sí | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… | | `unit_name` | string o null | sí | Nombre de la unidad (Unidad), hasta 20 caracteres. | | `sku` | string o null | sí | Número de identificación o SKU (NoIdentificacion). | | `unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). | | `open_price` | boolean | sí | Precio abierto: el producto no tiene precio fijo y cada concepto con él manda su unit_price. | | `taxability` | string | sí | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… | | `iva` | string | sí | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `ieps` | number o null | sí | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. | | `ieps_factor` | string o null | sí | Tasa o Cuota (TipoFactor del IEPS). | | `iva_retention` | number | sí | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). | | `isr_retention` | number | sí | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). | | `local_taxes` | arreglo de objeto | sí | Impuestos locales (complemento implocal): traslados y retenciones estatales. | | `local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. | | `local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). | | `track_stock` | boolean | sí | true si lleva existencias (Inventario). | | `cost` | number o null | sí | Su costo por unidad en pesos sin IVA, o null. | | `barcode` | string o null | sí | Su código de barras, o null. | | `min_stock` | number o null | sí | Su existencia mínima, o null. | | `created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez, ISO 8601 (-06:00). | Ejemplo: ```json { "id": "prd_OufpKqnrvx9p1", "object": "product", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": null, "description": "Mensualidad", "product_key": "86121700", "unit_key": "E48", "unit_name": "Unidad de servicio", "sku": "MEN-1", "unit_price": 2500, "open_price": false, "taxability": "02", "iva": "0.16", "ieps": null, "ieps_factor": null, "iva_retention": 0, "isr_retention": 0, "local_taxes": [], "track_stock": false, "cost": null, "barcode": null, "min_stock": null, "created_at": "2026-10-20T09:02:11-06:00", "updated_at": "2026-10-24T10:15:42-06:00" } ``` --- # ProductList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"product"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].name` | string o null | sí | Nombre corto para encontrarlo, solo para usted. | | `data[].description` | string | sí | Descripción que va en el CFDI (Descripcion). | | `data[].product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `data[].unit_key` | string | sí | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… | | `data[].unit_name` | string o null | sí | Nombre de la unidad (Unidad), hasta 20 caracteres. | | `data[].sku` | string o null | sí | Número de identificación o SKU (NoIdentificacion). | | `data[].unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). | | `data[].open_price` | boolean | sí | Precio abierto: el producto no tiene precio fijo y cada concepto con él manda su unit_price. | | `data[].taxability` | string | sí | Objeto de impuesto (ObjetoImp), c_ObjetoImp: 01 no objeto, 02 sí objeto (por omisión), 03 sin desglose, 04 no causa… | | `data[].iva` | string | sí | Tasa de IVA trasladado: 0.16 (por omisión), 0.08 (región fronteriza), 0 o exento. | | `data[].ieps` | number o null | sí | IEPS trasladado: tasa (0.265…) o cuota en pesos por unidad, según ieps_factor. | | `data[].ieps_factor` | string o null | sí | Tasa o Cuota (TipoFactor del IEPS). | | `data[].iva_retention` | number | sí | Tasa de IVA retenido (por ejemplo 0.106667, dos terceras partes del 16%). | | `data[].isr_retention` | number | sí | Tasa de ISR retenido (por ejemplo 0.10 honorarios, 0.0125 RESICO). | | `data[].local_taxes` | arreglo de objeto | sí | Impuestos locales (complemento implocal): traslados y retenciones estatales. | | `data[].local_taxes[].retention` | boolean | sí | true si es una retención local; false si es un traslado. | | `data[].local_taxes[].name` | string | sí | Nombre del impuesto local (ImpLocTrasladado o ImpLocRetenido), como ISH. | | `data[].local_taxes[].rate` | number | sí | Tasa en por ciento (TasadeTraslado o TasadeRetencion). | | `data[].track_stock` | boolean | sí | true si lleva existencias (Inventario). | | `data[].cost` | number o null | sí | Su costo por unidad en pesos sin IVA, o null. | | `data[].barcode` | string o null | sí | Su código de barras, o null. | | `data[].min_stock` | number o null | sí | Su existencia mínima, o null. | | `data[].created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez, ISO 8601 (-06:00). | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Quote Una cotización: su cliente la ve en una liga, la acepta, pide cambios o la rechaza, y se factura. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"quote"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `number` | string | sí | Su serie y folio, como lo ve el cliente (COT-12). | | `version` | integer | sí | Cambiar una ya enviada hace una versión nueva con el mismo número; el id contesta siempre la última. (de -9007199254740991 a 9007199254740991) | | `status` | `draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired`, `invoiced` | sí | draft, sent, viewed (el cliente abrió la liga), accepted, rejected, expired (pasó valid_until) o invoiced. | | `url` | string o null | sí | La liga donde el cliente la ve, descarga su PDF, la acepta, pide cambios o la rechaza; null mientras sea borrador. | | `customer` | objeto | sí | A quién va: tax_id null si aún no tiene sus datos fiscales; catalog_id es su cliente (cus_…) en su catálogo, o null. | | `customer.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.name` | string o null | sí | Nombre. | | `customer.email` | string o null | sí | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `customer.catalog_id` | string o null | sí | El cliente (cus_…) en su catálogo, o null si no está en él. | | `currency` | string | sí | La moneda. | | `items` | arreglo de objeto | sí | Los conceptos, como los de una factura. | | `items[].description` | string | sí | Descripción del concepto (Descripcion), hasta 1,000 caracteres. | | `items[].product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `items[].quantity` | number | | Cantidad (Cantidad). Por omisión 1. | | `items[].unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… | | `items[].unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). | | `total` | number o null | sí | El total con impuestos. | | `valid_until` | string | sí | Su último día de validez (AAAA-MM-DD). | | `terms` | string o null | sí | Las condiciones impresas: entrega, anticipo, garantía… | | `notes` | string o null | sí | Las notas, como las de una factura. | | `auto_invoice` | boolean | sí | Si al aceptarla se timbra la factura sola. | | `customer_comment` | string o null | sí | Lo que escribió el cliente al pedir cambios o al rechazarla, o null. | | `sent_at` | string (date-time) o null | sí | Cuándo se envió. | | `viewed_at` | string (date-time) o null | sí | Cuándo el cliente abrió la liga por primera vez. | | `decided_at` | string (date-time) o null | sí | Cuándo se aceptó o rechazó. | | `decided_by` | `customer`, `user` o null | sí | customer (en su liga) o user (usted, con POST /v1/quotes/{id}/accept o desde la app). | | `invoice` | string o null | sí | La factura (inv_…) que se timbró de ella, o null. | | `created_at` | string (date-time) o null | sí | Cuándo se creó. | Ejemplo: ```json { "id": "quo_OufpKqnrvx9p3Kd8wQm2XyT5uBcW8sKdLx", "object": "quote", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "number": "COT-12", "version": 1, "status": "sent", "url": "https://fiscalisto.com/cotizacion/7Hq2mZr1XyT5uBcW8sKdLx0VeNOufpKq", "customer": { "tax_id": "ICV060329BY0", "name": "INMOBILIARIA CVA", "email": "compras@cva.example", "catalog_id": "cus_OufpKqnrvx9p42" }, "currency": "MXN", "items": [ { "description": "Desarrollo del sitio web", "product_key": "81112103", "quantity": 1, "unit_key": "E48", "unit_price": 45000 } ], "total": 52200, "valid_until": "2026-11-09", "terms": "50% de anticipo, entrega en 4 semanas.", "notes": null, "auto_invoice": true, "customer_comment": null, "sent_at": "2026-10-25T10:12:00-06:00", "viewed_at": null, "decided_at": null, "decided_by": null, "invoice": null, "created_at": "2026-10-25T10:05:00-06:00" } ``` --- # QuoteList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"quote"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].number` | string | sí | Su serie y folio, como lo ve el cliente (COT-12). | | `data[].version` | integer | sí | Cambiar una ya enviada hace una versión nueva con el mismo número; el id contesta siempre la última. (de -9007199254740991 a 9007199254740991) | | `data[].status` | `draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired`, `invoiced` | sí | draft, sent, viewed (el cliente abrió la liga), accepted, rejected, expired (pasó valid_until) o invoiced. | | `data[].url` | string o null | sí | La liga donde el cliente la ve, descarga su PDF, la acepta, pide cambios o la rechaza; null mientras sea borrador. | | `data[].customer` | objeto | sí | A quién va: tax_id null si aún no tiene sus datos fiscales; catalog_id es su cliente (cus_…) en su catálogo, o null. | | `data[].customer.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].customer.name` | string o null | sí | Nombre. | | `data[].customer.email` | string o null | sí | Correo a donde se envía (varios separados por comas). Solo se usa si se pide enviarlo. | | `data[].customer.catalog_id` | string o null | sí | El cliente (cus_…) en su catálogo, o null si no está en él. | | `data[].currency` | string | sí | La moneda. | | `data[].items` | arreglo de objeto | sí | Los conceptos, como los de una factura. | | `data[].items[].description` | string | sí | Descripción del concepto (Descripcion), hasta 1,000 caracteres. | | `data[].items[].product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `data[].items[].quantity` | number | | Cantidad (Cantidad). Por omisión 1. | | `data[].items[].unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… | | `data[].items[].unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). | | `data[].total` | number o null | sí | El total con impuestos. | | `data[].valid_until` | string | sí | Su último día de validez (AAAA-MM-DD). | | `data[].terms` | string o null | sí | Las condiciones impresas: entrega, anticipo, garantía… | | `data[].notes` | string o null | sí | Las notas, como las de una factura. | | `data[].auto_invoice` | boolean | sí | Si al aceptarla se timbra la factura sola. | | `data[].customer_comment` | string o null | sí | Lo que escribió el cliente al pedir cambios o al rechazarla, o null. | | `data[].sent_at` | string (date-time) o null | sí | Cuándo se envió. | | `data[].viewed_at` | string (date-time) o null | sí | Cuándo el cliente abrió la liga por primera vez. | | `data[].decided_at` | string (date-time) o null | sí | Cuándo se aceptó o rechazó. | | `data[].decided_by` | `customer`, `user` o null | sí | customer (en su liga) o user (usted, con POST /v1/quotes/{id}/accept o desde la app). | | `data[].invoice` | string o null | sí | La factura (inv_…) que se timbró de ella, o null. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # ReceivableList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | El RFC y la moneda (RFC:MXN): para starting_after. | | `data[].object` | `"receivable"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].issuer` | string | sí | El emisor. | | `data[].customer` | objeto | sí | El cliente que debe. | | `data[].customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].currency` | string | sí | La moneda de esas facturas. | | `data[].amount_due` | number | sí | Lo que debe en total, en esa moneda. | | `data[].open_invoices` | integer | sí | Cuántas facturas tiene abiertas. (de -9007199254740991 a 9007199254740991) | | `data[].oldest_invoice_date` | string | sí | La fecha (AAAA-MM-DD) de su factura abierta más antigua. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # RecurringInvoice Una factura que el emisor timbra solo, cada mes, semana o día. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"recurring_invoice"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `name` | string | sí | Para reconocerla. | | `status` | `active`, `paused` | sí | active (timbra en cada fecha) o paused. | | `schedule` | objeto | sí | Cada cuánto: frequency (monthly, weekly o daily), interval, day_of_month (con monthly; "last" es el último del mes), y la hora (hour, en time_zone) en que se timbra. | | `schedule.frequency` | `monthly`, `weekly`, `daily` | sí | monthly: cada interval meses en day_of_month. weekly o daily: cada interval semanas o días. | | `schedule.interval` | integer | sí | Cada cuántos meses, semanas o días. (de -9007199254740991 a 9007199254740991) | | `schedule.day_of_month` | integer o `"last"` o null | sí | Con monthly: el día del mes, del 1 al 28, o "last" (el último). null con weekly o daily. | | `schedule.hour` | integer | sí | La hora (0 a 23) en que se timbra, en time_zone. (de -9007199254740991 a 9007199254740991) | | `schedule.time_zone` | string | sí | La zona de esa hora (America/Mexico_City, America/Cancun…). | | `next_invoice_on` | string | sí | La fecha (AAAA-MM-DD) de la siguiente factura. | | `last_invoiced_on` | string o null | sí | La fecha del último periodo que timbró, o null si aún no timbra ninguna. | | `last_invoice` | string o null | sí | La última factura (inv_…) que timbró, o null. | | `last_error` | objeto o null | sí | Por qué no se pudo timbrar la última vez (sin timbres, el cliente borrado, el SAT la rechazó…), o null. Se vuelve a intentar a la siguiente hora del día siguiente; también llega recurring_invoice.failed. | | `last_error.message` | string o null | sí | Qué pasó. | | `send_email` | boolean | sí | Si cada factura se manda al correo del cliente. | | `catch_up` | `all`, `latest` | sí | Si quedaron periodos sin timbrar: all los timbra todos, latest solo el más reciente. | | `customer` | objeto | sí | A quién se factura. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `currency` | string | sí | La moneda de la factura. | | `items` | arreglo de objeto | sí | Los conceptos, con description tal como se guardó ({mes}, {año}… se llenan en cada factura). | | `items[].description` | string | sí | Descripción del concepto (Descripcion), hasta 1,000 caracteres. | | `items[].product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `items[].quantity` | number | | Cantidad (Cantidad). Por omisión 1. | | `items[].unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… | | `items[].unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). | | `total` | number o null | sí | El total de cada factura con los precios de hoy. | | `created_at` | string (date-time) o null | sí | Cuándo se creó. | Ejemplo: ```json { "id": "rin_OufpKqnrvx9p7Hq2mZr1XyT5uBcW8sKdLx", "object": "recurring_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Renta oficina 3", "status": "active", "schedule": { "frequency": "monthly", "interval": 1, "day_of_month": 1, "hour": 7, "time_zone": "America/Mexico_City" }, "next_invoice_on": "2026-11-01", "last_invoiced_on": "2026-10-01", "last_invoice": "inv_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "last_error": null, "send_email": true, "catch_up": "all", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA" }, "currency": "MXN", "items": [ { "description": "Renta de oficina de {mes} {año}", "product_key": "80131500", "quantity": 1, "unit_key": "E48", "unit_price": 15000 } ], "total": 17400, "created_at": "2026-09-28T11:02:14-06:00" } ``` --- # RecurringInvoiceList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"recurring_invoice"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].name` | string | sí | Para reconocerla. | | `data[].status` | `active`, `paused` | sí | active (timbra en cada fecha) o paused. | | `data[].schedule` | objeto | sí | Cada cuánto: frequency (monthly, weekly o daily), interval, day_of_month (con monthly; "last" es el último del mes), y la hora (hour, en time_zone) en que se timbra. | | `data[].schedule.frequency` | `monthly`, `weekly`, `daily` | sí | monthly: cada interval meses en day_of_month. weekly o daily: cada interval semanas o días. | | `data[].schedule.interval` | integer | sí | Cada cuántos meses, semanas o días. (de -9007199254740991 a 9007199254740991) | | `data[].schedule.day_of_month` | integer o `"last"` o null | sí | Con monthly: el día del mes, del 1 al 28, o "last" (el último). null con weekly o daily. | | `data[].schedule.hour` | integer | sí | La hora (0 a 23) en que se timbra, en time_zone. (de -9007199254740991 a 9007199254740991) | | `data[].schedule.time_zone` | string | sí | La zona de esa hora (America/Mexico_City, America/Cancun…). | | `data[].next_invoice_on` | string | sí | La fecha (AAAA-MM-DD) de la siguiente factura. | | `data[].last_invoiced_on` | string o null | sí | La fecha del último periodo que timbró, o null si aún no timbra ninguna. | | `data[].last_invoice` | string o null | sí | La última factura (inv_…) que timbró, o null. | | `data[].last_error` | objeto o null | sí | Por qué no se pudo timbrar la última vez (sin timbres, el cliente borrado, el SAT la rechazó…), o null. Se vuelve a intentar a la siguiente hora del día siguiente; también llega recurring_invoice.failed. | | `data[].last_error.message` | string o null | sí | Qué pasó. | | `data[].send_email` | boolean | sí | Si cada factura se manda al correo del cliente. | | `data[].catch_up` | `all`, `latest` | sí | Si quedaron periodos sin timbrar: all los timbra todos, latest solo el más reciente. | | `data[].customer` | objeto | sí | A quién se factura. | | `data[].customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].currency` | string | sí | La moneda de la factura. | | `data[].items` | arreglo de objeto | sí | Los conceptos, con description tal como se guardó ({mes}, {año}… se llenan en cada factura). | | `data[].items[].description` | string | sí | Descripción del concepto (Descripcion), hasta 1,000 caracteres. | | `data[].items[].product_key` | string | sí | Clave del producto o servicio (ClaveProdServ), 8 dígitos de c_ClaveProdServ. Búsquela con GET /v1/sat_catalogs/product_keys?q=… | | `data[].items[].quantity` | number | | Cantidad (Cantidad). Por omisión 1. | | `data[].items[].unit_key` | string | | Clave de unidad (ClaveUnidad) de c_ClaveUnidad: E48 unidad de servicio (por omisión), H87 pieza, ACT actividad… | | `data[].items[].unit_price` | number | sí | Precio unitario antes de impuestos (ValorUnitario). | | `data[].total` | number o null | sí | El total de cada factura con los precios de hoy. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Retention Una constancia de retenciones e información de pagos (CFDI de retenciones 2.0). | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"retention"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor (iss_…). | | `status` | `processing`, `valid`, `rejected`, `canceled` | sí | processing: el PAC no ha contestado. valid: timbrada. rejected: el SAT o el PAC no la aceptaron (ver error). canceled: cancelada. | | `uuid` | string o null | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `folio` | integer o null | sí | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. | | `internal_folio` | string o null | sí | Folio interno (FolioInt): RET1, RET2… | | `date` | string o null | sí | Fecha de emisión (Fecha), hora de la Ciudad de México. | | `code` | string | sí | Clave de la retención (CveRetenc), c_CveRetenc: 01 servicios profesionales, 05 arrendamiento, 14 dividendos, 16 intereses, 18 pagos al extranjero… Las claves 14, 16, 17 y 18 a 24 llevan su complemento (dividends, interest, trust_lease, foreign_payment, share_sale, prize, trust, retirement_plan, mortgage_interest, financial_derivatives); la 26 (plataformas tecnológicas) aún no se puede emitir. | | `description` | string o null | sí | Descripción de la retención (DescRetenc). | | `recipient` | objeto | sí | Receptor de la constancia (Receptor): nacional con RFC y código postal, o extranjero. | | `recipient.foreign` | boolean | sí | true si el receptor es extranjero (sin RFC). | | `recipient.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `recipient.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `recipient.zip` | string | | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `recipient.curp` | string o null | | CURP, 18 caracteres. | | `period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `period.year` | integer | sí | Año (Ejercicio). (de -9007199254740991 a 9007199254740991) | | `period.from_month` | integer | sí | Mes inicial (MesIni), 1 a 12. (de -9007199254740991 a 9007199254740991) | | `period.to_month` | integer | sí | Mes final (MesFin), 1 a 12, del mismo año. (de -9007199254740991 a 9007199254740991) | | `totals` | objeto | sí | Totales (Totales). | | `totals.operation` | number o null | sí | Monto total de la operación (MontoTotOperacion). | | `totals.taxed` | number o null | sí | Monto gravado (MontoTotGrav). Por omisión, operation menos exempt. | | `totals.exempt` | number o null | sí | Monto exento (MontoTotExent). Por omisión 0. | | `totals.withheld` | number o null | sí | Total retenido (MontoTotRet). | | `taxes` | arreglo de objeto | sí | Impuestos del comprobante, por tipo. | | `taxes[].tax` | string | sí | Impuesto: isr, iva o ieps (ImpuestoRet 001, 002, 003). | | `taxes[].base` | number | sí | Base del impuesto retenido (BaseRet). | | `taxes[].amount` | number | sí | Monto retenido (MontoRet). | | `taxes[].payment_type` | string | sí | Tipo de pago de la retención (TipoPagoRet): 01 definitivo IVA, 02 definitivo IEPS, 03 definitivo ISR, 04 provisional ISR. Por omisión el usual de cada impuesto. | | `dividends` | objeto | | Complemento de dividendos. | | `interest` | objeto | | Complemento de intereses. | | `foreign_payment` | objeto | | Complemento de pagos a extranjeros. | | `trust_lease` | objeto | | Complemento de arrendamiento en fideicomiso (Arrendamientoenfideicomiso 1.0), obligatorio con code 17. | | `share_sale` | objeto | | Complemento de enajenación de acciones u operaciones de valores (EnajenaciondeAcciones 1.0), obligatorio con code 19. | | `prize` | objeto | | Complemento de premios (Premios 1.0), obligatorio con code 20. Sus montos son los de totals. | | `trust` | objeto | | Complemento de fideicomisos que no realizan actividades empresariales (Fideicomisonoempresarial 1.0), obligatorio con code 21. | | `retirement_plan` | objeto | | Complemento de planes personales de retiro (Planesderetiro 1.1), obligatorio con code 22: lo del año anterior. | | `mortgage_interest` | objeto | | Complemento de intereses reales deducibles por créditos hipotecarios (Intereseshipotecarios 1.0), obligatorio con code 23. | | `financial_derivatives` | objeto | | Complemento de operaciones financieras derivadas de capital (Operacionesconderivados 1.0), obligatorio con code 24. | | `cancellation` | objeto o null | sí | La cancelación, si se pidió: estado, motivo, sustituto y cuándo. | | `cancellation.status` | string | sí | pending (espera la respuesta del cliente), accepted (cancelada), rejected (el cliente la rechazó) o lapsed (venció; puede pedirla otra vez). | | `cancellation.motive` | string o null | sí | Motivo de cancelación (c_MotivoCancelacion): 01 con errores con relación (lleva substitution), 02 con errores sin relación (por omisión), 03 no se llevó a cabo, 04 solo en una factura global, cuando una venta incluida se facturó a nombre del cliente. | | `cancellation.canceled_at` | string (date-time) o null | sí | Cuándo se canceló. | | `email` | objeto o null | sí | El envío por correo: a quién, con copia a quién y cuándo (null si no se ha enviado). | | `email.to` | string o null | sí | A quién (correos separados por comas). | | `email.sent_at` | string (date-time) o null | sí | Cuándo se envió. | | `error` | objeto o null | sí | Por qué el SAT o el PAC no lo aceptaron: mensaje, código del SAT y texto original. | | `error.message` | string | sí | Qué pasó y cómo corregirlo, para una persona. | | `error.sat_code` | string o null | sí | Código del SAT o del PAC (CFDI40xxx). | | `error.raw` | string | sí | El texto original del PAC. | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `warnings` | arreglo de string | | Avisos que no impidieron timbrar (por ejemplo, el XML se guardará después). | | `email_error` | string | | Por qué no se pudo mandar el correo; el comprobante sí se timbró. | Ejemplo: ```json { "id": "ret_OufpKqnrvx9p5tENFcmUXpXJ58XxnF67kv", "object": "retention", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "valid", "uuid": "9A6F6BF3-6786-4747-800A-C9964E6509AE", "folio": 1, "internal_folio": "RET1", "date": "2026-10-24T10:15:42", "code": "01", "description": null, "recipient": { "foreign": false, "tax_id": "PEPJ800101AB1", "legal_name": "JUAN PEREZ PEREZ", "zip": "33826", "curp": null }, "period": { "year": 2026, "from_month": 9, "to_month": 9 }, "totals": { "operation": 10000, "taxed": 10000, "exempt": 0, "withheld": 2066.67 }, "taxes": [ { "tax": "isr", "base": 10000, "amount": 1000, "payment_type": "04" }, { "tax": "iva", "base": 10000, "amount": 1066.67, "payment_type": "01" } ], "cancellation": null, "email": null, "error": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": { "proveedor": "42" } } ``` --- # RetentionList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"retention"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor (iss_…). | | `data[].status` | `processing`, `valid`, `rejected`, `canceled` | sí | processing: el PAC no ha contestado. valid: timbrada. rejected: el SAT o el PAC no la aceptaron (ver error). canceled: cancelada. | | `data[].uuid` | string o null | sí | Folio fiscal que asigna el SAT (UUID del Timbre Fiscal Digital), en mayúsculas. | | `data[].folio` | integer o null | sí | Folio (Folio). Fiscalisto asigna el siguiente de la serie; mande uno solo si debe ser ese. | | `data[].internal_folio` | string o null | sí | Folio interno (FolioInt): RET1, RET2… | | `data[].date` | string o null | sí | Fecha de emisión (Fecha), hora de la Ciudad de México. | | `data[].code` | string | sí | Clave de la retención (CveRetenc), c_CveRetenc: 01 servicios profesionales, 05 arrendamiento, 14 dividendos, 16 intereses, 18 pagos al extranjero… Las claves 14, 16, 17 y 18 a 24 llevan su complemento (dividends, interest, trust_lease, foreign_payment, share_sale, prize, trust, retirement_plan, mortgage_interest, financial_derivatives); la 26 (plataformas tecnológicas) aún no se puede emitir. | | `data[].description` | string o null | sí | Descripción de la retención (DescRetenc). | | `data[].recipient` | objeto | sí | Receptor de la constancia (Receptor): nacional con RFC y código postal, o extranjero. | | `data[].recipient.foreign` | boolean | sí | true si el receptor es extranjero (sin RFC). | | `data[].recipient.tax_id` | string o null | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].recipient.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].recipient.zip` | string | | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `data[].recipient.curp` | string o null | | CURP, 18 caracteres. | | `data[].period` | objeto | sí | Periodo (Periodo): mes inicial, mes final y año. | | `data[].period.year` | integer | sí | Año (Ejercicio). (de -9007199254740991 a 9007199254740991) | | `data[].period.from_month` | integer | sí | Mes inicial (MesIni), 1 a 12. (de -9007199254740991 a 9007199254740991) | | `data[].period.to_month` | integer | sí | Mes final (MesFin), 1 a 12, del mismo año. (de -9007199254740991 a 9007199254740991) | | `data[].totals` | objeto | sí | Totales (Totales). | | `data[].totals.operation` | number o null | sí | Monto total de la operación (MontoTotOperacion). | | `data[].totals.taxed` | number o null | sí | Monto gravado (MontoTotGrav). Por omisión, operation menos exempt. | | `data[].totals.exempt` | number o null | sí | Monto exento (MontoTotExent). Por omisión 0. | | `data[].totals.withheld` | number o null | sí | Total retenido (MontoTotRet). | | `data[].taxes` | arreglo de objeto | sí | Impuestos del comprobante, por tipo. | | `data[].taxes[].tax` | string | sí | Impuesto: isr, iva o ieps (ImpuestoRet 001, 002, 003). | | `data[].taxes[].base` | number | sí | Base del impuesto retenido (BaseRet). | | `data[].taxes[].amount` | number | sí | Monto retenido (MontoRet). | | `data[].taxes[].payment_type` | string | sí | Tipo de pago de la retención (TipoPagoRet): 01 definitivo IVA, 02 definitivo IEPS, 03 definitivo ISR, 04 provisional ISR. Por omisión el usual de cada impuesto. | | `data[].dividends` | objeto | | Complemento de dividendos. | | `data[].interest` | objeto | | Complemento de intereses. | | `data[].foreign_payment` | objeto | | Complemento de pagos a extranjeros. | | `data[].trust_lease` | objeto | | Complemento de arrendamiento en fideicomiso (Arrendamientoenfideicomiso 1.0), obligatorio con code 17. | | `data[].share_sale` | objeto | | Complemento de enajenación de acciones u operaciones de valores (EnajenaciondeAcciones 1.0), obligatorio con code 19. | | `data[].prize` | objeto | | Complemento de premios (Premios 1.0), obligatorio con code 20. Sus montos son los de totals. | | `data[].trust` | objeto | | Complemento de fideicomisos que no realizan actividades empresariales (Fideicomisonoempresarial 1.0), obligatorio con code 21. | | `data[].retirement_plan` | objeto | | Complemento de planes personales de retiro (Planesderetiro 1.1), obligatorio con code 22: lo del año anterior. | | `data[].mortgage_interest` | objeto | | Complemento de intereses reales deducibles por créditos hipotecarios (Intereseshipotecarios 1.0), obligatorio con code 23. | | `data[].financial_derivatives` | objeto | | Complemento de operaciones financieras derivadas de capital (Operacionesconderivados 1.0), obligatorio con code 24. | | `data[].cancellation` | objeto o null | sí | La cancelación, si se pidió: estado, motivo, sustituto y cuándo. | | `data[].cancellation.status` | string | sí | pending (espera la respuesta del cliente), accepted (cancelada), rejected (el cliente la rechazó) o lapsed (venció; puede pedirla otra vez). | | `data[].cancellation.motive` | string o null | sí | Motivo de cancelación (c_MotivoCancelacion): 01 con errores con relación (lleva substitution), 02 con errores sin relación (por omisión), 03 no se llevó a cabo, 04 solo en una factura global, cuando una venta incluida se facturó a nombre del cliente. | | `data[].cancellation.canceled_at` | string (date-time) o null | sí | Cuándo se canceló. | | `data[].email` | objeto o null | sí | El envío por correo: a quién, con copia a quién y cuándo (null si no se ha enviado). | | `data[].email.to` | string o null | sí | A quién (correos separados por comas). | | `data[].email.sent_at` | string (date-time) o null | sí | Cuándo se envió. | | `data[].error` | objeto o null | sí | Por qué el SAT o el PAC no lo aceptaron: mensaje, código del SAT y texto original. | | `data[].error.message` | string | sí | Qué pasó y cómo corregirlo, para una persona. | | `data[].error.sat_code` | string o null | sí | Código del SAT o del PAC (CFDI40xxx). | | `data[].error.raw` | string | sí | El texto original del PAC. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `data[].warnings` | arreglo de string | | Avisos que no impidieron timbrar (por ejemplo, el XML se guardará después). | | `data[].email_error` | string | | Por qué no se pudo mandar el correo; el comprobante sí se timbró. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # Sandbox Un entorno de prueba: el modo de pruebas propio de una persona, con su emisor, llaves, webhooks y eventos aparte. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `object` | `"sandbox"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | `false` | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `name` | string | sí | Nombre. | | `issuers` | integer | sí | Emisores activos en el entorno. (de -9007199254740991 a 9007199254740991) | | `issuer` | objeto o null | | El emisor de pruebas del SAT con que nace (solo al crearlo). | | `issuer.id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `issuer.livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer.object` | `"issuer"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `issuer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `issuer.tax_system` | string o null | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `issuer.zip` | string o null | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `issuer.status` | `active`, `needs_certificate`, `certificate_expired`, `needs_efirma`, `archived` | sí | active: puede timbrar. needs_certificate: falta su CSD. needs_efirma: falta firmar una vez el contrato con el PAC con su e.firma (solo en producción). certificate_expired: su CSD venció. archived: archivado. | | `issuer.requirement` | string o null | sí | Qué le falta al emisor para timbrar, y cómo dárselo. | | `issuer.certificate` | objeto o null | sí | El sello digital (CSD) cargado: número de serie y vencimiento. | | `issuer.certificate.serial_number` | string o null | sí | Número de serie (NoCertificado). | | `issuer.certificate.expires_at` | string (date-time) o null | sí | Cuándo vence. | | `issuer.sat_downloads` | objeto | sí | Si puede pedirle al SAT sus facturas (POST /v1/sat_downloads): available es true en pruebas, y en producción mientras su e.firma guardada esté vigente (efirma_expires_at). | | `issuer.sat_downloads.available` | boolean | sí | true si puede pedir descargas al SAT ahora: siempre en pruebas; en producción con su e.firma guardada y vigente. | | `issuer.sat_downloads.efirma_expires_at` | string (date-time) o null | sí | Cuándo vence la e.firma guardada para las descargas, o null si no hay una. | | `issuer.employer_registration` | string o null | sí | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `issuer.curp` | string o null | sí | CURP, 18 caracteres. | | `issuer.stamps_as` | string | sí | RFC con el que se timbra: el del emisor en producción y el de pruebas del SAT (EKU9003173C9) en pruebas. | | `issuer.sandbox` | string o null | sí | El entorno de prueba (sbx_…) al que pertenece, o null si es de la cuenta. Vea /v1/sandboxes. | | `issuer.test_clock` | objeto o null | sí | Su reloj de prueba (solo emisores de prueba): la hora que es «hoy» para él, o null si va con la hora real. Vea POST /v1/issuers/{id}/test_clock. | | `issuer.test_clock.frozen_time` | string (date-time) | sí | La hora del reloj. | | `issuer.created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `issuer.archived_at` | string (date-time) o null | sí | Cuándo se archivó (ya no timbra), o null. | | `issuer.metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `key` | objeto | | Solo al crearlo. | | `key.id` | string | sí | Id de la llave. | | `key.secret` | string | sí | La llave fk_test_… del entorno. Solo se ve esta vez: guárdela. | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `deleted` | boolean | | Siempre true. | Ejemplo: ```json { "id": "sbx_4kQm8ZtR2wLp9XbN", "object": "sandbox", "livemode": false, "name": "Ana", "issuers": 1, "created_at": "2026-10-25T09:40:00-06:00" } ``` --- # SandboxList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].object` | `"sandbox"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].livemode` | `false` | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].name` | string | sí | Nombre. | | `data[].issuers` | integer | sí | Emisores activos en el entorno. (de -9007199254740991 a 9007199254740991) | | `data[].issuer` | objeto o null | | El emisor de pruebas del SAT con que nace (solo al crearlo). | | `data[].issuer.id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].issuer.livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].issuer.object` | `"issuer"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].issuer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].issuer.tax_system` | string o null | sí | Régimen fiscal (RegimenFiscal), clave de c_RegimenFiscal: 601, 612, 626… | | `data[].issuer.zip` | string o null | sí | Código postal del domicilio fiscal, 5 dígitos, como en la constancia de situación fiscal. El de un emisor es el lugar de expedición (LugarExpedicion) de sus comprobantes. | | `data[].issuer.status` | `active`, `needs_certificate`, `certificate_expired`, `needs_efirma`, `archived` | sí | active: puede timbrar. needs_certificate: falta su CSD. needs_efirma: falta firmar una vez el contrato con el PAC con su e.firma (solo en producción). certificate_expired: su CSD venció. archived: archivado. | | `data[].issuer.requirement` | string o null | sí | Qué le falta al emisor para timbrar, y cómo dárselo. | | `data[].issuer.certificate` | objeto o null | sí | El sello digital (CSD) cargado: número de serie y vencimiento. | | `data[].issuer.certificate.serial_number` | string o null | sí | Número de serie (NoCertificado). | | `data[].issuer.certificate.expires_at` | string (date-time) o null | sí | Cuándo vence. | | `data[].issuer.sat_downloads` | objeto | sí | Si puede pedirle al SAT sus facturas (POST /v1/sat_downloads): available es true en pruebas, y en producción mientras su e.firma guardada esté vigente (efirma_expires_at). | | `data[].issuer.sat_downloads.available` | boolean | sí | true si puede pedir descargas al SAT ahora: siempre en pruebas; en producción con su e.firma guardada y vigente. | | `data[].issuer.sat_downloads.efirma_expires_at` | string (date-time) o null | sí | Cuándo vence la e.firma guardada para las descargas, o null si no hay una. | | `data[].issuer.employer_registration` | string o null | sí | Registro patronal ante el IMSS (RegistroPatronal), 11 caracteres. Necesario para timbrar nómina. | | `data[].issuer.curp` | string o null | sí | CURP, 18 caracteres. | | `data[].issuer.stamps_as` | string | sí | RFC con el que se timbra: el del emisor en producción y el de pruebas del SAT (EKU9003173C9) en pruebas. | | `data[].issuer.sandbox` | string o null | sí | El entorno de prueba (sbx_…) al que pertenece, o null si es de la cuenta. Vea /v1/sandboxes. | | `data[].issuer.test_clock` | objeto o null | sí | Su reloj de prueba (solo emisores de prueba): la hora que es «hoy» para él, o null si va con la hora real. Vea POST /v1/issuers/{id}/test_clock. | | `data[].issuer.test_clock.frozen_time` | string (date-time) | sí | La hora del reloj. | | `data[].issuer.created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].issuer.archived_at` | string (date-time) o null | sí | Cuándo se archivó (ya no timbra), o null. | | `data[].issuer.metadata` | objeto | sí | Sus propios datos: hasta 50 llaves de 40 caracteres con valores de texto de hasta 500. No van al CFDI; vuelven en el objeto y en los webhooks. | | `data[].key` | objeto | | Solo al crearlo. | | `data[].key.id` | string | sí | Id de la llave. | | `data[].key.secret` | string | sí | La llave fk_test_… del entorno. Solo se ve esta vez: guárdela. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].deleted` | boolean | | Siempre true. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatCatalogEntryList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].code` | string | sí | La clave en el catálogo del SAT. | | `data[].description` | string | sí | Su descripción en el catálogo. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatDeclaration Una declaración que el emisor presentó ante el SAT (provisional o definitiva, anual o del servicio anterior Declaraciones y pagos), con sus cifras y sus archivos. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"sat_declaration"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor que la presentó. | | `source` | `monthly`, `annual`, `dyp` | sí | monthly (provisional o definitiva, desde 2022), annual (la anual de persona moral) o dyp (el servicio anterior Declaraciones y pagos, 2021 y antes). | | `year` | integer | sí | El ejercicio. (de -9007199254740991 a 9007199254740991) | | `month` | integer o null | sí | El mes (1 a 12), o null en la anual y otros periodos. | | `period` | string | sí | El periodo como lo nombra el SAT: «Enero», «Del Ejercicio»… | | `type` | `normal`, `complementary` | sí | normal o complementary (complementaria). | | `complementary_type` | string o null | sí | El tipo de complementaria como lo dice el SAT («Modificación de obligaciones»…), o null. | | `operation_number` | string | sí | El número de operación que le dio el SAT. | | `payment_line` | string o null | sí | La línea de captura, si hubo algo a pagar. | | `filed_on` | string o null | sí | Cuándo se presentó (AAAA-MM-DD). | | `profit_coefficient` | number o null | sí | El coeficiente de utilidad que aplicó (ISR de personas morales), o null. | | `isr_due` | number o null | sí | El ISR a cargo, según el acuse. | | `vat_due` | number o null | sí | El IVA a cargo, según el acuse. | | `total_due` | number o null | sí | El total a pagar, según el acuse. | | `sections` | arreglo de objeto | sí | Las cifras de la declaración por impuesto (tax), cada una con su texto como lo imprime el SAT (label) y su valor. Vacío en las de Declaraciones y pagos (su PDF trae dos columnas que no se leen con certeza) o si el PDF aún no llega. | | `sections[].tax` | string | sí | Impuesto: isr, iva o ieps (ImpuestoRet 001, 002, 003). | | `sections[].fields` | arreglo de objeto | sí | Los renglones del impuesto, en el orden del PDF. | | `sections[].fields[].label` | string | sí | El renglón como lo imprime el SAT. | | `sections[].fields[].value` | number | sí | Valor de la mercancía (ValorMercancia); obligatorio en transporte aéreo. | | `receipt` | objeto o null | sí | Lo que dice el acuse: cada concepto con su cantidad a cargo y a pagar, el total, la línea de captura y su vigencia. null si el acuse aún no llega. | | `receipt.concepts` | arreglo de objeto | sí | Cada impuesto del acuse. | | `receipt.concepts[].concept` | string | sí | Concepto del pago (ConceptoPago), c_TipoContribuyenteSujetoRetencion. | | `receipt.concepts[].due` | number o null | sí | Lo que resultó a cargo. | | `receipt.concepts[].amount_due` | number o null | sí | La cantidad a cargo. | | `receipt.concepts[].amount_to_pay` | number o null | sí | La cantidad a pagar. | | `receipt.total` | number o null | sí | Total en la moneda del comprobante (Total). | | `receipt.payment_line` | string o null | sí | La línea de captura del acuse. | | `receipt.valid_until` | string o null | sí | Hasta cuándo sirve la línea de captura (AAAA-MM-DD). | | `summary` | objeto | sí | Las mismas cifras con nombres fijos, por obligación (ISR, IVA, cada retención), para leerlas igual cada mes sin conocer el formato del SAT. Una cifra que no viene en la declaración o no se pudo leer es null, nunca 0. En Declaraciones y pagos solo trae lo del acuse. | | `summary.obligations` | arreglo de objeto | sí | Cada obligación de la declaración: isr, vat, ieps, isr_salaries, isr_assimilated_salaries, isr_professional_services, isr_rent, vat_withholdings u other. | | `summary.obligations[].obligation` | `isr`, `vat`, `ieps`, `isr_salaries`, `isr_assimilated_salaries`, `isr_professional_services`, `isr_rent`, `vat_withholdings`, `other` | sí | Qué obligación es. | | `summary.obligations[].name` | string | sí | Su nombre como lo imprime el SAT. | | `summary.obligations[].read` | boolean | sí | true si sus cifras se leyeron de la declaración; false si solo vienen del acuse. | | `summary.obligations[].amount_due` | number o null | sí | La cantidad a cargo. | | `summary.obligations[].amount_to_pay` | number o null | sí | La cantidad a pagar. | | `summary.obligations[].figures` | objeto | sí | Sus cifras con nombres fijos. ISR: income, invoiced_income, income_ytd, deductions, profit_coefficient, taxable_profit, losses_applied, tax_base, tax_caused, prior_payments, withheld. IVA: taxable_16, taxable_0, exempt, charged, paid_16, creditable, withheld, balance_applied. Salarios: workers, payments, exempt, withheld. Todas: surcharges, inflation, paid_before, due, in_favor, to_pay. null si no viene o no se pudo leer. | | `summary.total_to_pay` | number o null | sí | El total a pagar del acuse; si no hay acuse, la suma de las obligaciones cuando todas se conocen. | | `files` | objeto | sí | Qué archivos ya tiene: pdf (GET …/pdf), receipt (GET …/receipt) y financial_statements (GET …/financial_statements, solo en la anual de persona moral). | | `files.pdf` | boolean | sí | Si ya tiene la declaración en PDF (GET …/pdf). | | `files.receipt` | boolean | sí | Si ya tiene el acuse (GET …/receipt). | | `files.financial_statements` | boolean | sí | Si ya tiene el Excel de estados financieros (GET …/financial_statements), solo en la anual de persona moral. | | `files_error` | string o null | sí | Por qué falta un archivo, como lo dijo el SAT (por ejemplo «No se puede generar el archivo»). Se vuelve a pedir en la siguiente consulta. | | `created_at` | string (date-time) o null | sí | Cuándo se trajo por primera vez. | | `updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez. | Ejemplo: ```json { "id": "dcl_OufpKqnrvx9p1F", "object": "sat_declaration", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "source": "monthly", "year": 2026, "month": 9, "period": "Septiembre", "type": "normal", "complementary_type": null, "operation_number": "92026090000001", "payment_line": "0426TEST2026090001", "filed_on": "2026-10-15", "profit_coefficient": 0.0219, "isr_due": 7718.53, "vat_due": 16520, "total_due": 24238.53, "sections": [ { "tax": "ISR personas morales", "fields": [ { "label": "INGRESOS NOMINALES DEL PERIODO", "value": 292500 }, { "label": "COEFICIENTE DE UTILIDAD", "value": 0.0219 }, { "label": "IMPUESTO A CARGO", "value": 7718.53 } ] } ], "receipt": { "concepts": [ { "concept": "ISR personas morales", "due": 7718.53, "amount_due": 7718.53, "amount_to_pay": 7718.53 }, { "concept": "IVA personas morales", "due": 16520, "amount_due": 16520, "amount_to_pay": 16520 } ], "total": 24238.53, "payment_line": "0426TEST2026090001", "valid_until": null }, "summary": { "obligations": [ { "obligation": "isr", "name": "ISR personas morales", "read": true, "amount_due": 7718.53, "amount_to_pay": 7718.53, "figures": { "income": 292500, "invoiced_income": 292500, "income_ytd": 2632500, "deductions": null, "profit_coefficient": 0.0219, "taxable_profit": 57651.75, "losses_applied": 0, "tax_base": 57651.75, "tax_caused": 17295.53, "prior_payments": 9577, "withheld": 0, "surcharges": null, "inflation": null, "paid_before": null, "due": 7718.53, "in_favor": null, "to_pay": 7718.53 } }, { "obligation": "vat", "name": "IVA personas morales", "read": false, "amount_due": 16520, "amount_to_pay": 16520, "figures": { "taxable_16": null, "taxable_0": null, "exempt": null, "charged": null, "paid_16": null, "creditable": null, "withheld": null, "balance_applied": null, "surcharges": null, "inflation": null, "paid_before": null, "due": null, "in_favor": null, "to_pay": null } } ], "total_to_pay": 24238.53 }, "files": { "pdf": true, "receipt": true, "financial_statements": false }, "files_error": null, "created_at": "2026-10-26T09:12:00-06:00", "updated_at": "2026-10-26T09:12:04-06:00" } ``` --- # SatDeclarationCalendar El ejercicio del emisor mes por mes: qué obligaciones presentó, cuáles parecen faltar y cuáles aún no vencen. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"sat_declaration_calendar"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `year` | integer | sí | El ejercicio. (de -9007199254740991 a 9007199254740991) | | `missing` | integer | sí | Cuántas obligaciones ya vencidas no encontramos presentadas en el SAT. (de -9007199254740991 a 9007199254740991) | | `months` | arreglo de objeto | sí | Los 12 meses y, en persona moral, la anual (month null). Las obligaciones se deducen de lo que el emisor presenta: ISR e IVA cada mes desde el primero que presentó; una retención si la presentó en 2 de los 3 meses anteriores o antes y después. La opinión de cumplimiento del SAT es la que manda. | | `months[].month` | integer o null | sí | El mes (1 a 12), o null para la anual. | | `months[].due_on` | string | sí | El último día para presentarla (AAAA-MM-DD). | | `months[].obligations` | arreglo de objeto | sí | Las obligaciones del mes. | | `months[].obligations[].obligation` | `isr`, `vat`, `ieps`, `isr_salaries`, `isr_assimilated_salaries`, `isr_professional_services`, `isr_rent`, `vat_withholdings`, `other` o `"annual_return"` | sí | Qué obligación es (las de summary.obligations), o annual_return para la anual. | | `months[].obligations[].status` | `filed`, `missing`, `due` | sí | filed (presentada), missing (venció y no la encontramos) o due (aún no vence). | | `months[].obligations[].declarations` | arreglo de string | sí | Las declaraciones (dcl_…) que la presentaron, la normal y sus complementarias. | Ejemplo: ```json { "object": "sat_declaration_calendar", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "year": 2026, "missing": 1, "months": [ { "month": 8, "due_on": "2026-09-18", "obligations": [ { "obligation": "isr", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1E" ] }, { "obligation": "vat", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1E" ] }, { "obligation": "isr_salaries", "status": "missing", "declarations": [] } ] }, { "month": 9, "due_on": "2026-10-19", "obligations": [ { "obligation": "isr", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1F" ] }, { "obligation": "vat", "status": "filed", "declarations": [ "dcl_OufpKqnrvx9p1F" ] } ] }, { "month": null, "due_on": "2027-03-31", "obligations": [ { "obligation": "annual_return", "status": "due", "declarations": [] } ] } ] } ``` --- # SatDeclarationList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"sat_declaration"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor que la presentó. | | `data[].source` | `monthly`, `annual`, `dyp` | sí | monthly (provisional o definitiva, desde 2022), annual (la anual de persona moral) o dyp (el servicio anterior Declaraciones y pagos, 2021 y antes). | | `data[].year` | integer | sí | El ejercicio. (de -9007199254740991 a 9007199254740991) | | `data[].month` | integer o null | sí | El mes (1 a 12), o null en la anual y otros periodos. | | `data[].period` | string | sí | El periodo como lo nombra el SAT: «Enero», «Del Ejercicio»… | | `data[].type` | `normal`, `complementary` | sí | normal o complementary (complementaria). | | `data[].complementary_type` | string o null | sí | El tipo de complementaria como lo dice el SAT («Modificación de obligaciones»…), o null. | | `data[].operation_number` | string | sí | El número de operación que le dio el SAT. | | `data[].payment_line` | string o null | sí | La línea de captura, si hubo algo a pagar. | | `data[].filed_on` | string o null | sí | Cuándo se presentó (AAAA-MM-DD). | | `data[].profit_coefficient` | number o null | sí | El coeficiente de utilidad que aplicó (ISR de personas morales), o null. | | `data[].isr_due` | number o null | sí | El ISR a cargo, según el acuse. | | `data[].vat_due` | number o null | sí | El IVA a cargo, según el acuse. | | `data[].total_due` | number o null | sí | El total a pagar, según el acuse. | | `data[].sections` | arreglo de objeto | sí | Las cifras de la declaración por impuesto (tax), cada una con su texto como lo imprime el SAT (label) y su valor. Vacío en las de Declaraciones y pagos (su PDF trae dos columnas que no se leen con certeza) o si el PDF aún no llega. | | `data[].sections[].tax` | string | sí | Impuesto: isr, iva o ieps (ImpuestoRet 001, 002, 003). | | `data[].sections[].fields` | arreglo de objeto | sí | Los renglones del impuesto, en el orden del PDF. | | `data[].sections[].fields[].label` | string | sí | El renglón como lo imprime el SAT. | | `data[].sections[].fields[].value` | number | sí | Valor de la mercancía (ValorMercancia); obligatorio en transporte aéreo. | | `data[].receipt` | objeto o null | sí | Lo que dice el acuse: cada concepto con su cantidad a cargo y a pagar, el total, la línea de captura y su vigencia. null si el acuse aún no llega. | | `data[].receipt.concepts` | arreglo de objeto | sí | Cada impuesto del acuse. | | `data[].receipt.concepts[].concept` | string | sí | Concepto del pago (ConceptoPago), c_TipoContribuyenteSujetoRetencion. | | `data[].receipt.concepts[].due` | number o null | sí | Lo que resultó a cargo. | | `data[].receipt.concepts[].amount_due` | number o null | sí | La cantidad a cargo. | | `data[].receipt.concepts[].amount_to_pay` | number o null | sí | La cantidad a pagar. | | `data[].receipt.total` | number o null | sí | Total en la moneda del comprobante (Total). | | `data[].receipt.payment_line` | string o null | sí | La línea de captura del acuse. | | `data[].receipt.valid_until` | string o null | sí | Hasta cuándo sirve la línea de captura (AAAA-MM-DD). | | `data[].summary` | objeto | sí | Las mismas cifras con nombres fijos, por obligación (ISR, IVA, cada retención), para leerlas igual cada mes sin conocer el formato del SAT. Una cifra que no viene en la declaración o no se pudo leer es null, nunca 0. En Declaraciones y pagos solo trae lo del acuse. | | `data[].summary.obligations` | arreglo de objeto | sí | Cada obligación de la declaración: isr, vat, ieps, isr_salaries, isr_assimilated_salaries, isr_professional_services, isr_rent, vat_withholdings u other. | | `data[].summary.obligations[].obligation` | `isr`, `vat`, `ieps`, `isr_salaries`, `isr_assimilated_salaries`, `isr_professional_services`, `isr_rent`, `vat_withholdings`, `other` | sí | Qué obligación es. | | `data[].summary.obligations[].name` | string | sí | Su nombre como lo imprime el SAT. | | `data[].summary.obligations[].read` | boolean | sí | true si sus cifras se leyeron de la declaración; false si solo vienen del acuse. | | `data[].summary.obligations[].amount_due` | number o null | sí | La cantidad a cargo. | | `data[].summary.obligations[].amount_to_pay` | number o null | sí | La cantidad a pagar. | | `data[].summary.obligations[].figures` | objeto | sí | Sus cifras con nombres fijos. ISR: income, invoiced_income, income_ytd, deductions, profit_coefficient, taxable_profit, losses_applied, tax_base, tax_caused, prior_payments, withheld. IVA: taxable_16, taxable_0, exempt, charged, paid_16, creditable, withheld, balance_applied. Salarios: workers, payments, exempt, withheld. Todas: surcharges, inflation, paid_before, due, in_favor, to_pay. null si no viene o no se pudo leer. | | `data[].summary.total_to_pay` | number o null | sí | El total a pagar del acuse; si no hay acuse, la suma de las obligaciones cuando todas se conocen. | | `data[].files` | objeto | sí | Qué archivos ya tiene: pdf (GET …/pdf), receipt (GET …/receipt) y financial_statements (GET …/financial_statements, solo en la anual de persona moral). | | `data[].files.pdf` | boolean | sí | Si ya tiene la declaración en PDF (GET …/pdf). | | `data[].files.receipt` | boolean | sí | Si ya tiene el acuse (GET …/receipt). | | `data[].files.financial_statements` | boolean | sí | Si ya tiene el Excel de estados financieros (GET …/financial_statements), solo en la anual de persona moral. | | `data[].files_error` | string o null | sí | Por qué falta un archivo, como lo dijo el SAT (por ejemplo «No se puede generar el archivo»). Se vuelve a pedir en la siguiente consulta. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se trajo por primera vez. | | `data[].updated_at` | string (date-time) o null | sí | Cuándo cambió por última vez. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatDeclarationsRefresh Lo que trajo una consulta de las declaraciones del emisor en el SAT. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"sat_declarations_refresh"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor consultado. | | `status` | `completed`, `processing` | sí | completed (ya bajó todo) o processing (queda algo por bajar: sigue solo en la siguiente hora, sin otro timbre). | | `found` | integer | sí | Cuántas declaraciones listó el SAT en los años consultados. (de -9007199254740991 a 9007199254740991) | | `downloaded` | integer | sí | Cuántos archivos bajó en esta consulta. (de -9007199254740991 a 9007199254740991) | | `pending` | integer | sí | Cuántas declaraciones quedan con archivos por bajar. (de -9007199254740991 a 9007199254740991) | | `warning` | string o null | sí | Si una parte del SAT falló (por ejemplo, la anual), lo que dijo; lo demás sí se trajo. null si todo salió. | | `refreshed_at` | string (date-time) | sí | Cuándo se consultó. | | `next_refresh_at` | string (date-time) | sí | Desde cuándo se puede volver a consultar este emisor (10 minutos después). | Ejemplo: ```json { "object": "sat_declarations_refresh", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "status": "completed", "found": 10, "downloaded": 10, "pending": 0, "warning": null, "refreshed_at": "2026-10-26T09:12:00-06:00", "next_refresh_at": "2026-10-26T09:22:00-06:00" } ``` --- # SatDownload Una solicitud al SAT de las facturas emitidas o recibidas de un emisor en un periodo. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"sat_download"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor cuyas facturas se piden. | | `direction` | `received`, `issued` | sí | received (las que le hicieron) o issued (las que emitió). | | `from` | string o null | sí | Desde qué día (incluido). | | `to` | string o null | sí | Hasta qué día (incluido). | | `type` | `I`, `E`, `P`, `T`, `N` o null | sí | El tipo de comprobante pedido, o null si todos. | | `counterparty_tax_id` | string o null | sí | El RFC de la contraparte pedido, o null. | | `uuid` | string o null | sí | El folio fiscal pedido, o null. | | `status` | `processing`, `completed`, `failed` | sí | processing (el SAT la está preparando: de minutos a horas), completed (las facturas ya están en GET /v1/sat_invoices) o failed (el motivo en error). | | `new_invoices` | integer o null | sí | Cuántas facturas nuevas trajo (las que ya tenía no cuentan). null mientras no termine. | | `error` | string o null | sí | Por qué falló, como lo dijo el SAT. null si no falló. | | `created_at` | string (date-time) o null | sí | Cuándo se pidió. | | `completed_at` | string (date-time) o null | sí | Cuándo terminó, o null. | Ejemplo: ```json { "id": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "object": "sat_download", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "direction": "received", "from": "2026-09-01", "to": "2026-09-30", "type": null, "counterparty_tax_id": null, "uuid": null, "status": "completed", "new_invoices": 8, "error": null, "created_at": "2026-10-24T10:20:00-06:00", "completed_at": "2026-10-24T10:27:41-06:00" } ``` --- # SatDownloadList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"sat_download"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor cuyas facturas se piden. | | `data[].direction` | `received`, `issued` | sí | received (las que le hicieron) o issued (las que emitió). | | `data[].from` | string o null | sí | Desde qué día (incluido). | | `data[].to` | string o null | sí | Hasta qué día (incluido). | | `data[].type` | `I`, `E`, `P`, `T`, `N` o null | sí | El tipo de comprobante pedido, o null si todos. | | `data[].counterparty_tax_id` | string o null | sí | El RFC de la contraparte pedido, o null. | | `data[].uuid` | string o null | sí | El folio fiscal pedido, o null. | | `data[].status` | `processing`, `completed`, `failed` | sí | processing (el SAT la está preparando: de minutos a horas), completed (las facturas ya están en GET /v1/sat_invoices) o failed (el motivo en error). | | `data[].new_invoices` | integer o null | sí | Cuántas facturas nuevas trajo (las que ya tenía no cuentan). null mientras no termine. | | `data[].error` | string o null | sí | Por qué falló, como lo dijo el SAT. null si no falló. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se pidió. | | `data[].completed_at` | string (date-time) o null | sí | Cuándo terminó, o null. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatFindingList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | El código del hallazgo (el mismo que code). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"sat_finding"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].code` | string | sí | El código del hallazgo, estable (por ejemplo efos_definitivo). | | `data[].severity` | `critical`, `warning`, `info` | sí | critical (grave), warning (revisar) o info. | | `data[].title` | string | sí | Qué es, en palabras. | | `data[].explanation` | string o null | sí | Por qué importa y qué hacer, o null. | | `data[].invoices` | integer | sí | Cuántos CFDI lo tienen, sin revisar. (de -9007199254740991 a 9007199254740991) | | `data[].amount` | number | sí | La suma de sus totales. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatInvoice Una factura que el SAT tiene del emisor, emitida o recibida, traída con una descarga. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"sat_invoice"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor de su cuenta (iss_…) a quien pertenece esta factura. | | `uuid` | string | sí | El folio fiscal (UUID del timbre). | | `direction` | `received`, `issued` | sí | received (se la hicieron al emisor) o issued (la emitió). | | `type` | `I`, `E`, `P`, `T`, `N` o null | sí | Tipo de comprobante (TipoDeComprobante): I ingreso, E egreso, P pago, T traslado, N nómina. | | `date` | string | sí | La fecha del comprobante (Fecha), en la hora local con que se emitió. | | `series` | string o null | sí | Serie (Serie), o null. | | `folio` | string o null | sí | Folio (Folio), o null. | | `issued_by` | objeto | sí | Quien la emitió (Emisor). | | `issued_by.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `issued_by.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `issued_to` | objeto | sí | A quien se la emitieron (Receptor). | | `issued_to.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `issued_to.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `currency` | string o null | sí | Moneda (Moneda): MXN, USD… | | `exchange_rate` | number o null | sí | Tipo de cambio (TipoCambio), o null en pesos. | | `payment_method` | string o null | sí | Método de pago (MetodoPago): PUE o PPD. | | `payment_form` | string o null | sí | Forma de pago (FormaPago), clave de c_FormaPago. | | `use` | string o null | sí | Uso del CFDI (UsoCFDI). | | `subtotal` | number o null | sí | Subtotal (SubTotal). | | `total` | number o null | sí | Total (Total). | | `taxes` | objeto | sí | Impuestos del comprobante: trasladados (iva, ieps), retenidos (iva_withheld, isr_withheld, ieps_withheld) y locales. | | `taxes.iva` | number | sí | IVA trasladado. | | `taxes.ieps` | number | sí | IEPS trasladado. | | `taxes.iva_withheld` | number | sí | IVA retenido. | | `taxes.isr_withheld` | number | sí | ISR retenido. | | `taxes.ieps_withheld` | number | sí | IEPS retenido. | | `taxes.local_transferred` | number | sí | Impuestos locales trasladados (ISH y similares). | | `taxes.local_withheld` | number | sí | Impuestos locales retenidos. | | `sat_status` | `active`, `canceled` o null | sí | Su estado en el SAT cuando se revisó: active (vigente) o canceled. null si aún no se revisa. | | `sat_download` | string o null | sí | La descarga (sdl_…) que la trajo, o null. | | `downloaded_at` | string (date-time) o null | sí | Cuándo llegó. | Ejemplo: ```json { "id": "sat_OufpKqnrvx9p1F", "object": "sat_invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "uuid": "9B2D4E61-7C3A-4F18-8E5D-0A6B9C1D2E3F", "direction": "received", "type": "I", "date": "2026-09-12T11:04:00", "series": "A", "folio": "4821", "issued_by": { "tax_id": "XIA190128J61", "legal_name": "XENON INDUSTRIAL ARTICLES" }, "issued_to": { "tax_id": "TOL190312KT4", "legal_name": "TOLPI" }, "currency": "MXN", "exchange_rate": null, "payment_method": "PUE", "payment_form": "03", "use": "G03", "subtotal": 18400, "total": 21344, "taxes": { "iva": 2944, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "ieps_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "sat_status": "active", "sat_download": "sdl_OufpKqnrvx9p3n8vQd2LkW7mZr1XyT5uBc", "downloaded_at": "2026-10-24T10:27:40-06:00" } ``` --- # SatInvoiceList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"sat_invoice"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor de su cuenta (iss_…) a quien pertenece esta factura. | | `data[].uuid` | string | sí | El folio fiscal (UUID del timbre). | | `data[].direction` | `received`, `issued` | sí | received (se la hicieron al emisor) o issued (la emitió). | | `data[].type` | `I`, `E`, `P`, `T`, `N` o null | sí | Tipo de comprobante (TipoDeComprobante): I ingreso, E egreso, P pago, T traslado, N nómina. | | `data[].date` | string | sí | La fecha del comprobante (Fecha), en la hora local con que se emitió. | | `data[].series` | string o null | sí | Serie (Serie), o null. | | `data[].folio` | string o null | sí | Folio (Folio), o null. | | `data[].issued_by` | objeto | sí | Quien la emitió (Emisor). | | `data[].issued_by.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].issued_by.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].issued_to` | objeto | sí | A quien se la emitieron (Receptor). | | `data[].issued_to.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].issued_to.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].currency` | string o null | sí | Moneda (Moneda): MXN, USD… | | `data[].exchange_rate` | number o null | sí | Tipo de cambio (TipoCambio), o null en pesos. | | `data[].payment_method` | string o null | sí | Método de pago (MetodoPago): PUE o PPD. | | `data[].payment_form` | string o null | sí | Forma de pago (FormaPago), clave de c_FormaPago. | | `data[].use` | string o null | sí | Uso del CFDI (UsoCFDI). | | `data[].subtotal` | number o null | sí | Subtotal (SubTotal). | | `data[].total` | number o null | sí | Total (Total). | | `data[].taxes` | objeto | sí | Impuestos del comprobante: trasladados (iva, ieps), retenidos (iva_withheld, isr_withheld, ieps_withheld) y locales. | | `data[].taxes.iva` | number | sí | IVA trasladado. | | `data[].taxes.ieps` | number | sí | IEPS trasladado. | | `data[].taxes.iva_withheld` | number | sí | IVA retenido. | | `data[].taxes.isr_withheld` | number | sí | ISR retenido. | | `data[].taxes.ieps_withheld` | number | sí | IEPS retenido. | | `data[].taxes.local_transferred` | number | sí | Impuestos locales trasladados (ISH y similares). | | `data[].taxes.local_withheld` | number | sí | Impuestos locales retenidos. | | `data[].sat_status` | `active`, `canceled` o null | sí | Su estado en el SAT cuando se revisó: active (vigente) o canceled. null si aún no se revisa. | | `data[].sat_download` | string o null | sí | La descarga (sdl_…) que la trajo, o null. | | `data[].downloaded_at` | string (date-time) o null | sí | Cuándo llegó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatListCheck Si un RFC aparece en las listas del SAT (69-B, 69-B Bis y art. 69). | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"sat_list_check"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `tax_id` | string | sí | El RFC revisado. | | `name` | string o null | sí | El nombre con que aparece en las listas del SAT (null si no aparece). | | `listed` | boolean | sí | true si el RFC aparece en alguna lista, aunque haya desvirtuado. | | `risk` | `high`, `medium`, `none` | sí | high: 69-B o 69-B Bis como presunto o definitivo (el SAT dice que simula operaciones; sus facturas no se pueden deducir). medium: una lista del art. 69 (no localizado, créditos firmes o exigibles, cancelados, sentencias, CSD sin efectos). none: no aparece, o ya desvirtuó o tiene sentencia favorable. | | `lists` | arreglo de objeto | sí | Cada lista en que aparece; vacía si no aparece en ninguna. | | `lists[].list` | `69`, `69b`, `69b_bis` | sí | 69b: art. 69-B (operaciones simuladas). 69b_bis: art. 69-B Bis (pérdidas fiscales). 69: art. 69 (no localizados, adeudos, cancelados). | | `lists[].source` | string | sí | El archivo del SAT de donde viene (clave). | | `lists[].source_name` | string | sí | El nombre del archivo del SAT, para mostrarlo. | | `lists[].situation` | string o null | sí | Su situación según el SAT: Presunto, Definitivo, Desvirtuado, Sentencia favorable; en el art. 69, el supuesto (null si el archivo no la trae). | | `lists[].published_on` | string o null | sí | Fecha de publicación en el SAT o en el DOF (null si no la trae). | | `lists[].details` | objeto o null | sí | Lo demás que publica el SAT para ese RFC (oficios, fechas, montos), tal cual; null si nada. | | `lists_updated_at` | string (date-time) o null | sí | Cuándo se actualizaron las listas por última vez (cada día a las 00:00 de la Ciudad de México). | Ejemplo: ```json { "object": "sat_list_check", "tax_id": "AAA080808HL8", "name": "EMPRESA SIMULADORA SA DE CV", "listed": true, "risk": "high", "lists": [ { "list": "69b", "source": "69b", "source_name": "69-B (operaciones simuladas)", "situation": "Definitivo", "published_on": null, "details": { "Publicación página SAT definitivos": "15/03/2024", "Número y fecha de oficio global de definitivos": "500-05-2024-1234 de fecha 01/03/2024" } } ], "lists_updated_at": "2026-10-24T00:12:40-06:00" } ``` --- # SatListCheckList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].object` | `"sat_list_check"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].tax_id` | string | sí | El RFC revisado. | | `data[].name` | string o null | sí | El nombre con que aparece en las listas del SAT (null si no aparece). | | `data[].listed` | boolean | sí | true si el RFC aparece en alguna lista, aunque haya desvirtuado. | | `data[].risk` | `high`, `medium`, `none` | sí | high: 69-B o 69-B Bis como presunto o definitivo (el SAT dice que simula operaciones; sus facturas no se pueden deducir). medium: una lista del art. 69 (no localizado, créditos firmes o exigibles, cancelados, sentencias, CSD sin efectos). none: no aparece, o ya desvirtuó o tiene sentencia favorable. | | `data[].lists` | arreglo de objeto | sí | Cada lista en que aparece; vacía si no aparece en ninguna. | | `data[].lists[].list` | `69`, `69b`, `69b_bis` | sí | 69b: art. 69-B (operaciones simuladas). 69b_bis: art. 69-B Bis (pérdidas fiscales). 69: art. 69 (no localizados, adeudos, cancelados). | | `data[].lists[].source` | string | sí | El archivo del SAT de donde viene (clave). | | `data[].lists[].source_name` | string | sí | El nombre del archivo del SAT, para mostrarlo. | | `data[].lists[].situation` | string o null | sí | Su situación según el SAT: Presunto, Definitivo, Desvirtuado, Sentencia favorable; en el art. 69, el supuesto (null si el archivo no la trae). | | `data[].lists[].published_on` | string o null | sí | Fecha de publicación en el SAT o en el DOF (null si no la trae). | | `data[].lists[].details` | objeto o null | sí | Lo demás que publica el SAT para ese RFC (oficios, fechas, montos), tal cual; null si nada. | | `data[].lists_updated_at` | string (date-time) o null | sí | Cuándo se actualizaron las listas por última vez (cada día a las 00:00 de la Ciudad de México). | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatMailboxMessage Un aviso del buzón tributario del SAT del emisor. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"sat_mailbox_message"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor. | | `subject` | string | sí | Su asunto. | | `summary` | string o null | sí | Su texto, resumido, o null. | | `source` | `mailbox`, `email` | sí | mailbox (el buzón del portal) o email (un aviso que el SAT mandó por correo). | | `unread` | boolean | sí | true si no se ha leído en el SAT. | | `urgency` | `urgent`, `attention`, `informational` | sí | urgent (un requerimiento, un plazo), attention o informational. | | `kind` | string | sí | Qué es: requerimiento, carta invitación, informativo… | | `action_required` | boolean | sí | true si pide hacer algo. | | `received_at` | string o null | sí | Cuándo llegó, como lo dice el SAT. | | `created_at` | string (date-time) o null | sí | Cuándo lo trajo Fiscalisto. | Ejemplo: ```json { "id": "sbm_OufpKqnrvx9p1", "object": "sat_mailbox_message", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "subject": "Requerimiento de información", "summary": "Se le requiere la documentación que acredite…", "source": "mailbox", "unread": true, "urgency": "urgent", "kind": "Requerimiento", "action_required": true, "received_at": "2026-10-27", "created_at": "2026-10-27T09:12:00-06:00" } ``` --- # SatMailboxMessageList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"sat_mailbox_message"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].subject` | string | sí | Su asunto. | | `data[].summary` | string o null | sí | Su texto, resumido, o null. | | `data[].source` | `mailbox`, `email` | sí | mailbox (el buzón del portal) o email (un aviso que el SAT mandó por correo). | | `data[].unread` | boolean | sí | true si no se ha leído en el SAT. | | `data[].urgency` | `urgent`, `attention`, `informational` | sí | urgent (un requerimiento, un plazo), attention o informational. | | `data[].kind` | string | sí | Qué es: requerimiento, carta invitación, informativo… | | `data[].action_required` | boolean | sí | true si pide hacer algo. | | `data[].received_at` | string o null | sí | Cuándo llegó, como lo dice el SAT. | | `data[].created_at` | string (date-time) o null | sí | Cuándo lo trajo Fiscalisto. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # SatRefresh Una consulta al SAT de un documento del emisor: si ya terminó y dónde leerlo. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"sat_refresh"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `document` | `compliance_opinion`, `tax_status_certificate`, `sat_mailbox` | sí | Qué se consultó. | | `status` | `completed`, `processing` | sí | completed (ya está en url) o processing (el SAT tarda; llega a url en unos minutos). | | `refreshed_at` | string (date-time) o null | sí | Cuándo se pidió. | | `next_refresh_at` | string (date-time) o null | sí | Desde cuándo se puede pedir otra vez. | | `url` | string | sí | Dónde leer lo que trajo. | Ejemplo: ```json { "object": "sat_refresh", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "document": "compliance_opinion", "status": "completed", "refreshed_at": "2026-10-27T09:12:00-06:00", "next_refresh_at": "2026-10-27T09:22:00-06:00", "url": "/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion" } ``` --- # SelfInvoicing El portal de autofactura de un emisor y sus reglas. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"self_invoicing"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `portal_url` | string | sí | El portal permanente del emisor: ahí cualquier comprador factura con su código y su total. | | `window` | `month`, `days` | sí | month: se factura hasta el fin del mes de la venta. days: hasta window_days días después. | | `window_days` | integer | sí | Días para facturar con window: days. (de -9007199254740991 a 9007199254740991) | | `global_periodicity` | `01`, `02`, `03`, `04` | sí | Cada cuánto se timbra sola la factura global con lo que nadie facturó: 01 diaria, 02 semanal, 03 quincenal, 04 mensual. | Ejemplo: ```json { "object": "self_invoicing", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "portal_url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c", "window": "month", "window_days": 7, "global_periodicity": "04" } ``` --- # StockAdjustment Un ajuste de existencias: lo que entró o salió de un almacén por inventario inicial, merma, robo, conteo… | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"stock_adjustment"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor. | | `warehouse` | string | sí | El almacén (whs_…). | | `reason` | `shrinkage`, `theft`, `damage`, `expiration`, `internal_use`, `gift`, `initial`, `surplus`, `count`, `other` | sí | El motivo (ver POST /v1/stock_adjustments). | | `note` | string o null | sí | Su nota, o null. | | `status` | `applied`, `pending`, `rejected`, `reversed` | sí | applied (ya movió existencias), pending (espera aprobación en la app), rejected o reversed (se deshizo). | | `value` | number | sí | Lo que vale lo que movió, en pesos sin IVA. | | `lines` | arreglo de objeto | sí | Lo que entró o salió de cada producto. | | `lines[].product` | string | sí | Un producto de su catálogo (prd_…): toma su clave, unidad, precio e impuestos. | | `lines[].quantity` | number | sí | Cantidad (Cantidad). Por omisión 1. | | `lines[].unit_cost` | number o null | sí | El costo por unidad que se dio al ajuste, en pesos sin IVA, o null (el costo promedio). | | `lines[].lot` | string o null | sí | Lote del medicamento (LoteMedicamento). | | `created_at` | string (date-time) o null | sí | Cuándo se hizo. | Ejemplo: ```json { "id": "adj_OufpKqnrvx9p1", "object": "stock_adjustment", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "warehouse": "whs_OufpKqnrvx9p1", "reason": "initial", "note": "Inventario de apertura", "status": "applied", "value": 4500, "lines": [ { "product": "prd_OufpKqnrvx9p2", "quantity": 50, "unit_cost": 90, "lot": null } ], "created_at": "2026-10-27T09:30:00-06:00" } ``` --- # StockAdjustmentList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"stock_adjustment"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].warehouse` | string | sí | El almacén (whs_…). | | `data[].reason` | `shrinkage`, `theft`, `damage`, `expiration`, `internal_use`, `gift`, `initial`, `surplus`, `count`, `other` | sí | El motivo (ver POST /v1/stock_adjustments). | | `data[].note` | string o null | sí | Su nota, o null. | | `data[].status` | `applied`, `pending`, `rejected`, `reversed` | sí | applied (ya movió existencias), pending (espera aprobación en la app), rejected o reversed (se deshizo). | | `data[].value` | number | sí | Lo que vale lo que movió, en pesos sin IVA. | | `data[].lines` | arreglo de objeto | sí | Lo que entró o salió de cada producto. | | `data[].lines[].product` | string | sí | Un producto de su catálogo (prd_…): toma su clave, unidad, precio e impuestos. | | `data[].lines[].quantity` | number | sí | Cantidad (Cantidad). Por omisión 1. | | `data[].lines[].unit_cost` | number o null | sí | El costo por unidad que se dio al ajuste, en pesos sin IVA, o null (el costo promedio). | | `data[].lines[].lot` | string o null | sí | Lote del medicamento (LoteMedicamento). | | `data[].created_at` | string (date-time) o null | sí | Cuándo se hizo. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # StockLevelList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Su id (stl_…): el producto en ese almacén. Sirve como starting_after. | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"stock_level"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].product` | string | sí | El producto (prd_…). | | `data[].warehouse` | string | sí | El almacén (whs_…). | | `data[].on_hand` | number | sí | Lo que hay, en la unidad del producto (todos sus lotes). | | `data[].reserved` | number | sí | Lo apartado por cotizaciones aceptadas que no se han facturado. | | `data[].available` | number | sí | on_hand menos reserved: lo que se puede vender. | | `data[].value` | number | sí | Lo que valen esas existencias a su costo, en pesos sin IVA. | | `data[].average_cost` | number o null | sí | value / on_hand: el costo por unidad, o null sin existencias. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # StockMovement Una entrada o salida de existencias (el kárdex): una venta, una compra, un ajuste, un traspaso… | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"stock_movement"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor. | | `product` | string | sí | El producto (prd_…). | | `warehouse` | string | sí | El almacén (whs_…). | | `type` | `sale`, `purchase`, `customer_return`, `supplier_return`, `adjustment`, `count`, `transfer_out`, `transfer_in`, `initial`, `cost`, `reversal` | sí | sale (una factura), purchase (una compra), customer_return, supplier_return, adjustment, count (conteo físico), transfer_out, transfer_in, initial, cost (un ajuste de costo) o reversal (deshace otro, como una factura cancelada). | | `quantity` | number | sí | Cuánto entró (positivo) o salió (negativo). | | `unit_cost` | number | sí | El costo por unidad con que entró o salió, en pesos sin IVA. | | `value` | number | sí | quantity × unit_cost: lo que cambió el valor del inventario. | | `lot` | string o null | sí | El lote o número de serie, o null. | | `source` | objeto | sí | Lo que lo causó: type invoice (id es el UUID del CFDI), adjustment (adj_…), receipt, transfer, count o ticket. | | `source.type` | string | sí | Tipo de comprobante (TipoDeComprobante): I ingreso, E egreso, P pago, T traslado, N nómina. | | `source.id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `reason` | string o null | sí | El motivo de un ajuste, o null. | | `date` | string o null | sí | Cuándo pasó, hora de la Ciudad de México (la Fecha del CFDI en una venta). | | `reversed` | boolean | sí | true si un movimiento posterior lo deshizo. | | `created_at` | string (date-time) o null | sí | Cuándo se registró. | Ejemplo: ```json { "id": "stm_OufpKqnrvx9p7", "object": "stock_movement", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "product": "prd_OufpKqnrvx9p2", "warehouse": "whs_OufpKqnrvx9p1", "type": "sale", "quantity": -2, "unit_cost": 90, "value": -180, "lot": null, "source": { "type": "invoice", "id": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718" }, "reason": null, "date": "2026-10-27T10:15:42", "reversed": false, "created_at": "2026-10-27T10:15:43-06:00" } ``` --- # StockMovementList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"stock_movement"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor. | | `data[].product` | string | sí | El producto (prd_…). | | `data[].warehouse` | string | sí | El almacén (whs_…). | | `data[].type` | `sale`, `purchase`, `customer_return`, `supplier_return`, `adjustment`, `count`, `transfer_out`, `transfer_in`, `initial`, `cost`, `reversal` | sí | sale (una factura), purchase (una compra), customer_return, supplier_return, adjustment, count (conteo físico), transfer_out, transfer_in, initial, cost (un ajuste de costo) o reversal (deshace otro, como una factura cancelada). | | `data[].quantity` | number | sí | Cuánto entró (positivo) o salió (negativo). | | `data[].unit_cost` | number | sí | El costo por unidad con que entró o salió, en pesos sin IVA. | | `data[].value` | number | sí | quantity × unit_cost: lo que cambió el valor del inventario. | | `data[].lot` | string o null | sí | El lote o número de serie, o null. | | `data[].source` | objeto | sí | Lo que lo causó: type invoice (id es el UUID del CFDI), adjustment (adj_…), receipt, transfer, count o ticket. | | `data[].source.type` | string | sí | Tipo de comprobante (TipoDeComprobante): I ingreso, E egreso, P pago, T traslado, N nómina. | | `data[].source.id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].reason` | string o null | sí | El motivo de un ajuste, o null. | | `data[].date` | string o null | sí | Cuándo pasó, hora de la Ciudad de México (la Fecha del CFDI en una venta). | | `data[].reversed` | boolean | sí | true si un movimiento posterior lo deshizo. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se registró. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # StoreConnection Una tienda de Shopify o Mercado Libre cuyos pedidos pagados se vuelven tickets de autofactura del emisor. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"store_connection"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor dueño de la tienda. | | `platform` | `shopify`, `mercadolibre` | sí | shopify o mercadolibre. | | `shop` | string o null | sí | El dominio de la tienda de Shopify, o el id del vendedor en Mercado Libre (al autorizar). | | `status` | `pending`, `active`, `disconnected` | sí | pending (Mercado Libre espera que el vendedor autorice en authorize_url), active o disconnected. | | `authorize_url` | string o null | sí | Si está pending: la página de Mercado Libre donde el vendedor autoriza (vale un día; cada consulta da una nueva). Si no, null. | | `settings` | objeto | sí | Cómo se vuelve ticket cada pedido. | | `settings.product_key` | string o null | sí | c_ClaveProdServ de lo que vende, o null. | | `settings.unit_key` | string o null | sí | c_ClaveUnidad, o null (la de la clave del producto). | | `settings.iva` | `0`, `0.16`, `0.08`, `exento` | sí | Tasa de IVA incluida en los precios. | | `settings.description` | string o null | sí | El concepto fijo de cada ticket, o null (el pedido y sus artículos). | | `settings.folio_prefix` | string | sí | Lo que va antes del número del pedido en el folio. | | `settings.payment_form` | string o null | sí | c_FormaPago cuando la del pedido no está en payment_forms, o null. | | `settings.payment_forms` | objeto | sí | c_FormaPago por pasarela o tipo de pago. | | `settings.auto_invoice` | boolean o null | sí | Mercado Libre: si se timbra al momento con los datos fiscales del comprador; null en Shopify. | | `orders_count` | integer | sí | Cuántos pedidos se han vuelto tickets. (de -9007199254740991 a 9007199254740991) | | `last_order_at` | string (date-time) o null | sí | Cuándo llegó el último, o null. | | `last_error` | objeto o null | sí | El último problema (un pedido en dólares, un ticket que ya se facturó cuando se canceló el pedido…), o null. | | `last_error.message` | string | sí | Qué pasó. | | `last_error.at` | string (date-time) o null | sí | Cuándo. | | `created_at` | string (date-time) o null | sí | Cuándo se conectó. | Ejemplo: ```json { "id": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "object": "store_connection", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "platform": "shopify", "shop": "mi-tienda.myshopify.com", "status": "active", "authorize_url": null, "settings": { "product_key": "53101600", "unit_key": null, "iva": "0.16", "description": null, "folio_prefix": "SHOP-", "payment_form": null, "payment_forms": { "paypal": "31" }, "auto_invoice": null }, "orders_count": 128, "last_order_at": "2026-10-25T10:12:44-06:00", "last_error": null, "created_at": "2026-10-01T09:00:00-06:00" } ``` --- # StoreConnectionList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"store_connection"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor dueño de la tienda. | | `data[].platform` | `shopify`, `mercadolibre` | sí | shopify o mercadolibre. | | `data[].shop` | string o null | sí | El dominio de la tienda de Shopify, o el id del vendedor en Mercado Libre (al autorizar). | | `data[].status` | `pending`, `active`, `disconnected` | sí | pending (Mercado Libre espera que el vendedor autorice en authorize_url), active o disconnected. | | `data[].authorize_url` | string o null | sí | Si está pending: la página de Mercado Libre donde el vendedor autoriza (vale un día; cada consulta da una nueva). Si no, null. | | `data[].settings` | objeto | sí | Cómo se vuelve ticket cada pedido. | | `data[].settings.product_key` | string o null | sí | c_ClaveProdServ de lo que vende, o null. | | `data[].settings.unit_key` | string o null | sí | c_ClaveUnidad, o null (la de la clave del producto). | | `data[].settings.iva` | `0`, `0.16`, `0.08`, `exento` | sí | Tasa de IVA incluida en los precios. | | `data[].settings.description` | string o null | sí | El concepto fijo de cada ticket, o null (el pedido y sus artículos). | | `data[].settings.folio_prefix` | string | sí | Lo que va antes del número del pedido en el folio. | | `data[].settings.payment_form` | string o null | sí | c_FormaPago cuando la del pedido no está en payment_forms, o null. | | `data[].settings.payment_forms` | objeto | sí | c_FormaPago por pasarela o tipo de pago. | | `data[].settings.auto_invoice` | boolean o null | sí | Mercado Libre: si se timbra al momento con los datos fiscales del comprador; null en Shopify. | | `data[].orders_count` | integer | sí | Cuántos pedidos se han vuelto tickets. (de -9007199254740991 a 9007199254740991) | | `data[].last_order_at` | string (date-time) o null | sí | Cuándo llegó el último, o null. | | `data[].last_error` | objeto o null | sí | El último problema (un pedido en dólares, un ticket que ya se facturó cuando se canceló el pedido…), o null. | | `data[].last_error.message` | string | sí | Qué pasó. | | `data[].last_error.at` | string (date-time) o null | sí | Cuándo. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se conectó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # StoreImport Los pedidos pagados que se trajeron. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"store_import"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `store_connection` | string | sí | La conexión (stc_…). | | `since` | string | sí | Desde qué día se buscaron pedidos. | | `tickets` | integer | sí | Pedidos que son tickets (los que ya lo eran cuentan otra vez). (de -9007199254740991 a 9007199254740991) | | `invoiced` | integer | sí | De ellos, los que se facturaron al momento con los datos del comprador. (de -9007199254740991 a 9007199254740991) | | `skipped` | arreglo de objeto | sí | Los que no se volvieron ticket, con el motivo. | | `skipped[].order` | string | sí | El pedido. | | `skipped[].reason` | string | sí | Por qué no se volvió ticket. | Ejemplo: ```json { "object": "store_import", "livemode": false, "store_connection": "stc_OufpKqnrvx9p3Kd8sLm2XyT5uBcW8sKdLx", "since": "2026-10-18", "tickets": 14, "invoiced": 0, "skipped": [ { "order": "1042", "reason": "está en USD; solo se registran pedidos en pesos." } ] } ``` --- # TaxIdValidation Si los datos fiscales de un cliente van a pasar el SAT. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"tax_id_validation"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `tax_id` | string | sí | El RFC revisado. | | `kind` | `company`, `person`, `generic` | sí | company (persona moral, 12 caracteres), person (persona física, 13) o generic (XAXX010101000, XEXX010101000). | | `valid` | boolean | sí | true si no hay errores: con lo que mandó, el SAT no debería rechazar la factura por los datos del cliente. | | `checked_with_sat` | boolean | sí | true si se comparó con el registro del SAT (mandó cif_id y el SAT contestó). | | `errors` | arreglo de objeto | sí | Lo que haría que el SAT rechace la factura. Cada uno con el campo (param), un código, la explicación y, si se puede, el valor correcto (suggestion). | | `errors[].param` | string | sí | El campo: tax_id, legal_name, tax_system, zip, use o cif_id. | | `errors[].code` | string | sí | Qué falló, para su código: tax_id_format, tax_id_date, legal_name_capital_regime, legal_name_mismatch, tax_system_person, tax_system_mismatch, zip_unknown, zip_mismatch, use_tax_system, cif_id_not_found… | | `errors[].message` | string | sí | La explicación, para mostrarla a su usuario. | | `errors[].suggestion` | string o null | sí | El valor correcto cuando se sabe (el del SAT con cif_id, o el nombre sin régimen de capital); null si no. | | `warnings` | arreglo de objeto | sí | Lo que no impide timbrar pero conviene revisar: el dígito verificador, mayúsculas, si el RFC está en las listas del SAT. | | `warnings[].param` | string | sí | El campo. | | `warnings[].code` | string | sí | tax_id_check_digit, legal_name_format, tax_id_listed_69b o tax_id_listed_69. | | `warnings[].message` | string | sí | La explicación. | | `warnings[].suggestion` | string o null | sí | El valor sugerido, o null. | | `sat_record` | objeto o null | sí | Lo que tiene el SAT en la constancia (con cif_id): nombre, código postal y regímenes vigentes. null sin cif_id. | | `sat_record.legal_name` | string o null | sí | El nombre o razón social, como lo tiene el SAT. | | `sat_record.zip` | string o null | sí | El código postal de su domicilio fiscal. | | `sat_record.tax_systems` | arreglo de string | sí | Sus regímenes vigentes (c_RegimenFiscal). | | `sat_error` | string o null | sí | Si el SAT no contestó a tiempo (con cif_id): reintente en unos minutos. null si contestó o no se consultó. | | `sat_lists` | objeto o null | sí | Si el RFC aparece en las listas del SAT. null con un RFC genérico o mal escrito. | | `sat_lists.object` | `"sat_list_check"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `sat_lists.tax_id` | string | sí | El RFC revisado. | | `sat_lists.name` | string o null | sí | El nombre con que aparece en las listas del SAT (null si no aparece). | | `sat_lists.listed` | boolean | sí | true si el RFC aparece en alguna lista, aunque haya desvirtuado. | | `sat_lists.risk` | `high`, `medium`, `none` | sí | high: 69-B o 69-B Bis como presunto o definitivo (el SAT dice que simula operaciones; sus facturas no se pueden deducir). medium: una lista del art. 69 (no localizado, créditos firmes o exigibles, cancelados, sentencias, CSD sin efectos). none: no aparece, o ya desvirtuó o tiene sentencia favorable. | | `sat_lists.lists` | arreglo de objeto | sí | Cada lista en que aparece; vacía si no aparece en ninguna. | | `sat_lists.lists[].list` | `69`, `69b`, `69b_bis` | sí | 69b: art. 69-B (operaciones simuladas). 69b_bis: art. 69-B Bis (pérdidas fiscales). 69: art. 69 (no localizados, adeudos, cancelados). | | `sat_lists.lists[].source` | string | sí | El archivo del SAT de donde viene (clave). | | `sat_lists.lists[].source_name` | string | sí | El nombre del archivo del SAT, para mostrarlo. | | `sat_lists.lists[].situation` | string o null | sí | Su situación según el SAT: Presunto, Definitivo, Desvirtuado, Sentencia favorable; en el art. 69, el supuesto (null si el archivo no la trae). | | `sat_lists.lists[].published_on` | string o null | sí | Fecha de publicación en el SAT o en el DOF (null si no la trae). | | `sat_lists.lists[].details` | objeto o null | sí | Lo demás que publica el SAT para ese RFC (oficios, fechas, montos), tal cual; null si nada. | | `sat_lists.lists_updated_at` | string (date-time) o null | sí | Cuándo se actualizaron las listas por última vez (cada día a las 00:00 de la Ciudad de México). | Ejemplo: ```json { "object": "tax_id_validation", "tax_id": "ICV060329BY0", "kind": "company", "valid": false, "checked_with_sat": true, "errors": [ { "param": "legal_name", "code": "legal_name_capital_regime", "message": "Sin «S.A. de C.V.»: en CFDI 4.0 el nombre va sin régimen de capital, como en la constancia.", "suggestion": "INMOBILIARIA CVA" }, { "param": "zip", "code": "zip_mismatch", "message": "El código postal no es el del domicilio fiscal que tiene el SAT.", "suggestion": "33826" } ], "warnings": [], "sat_record": { "legal_name": "INMOBILIARIA CVA", "zip": "33826", "tax_systems": [ "601" ] }, "sat_error": null, "sat_lists": { "object": "sat_list_check", "tax_id": "ICV060329BY0", "name": null, "listed": false, "risk": "none", "lists": [], "lists_updated_at": "2026-10-24T00:12:40-06:00" } } ``` --- # TaxStatusCertificate La constancia de situación fiscal más reciente del emisor, generada en el SAT con su e.firma. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"tax_status_certificate"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `issued_on` | string o null | sí | El día que la emitió el SAT (AAAA-MM-DD), o null. | | `id_cif` | string o null | sí | Su idCIF, o null. | | `tax_id` | string | sí | El RFC. | | `legal_name` | string o null | sí | Nombre o razón social como la tiene el SAT, o null. | | `tax_systems` | arreglo de objeto | sí | Sus regímenes fiscales (clave de c_RegimenFiscal y nombre). | | `tax_systems[].code` | string | sí | Clave del régimen fiscal (c_RegimenFiscal): 601, 612, 626… | | `tax_systems[].name` | string | sí | Nombre del régimen fiscal, como lo escribe el SAT. | | `zip` | string o null | sí | El código postal de su domicilio fiscal, o null. | | `status` | string o null | sí | Estatus en el padrón (ACTIVO, SUSPENDIDO…), o null. | | `pdf` | boolean | sí | true si su PDF está en GET /v1/issuers/{id}/tax_status_certificate/pdf. | | `created_at` | string (date-time) o null | sí | Cuándo se generó. | Ejemplo: ```json { "object": "tax_status_certificate", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "issued_on": "2026-10-27", "id_cif": "26010000000", "tax_id": "EKU9003173C9", "legal_name": "ESCUELA KEMPER URGATE", "tax_systems": [ { "code": "601", "name": "Régimen General de Ley Personas Morales" } ], "zip": "42501", "status": "ACTIVO", "pdf": true, "created_at": "2026-10-27T09:12:00-06:00" } ``` --- # TestClock El reloj de prueba de un emisor de pruebas: su «hoy», que solo avanza cuando usted lo adelanta, haciendo día por día lo que toca. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | clk_ y la clave del emisor. | | `object` | `"test_clock"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | `false` | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor de pruebas que vive en esta hora. | | `frozen_time` | string (date-time) | sí | La hora del reloj: el «hoy» del emisor para sus facturas recurrentes, cotizaciones, cuentas por cobrar y tickets. | | `status` | `ready`, `advancing` | sí | advancing mientras una solicitud lo adelanta; ready en cualquier otro momento. | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `advanced` | objeto | | Al adelantarlo: lo que se hizo en el camino. | | `advanced.recurring_invoices` | integer | sí | Facturas recurrentes timbradas en el camino. (de -9007199254740991 a 9007199254740991) | | `advanced.quotes_expired` | integer | sí | Cotizaciones que vencieron en el camino. (de -9007199254740991 a 9007199254740991) | | `advanced.global_invoices` | integer | sí | Facturas globales de tickets timbradas en el camino. (de -9007199254740991 a 9007199254740991) | | `stopped_early` | boolean | | true si se detuvo antes de requested_time porque ya timbró 25 comprobantes en esta solicitud: adelántelo otra vez para seguir. | | `requested_time` | string (date-time) | | A dónde se pidió adelantarlo (con stopped_early). | | `deleted` | boolean | | Siempre true. | Ejemplo: ```json { "id": "clk_0q1Vmmw52sbD", "object": "test_clock", "livemode": false, "issuer": "iss_0q1Vmmw52sbD", "frozen_time": "2026-12-01T09:00:00-06:00", "status": "ready", "created_at": "2026-10-25T09:40:00-06:00", "advanced": { "recurring_invoices": 2, "quotes_expired": 1, "global_invoices": 1 } } ``` --- # TestDataReset Lo que se borró (y se creó) al reiniciar los datos de prueba. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"test_data_reset"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | `false` | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuers` | integer | sí | Cuántos emisores de pruebas se vaciaron (archivados incluidos). (de -9007199254740991 a 9007199254740991) | | `deleted_rows` | integer | sí | Cuántos registros se borraron: facturas, clientes, productos, lotes, ligas, descargas… (de -9007199254740991 a 9007199254740991) | | `deleted_events` | integer | sí | Cuántos eventos de pruebas se borraron (con sus entregas). (de -9007199254740991 a 9007199254740991) | | `customers_created` | integer | sí | Clientes de ejemplo creados (con seed). (de -9007199254740991 a 9007199254740991) | | `products_created` | integer | sí | Productos de ejemplo creados (con seed). (de -9007199254740991 a 9007199254740991) | Ejemplo: ```json { "object": "test_data_reset", "livemode": false, "issuers": 1, "deleted_rows": 214, "deleted_events": 37, "customers_created": 3, "products_created": 3 } ``` --- # Ticket Una venta que su comprador puede facturar él mismo en el portal del emisor (autofactura). | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"ticket"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor que vendió. | | `folio` | string | sí | El folio de la venta en su sistema. | | `code` | string | sí | Lo que su comprador escribe en el portal junto con el total: el folio y 4 caracteres al azar (A123-K7QX). Imprímalo en el ticket o mándelo con la compra. | | `status` | `pending`, `invoiced`, `global`, `cancelled` | sí | pending (se puede facturar), invoiced (su comprador lo facturó: la factura en invoice), global (entró en la factura global del periodo) o cancelled. | | `url` | string o null | sí | La página del portal con el código ya escrito: el comprador pone el total y sus datos. Puede incrustarla con fiscalisto.js. null si ya no está pendiente. | | `total` | number | sí | Lo que se cobró, impuestos incluidos y sin propina. | | `tip` | number | sí | La propina (no va en la factura). | | `payment_form` | string | sí | c_FormaPago de la venta. | | `description` | string | sí | El concepto de la factura. | | `product_key` | string | sí | c_ClaveProdServ del concepto. | | `sold_at` | string (date-time) | sí | Cuándo se vendió. | | `invoiceable_until` | string | sí | El último día (AAAA-MM-DD) en que el comprador puede facturarlo; después entra en la factura global. | | `invoice` | string o null | sí | La factura (inv_…) que hizo su comprador, o null. | | `global_invoice` | string o null | sí | La factura global (inv_…) en que entró, o null. | | `customer` | objeto o null | sí | A quién se facturó, o null. | | `customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `created_at` | string (date-time) o null | sí | Cuándo se registró. | Ejemplo: ```json { "id": "tkt_OufpKqnrvx9p6hW1yQbT3mZr8sKdLx0VeN", "object": "ticket", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "folio": "A1042", "code": "A1042-K7QX", "status": "pending", "url": "https://fiscalisto.com/autofactura/Tx8mQ2vLp9WnR4c?c=A1042-K7QX", "total": 580, "tip": 0, "payment_form": "04", "description": "Consumo de alimentos", "product_key": "90101501", "sold_at": "2026-10-24T14:32:00-06:00", "invoiceable_until": "2026-10-31", "invoice": null, "global_invoice": null, "customer": null, "created_at": "2026-10-24T14:32:05-06:00" } ``` --- # TicketList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"ticket"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor que vendió. | | `data[].folio` | string | sí | El folio de la venta en su sistema. | | `data[].code` | string | sí | Lo que su comprador escribe en el portal junto con el total: el folio y 4 caracteres al azar (A123-K7QX). Imprímalo en el ticket o mándelo con la compra. | | `data[].status` | `pending`, `invoiced`, `global`, `cancelled` | sí | pending (se puede facturar), invoiced (su comprador lo facturó: la factura en invoice), global (entró en la factura global del periodo) o cancelled. | | `data[].url` | string o null | sí | La página del portal con el código ya escrito: el comprador pone el total y sus datos. Puede incrustarla con fiscalisto.js. null si ya no está pendiente. | | `data[].total` | number | sí | Lo que se cobró, impuestos incluidos y sin propina. | | `data[].tip` | number | sí | La propina (no va en la factura). | | `data[].payment_form` | string | sí | c_FormaPago de la venta. | | `data[].description` | string | sí | El concepto de la factura. | | `data[].product_key` | string | sí | c_ClaveProdServ del concepto. | | `data[].sold_at` | string (date-time) | sí | Cuándo se vendió. | | `data[].invoiceable_until` | string | sí | El último día (AAAA-MM-DD) en que el comprador puede facturarlo; después entra en la factura global. | | `data[].invoice` | string o null | sí | La factura (inv_…) que hizo su comprador, o null. | | `data[].global_invoice` | string o null | sí | La factura global (inv_…) en que entró, o null. | | `data[].customer` | objeto o null | sí | A quién se facturó, o null. | | `data[].customer.tax_id` | string | sí | RFC (Rfc), 12 caracteres una persona moral y 13 una física. | | `data[].customer.legal_name` | string o null | sí | Nombre o razón social tal como está en la constancia de situación fiscal, sin el régimen de capital (sin «S.A. de C.V.»). | | `data[].created_at` | string (date-time) o null | sí | Cuándo se registró. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # TrialBalance La balanza de comprobación de un mes. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"trial_balance"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `issuer` | string | sí | El emisor. | | `month` | string | sí | El mes (AAAA-MM). | | `journal_entries` | integer | sí | Cuántas pólizas tuvo el mes. (de -9007199254740991 a 9007199254740991) | | `accounts` | arreglo de objeto | sí | Cada cuenta con movimiento o saldo: saldo inicial, cargos, abonos y saldo final. | | `accounts[].object` | `"ledger_account"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `accounts[].number` | string | sí | Número de empleado (NumEmpleado). | | `accounts[].name` | string | sí | Nombre. | | `accounts[].grouping_code` | string | sí | El código agrupador del SAT. | | `accounts[].nature` | `debit`, `credit` | sí | debit (deudora) o credit (acreedora). | | `accounts[].opening_balance` | number | sí | El saldo al empezar el mes. | | `accounts[].debit` | number | sí | Los cargos del mes. | | `accounts[].credit` | number | sí | Los abonos del mes. | | `accounts[].closing_balance` | number | sí | El saldo al terminar el mes. | | `totals` | objeto | sí | Cargos y abonos del mes: siempre iguales. | | `totals.debit` | number | sí | Todos los cargos del mes. | | `totals.credit` | number | sí | Todos los abonos del mes. | Ejemplo: ```json { "object": "trial_balance", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "month": "2026-10", "journal_entries": 1, "accounts": [ { "object": "ledger_account", "number": "105-01", "name": "Clientes nacionales", "grouping_code": "105.01", "nature": "debit", "opening_balance": 0, "debit": 5800, "credit": 0, "closing_balance": 5800 }, { "object": "ledger_account", "number": "208-01", "name": "IVA trasladado cobrado", "grouping_code": "208.01", "nature": "credit", "opening_balance": 0, "debit": 0, "credit": 800, "closing_balance": 800 }, { "object": "ledger_account", "number": "401-01", "name": "Ventas y/o servicios gravados a la tasa general", "grouping_code": "401.01", "nature": "credit", "opening_balance": 0, "debit": 0, "credit": 5000, "closing_balance": 5000 } ], "totals": { "debit": 5800, "credit": 5800 } } ``` --- # Warehouse Un almacén del emisor: el de la Matriz y uno por sucursal se crean solos; cada factura saca existencias del de su sucursal. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"warehouse"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `issuer` | string | sí | El emisor del almacén. | | `name` | string | sí | Su nombre («Matriz», el de su sucursal, o el que le dieron en la app). | | `code` | string o null | sí | Su clave corta, o null. | | `type` | `warehouse`, `consignment` | sí | warehouse (almacén propio) o consignment (mercancía en consignación con un cliente, todavía suya). | | `branch` | string o null | sí | Su sucursal (brn_…), o null si es de la Matriz. | | `default` | boolean | sí | true si es el almacén del que vende y recibe su sucursal. | | `archived` | boolean | sí | true si está archivado. | | `created_at` | string (date-time) o null | sí | Cuándo se creó. | Ejemplo: ```json { "id": "whs_OufpKqnrvx9p1", "object": "warehouse", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "name": "Matriz", "code": null, "type": "warehouse", "branch": null, "default": true, "archived": false, "created_at": "2026-10-27T09:12:00-06:00" } ``` --- # WarehouseList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"warehouse"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].issuer` | string | sí | El emisor del almacén. | | `data[].name` | string | sí | Su nombre («Matriz», el de su sucursal, o el que le dieron en la app). | | `data[].code` | string o null | sí | Su clave corta, o null. | | `data[].type` | `warehouse`, `consignment` | sí | warehouse (almacén propio) o consignment (mercancía en consignación con un cliente, todavía suya). | | `data[].branch` | string o null | sí | Su sucursal (brn_…), o null si es de la Matriz. | | `data[].default` | boolean | sí | true si es el almacén del que vende y recibe su sucursal. | | `data[].archived` | boolean | sí | true si está archivado. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # WebhookEndpoint Una dirección de su servidor que recibe eventos. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `object` | `"webhook_endpoint"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `sandbox` | string o null | sí | El entorno de prueba (sbx_…) al que pertenece, o null si es de la cuenta. Vea /v1/sandboxes. | | `url` | string | sí | Dirección https de su servidor que recibe los avisos. | | `description` | string o null | sí | Para qué es, para usted. | | `events` | arreglo de string | sí | Eventos que quiere recibir, o ["*"] para todos (por omisión). La lista con cuándo llega cada uno: [Tipos de evento](https://fiscalisto.com/docs/api/webhooks#tipos-de-evento). | | `status` | `enabled`, `failing`, `disabled` | sí | enabled: entregando. failing: sus entregas fallan (se apaga tras 5 días así). disabled: apagado. | | `disabled_reason` | string o null | | Por qué se apagó (sus entregas fallaron 5 días seguidos, o usted lo apagó). | | `failing_since` | string (date-time) o null | | Desde cuándo fallan las entregas, o null. | | `previous_secret_expires_at` | string (date-time) o null | sí | Hasta cuándo el secreto anterior también firma (después de cambiarlo), o null. | | `api_version` | string o null | sí | La versión de la API de sus eventos; null: la de la cuenta. | | `created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `secret` | string | | Secreto de firma (whsec_…). Solo viene al crearlo o al cambiarlo: guárdelo para verificar las firmas. | Ejemplo: ```json { "id": "whe_0q1Vmmw52sbDpmtsKD1E", "object": "webhook_endpoint", "livemode": false, "sandbox": null, "url": "https://mi-plataforma.mx/webhooks/fiscalisto", "description": null, "events": [ "invoice.stamped" ], "status": "enabled", "disabled_reason": null, "failing_since": null, "previous_secret_expires_at": null, "api_version": null, "created_at": "2026-10-24T10:15:42-06:00", "secret": "whsec_MfKQ9r7Cq1ZzR0nN3xY2bW8eT5uV6sA4" } ``` --- # WebhookEndpointList | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"list"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data` | arreglo de objeto | sí | Los objetos de esta página. | | `data[].id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `data[].livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `data[].object` | `"webhook_endpoint"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `data[].sandbox` | string o null | sí | El entorno de prueba (sbx_…) al que pertenece, o null si es de la cuenta. Vea /v1/sandboxes. | | `data[].url` | string | sí | Dirección https de su servidor que recibe los avisos. | | `data[].description` | string o null | sí | Para qué es, para usted. | | `data[].events` | arreglo de string | sí | Eventos que quiere recibir, o ["*"] para todos (por omisión). La lista con cuándo llega cada uno: [Tipos de evento](https://fiscalisto.com/docs/api/webhooks#tipos-de-evento). | | `data[].status` | `enabled`, `failing`, `disabled` | sí | enabled: entregando. failing: sus entregas fallan (se apaga tras 5 días así). disabled: apagado. | | `data[].disabled_reason` | string o null | | Por qué se apagó (sus entregas fallaron 5 días seguidos, o usted lo apagó). | | `data[].failing_since` | string (date-time) o null | | Desde cuándo fallan las entregas, o null. | | `data[].previous_secret_expires_at` | string (date-time) o null | sí | Hasta cuándo el secreto anterior también firma (después de cambiarlo), o null. | | `data[].api_version` | string o null | sí | La versión de la API de sus eventos; null: la de la cuenta. | | `data[].created_at` | string (date-time) o null | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `data[].secret` | string | | Secreto de firma (whsec_…). Solo viene al crearlo o al cambiarlo: guárdelo para verificar las firmas. | | `has_more` | boolean | sí | true si hay más: pida la siguiente con starting_after igual al id del último. | | `url` | string | sí | La ruta de esta lista. | --- # WebhookTest Un evento de prueba que se mandó a su webhook, y lo que respondió su servidor. | Campo | Tipo | Siempre viene | Descripción | | --- | --- | --- | --- | | `object` | `"webhook_test"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `event` | objeto | sí | Algo que pasó en su cuenta: el objeto completo como quedó. Se manda a sus webhooks firmado (Standard Webhooks). | | `event.id` | string | sí | Identificador del objeto, con su prefijo (iss_, inv_, ret_, cus_, prd_, emp_, bat_, brn_, whs_, stm_, adj_, evt_, whe_). | | `event.object` | `"event"` | sí | Qué tipo de objeto es («invoice», «customer»…). | | `event.type` | string | sí | Qué pasó: invoice.stamped, invoice.failed, invoice.canceled, invoice.cancellation_pending, invoice.cancellation_rejected, invoice.paid, invoice.partially_paid, retention.stamped, retention.failed, retention.canceled, invoice_batch.completed, customer_data_request.completed, sat_download.completed, sat_download.failed, sat_declaration.created, sat_declaration.updated, ticket.invoiced, ticket.included_in_global, recurring_invoice.failed, quote.viewed, quote.accepted, quote.changes_requested, quote.rejected, issuer.ready, balance.low o test_clock.ready. | | `event.api_version` | string | sí | Versión de la API con que se armó el objeto. | | `event.livemode` | boolean | sí | true en producción (llaves fk_live_), false en pruebas (fk_test_). Lo de pruebas no vale ante el SAT. | | `event.created_at` | string (date-time) | sí | Cuándo se creó, ISO 8601 con la hora de la Ciudad de México (-06:00). | | `event.data` | objeto | sí | Lo que cambió, en data.object: el comprobante (o el lote) completo, como lo contesta su GET. | | `event.data.object` | objeto | sí | El objeto como quedó: una factura, una constancia o un lote. | | `delivery` | objeto | sí | Cómo le fue: ok si contestó 2xx, el código, el error y el inicio de su respuesta. | | `delivery.ok` | boolean | sí | true si su servidor contestó 2xx. | | `delivery.status` | integer o null | sí | El código HTTP que contestó; null si no se pudo conectar. | | `delivery.error` | string o null | sí | Qué falló (sin respuesta, no se pudo conectar, otro código), o null. | | `delivery.response_body` | string o null | sí | El inicio de lo que contestó su servidor (hasta 2,000 caracteres). | | `delivery.duration_ms` | integer | sí | Cuánto tardó su servidor, en milisegundos. (de -9007199254740991 a 9007199254740991) | Ejemplo: ```json { "object": "webhook_test", "event": { "id": "evt_0PKhIdhYuAGwzmMQYfkc", "object": "event", "type": "invoice.stamped", "api_version": "2026-10-24", "livemode": false, "created_at": "2026-10-24T10:15:42-06:00", "data": { "object": { "id": "inv_OufpKqnrvx9p28SINJVjjWKYRhojBTJb8P", "object": "invoice", "livemode": false, "issuer": "iss_OufpKqnrvx9p", "branch": null, "type": "I", "status": "valid", "uuid": "5F1E8C2A-3B7D-4C9E-A1B2-C3D4E5F60718", "series": null, "folio": "1", "date": "2026-10-24T10:15:42", "customer": { "tax_id": "ICV060329BY0", "legal_name": "INMOBILIARIA CVA", "tax_system": "601", "zip": "33826" }, "use": "G03", "payment_form": "03", "payment_method": "PUE", "currency": "MXN", "exchange_rate": null, "items": [ { "description": "Consultoría", "product_key": "80101500", "unit_key": "E48", "unit_name": "Unidad de servicio", "quantity": 1, "unit_price": 1000, "taxability": "02", "iva": "0.16" } ], "subtotal": 1000, "discount": 0, "taxes": { "iva": 160, "ieps": 0, "iva_withheld": 0, "isr_withheld": 0, "local_transferred": 0, "local_withheld": 0 }, "total": 1160, "payment_status": "paid", "amount_due": 0, "amount_paid": 1160, "related": null, "cancellation": null, "email": null, "error": null, "recurring_invoice": null, "created_at": "2026-10-24T10:15:42-06:00", "metadata": {} } } }, "delivery": { "ok": true, "status": 200, "error": null, "response_body": "{\"received\":true}", "duration_ms": 182 } } ```