> ## Documentation Index
> Fetch the complete documentation index at: https://invoiceapi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# SAT sync — every CFDI the SAT has on file

> Register a taxpayer's RFC once. We backfill their CFDI history from the SAT — up to the full 71-month window — and then keep it current, automatically.

Register a taxpayer's RFC once. We backfill their CFDI history from the SAT — up to the full
71-month window — and then keep it current, automatically. You get every CFDI the SAT holds for
that RFC: the ones they **issued**, and the ones they **received** from their suppliers.

This is a different thing from [`/v1/invoices`](/guides/stamping-your-first-invoice). An invoice is a
CFDI *you* stamped through this API. A **sat\_document** is any CFDI the SAT has on file for that
RFC — including a supplier's invoice *to* the taxpayer, and everything they issued long before
they ever integrated with you.

It is also different from [massive download](/guides/massive-download): that is an on-demand pull for
one date range, hemmed in by hard SAT limits. This is a standing sync you turn on once.

## 1. Attach a FIEL

Sync authorizes with the taxpayer's **FIEL (e.firma)** — not the CSD. Attach it to the fiscal
profile (`fiel_cer`, `fiel_key`, `fiel_password`); without one you get
**422 `sync.fiel_required`**.

## 2. Turn sync on

```bash theme={null}
curl -s -X POST https://api.newinvoice.dev/v1/fiscal_profiles/$FP_ID/sync \
  -H "authorization: Bearer $API_KEY" -H 'content-type: application/json' \
  -d '{ "max_monthly_documents": 500 }' | jq
```

```json theme={null}
{
  "object": "fiscal_profile_sync",
  "rfc": "AAA010101AAA",
  "enabled": true,
  "status": "pending",
  "sync_from": "2020-08-01",
  "synced_through": null,
  "lag_days": null,
  "documents": 0
}
```

| Field                   | Notes                                                                                                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sync_from`             | How far back to backfill. Defaults to the **maximum history the SAT allows**: 71 months back, floored to the 1st of that month. Ask for more and we clamp it rather than fail.      |
| `max_monthly_documents` | Roughly how many CFDIs this taxpayer issues + receives per month. **Set it honestly** — it decides how the historical backfill is chunked, and a bad estimate makes it chunk badly. |
| `phone`                 | Optional; used upstream for notifications.                                                                                                                                          |

Enabling is **idempotent** — re-enabling an already-registered RFC is a no-op, not an error.

The backfill runs in the background, chunk by chunk. Six years of history is many small jobs, not
one enormous request, so it makes steady progress rather than timing out.

## 3. Read the documents

```bash theme={null}
curl -s "https://api.newinvoice.dev/v1/sat_documents?side=received&limit=50" \
  -H "authorization: Bearer $API_KEY" | jq
```

Filter by `fiscal_profile_id`, `side` (`issued` / `received`), and `status`
(`vigente` / `cancelado`); cursor-paginate with `starting_after`. Each row carries the folio
fiscal (`uuid`), both parties, `total`, `subtotal`, `type`, `serie`/`folio`, and the SAT status.

Cancellations flow through: if a CFDI you already mirrored is cancelled later, the next sync flips
its `status` to `cancelado` and fills `cancelled_at` on the **same** row.

## Freshness: expect 1–3 days, and we tell you exactly

`synced_through` is the honest watermark — **we have your CFDIs up to and including this date**.
`lag_days` is how far that trails today.

**A healthy sync sits 1–3 days behind, and that floor cannot be beaten.** It is not our latency:
the SAT itself does not publish a CFDI to its bulk-download service until roughly 24–72 hours
after it is issued. Any vendor claiming real-time SAT data for received invoices is describing
something the SAT does not offer.

So: use `/v1/invoices` for the CFDIs you stamp (those are immediate). Use SAT sync for the
complete fiscal picture, and read `synced_through` before you reconcile a period.

## Health: `enabled` is not the same as `working`

The upstream sync can **stop silently**. When the SAT rejects one of its requests, it stops
fetching for that RFC and does not recover on its own — while still reporting itself as enabled.
Left alone, a taxpayer's data simply stops updating and nothing says so.

We watch the watermark instead of the flag. If it stops advancing, we mark the profile
**`stalled`**, attempt an automatic repair, and fire a webhook:

```json theme={null}
{
  "type": "fiscal_profile.sync_stalled",
  "data": {
    "rfc": "AAA010101AAA",
    "synced_through": "2026-05-25",
    "lag_days": 50,
    "repair_attempted": true
  }
}
```

**Subscribe to `fiscal_profile.sync_stalled`.** It is the only warning that a taxpayer's fiscal
data has quietly gone stale.

### When the automatic repair is not enough: an expired FIEL

The most common cause of a permanent stall is an **expired or revoked e.firma**.

This one is nasty, and worth understanding:

* A FIEL is **not** validated when you register it. Registration succeeds with a dead certificate.
* The failure only appears later, when the SAT rejects the data requests.
* The result looks identical to a transient stall — except no repair fixes it.

The signature is a sync that ran fine for a long time and then froze on a specific date, with every
subsequent request rejected. An e.firma is valid about four years, so this tends to hit taxpayers
who registered a while ago.

**The fix is a renewed e.firma** — the taxpayer renews it at the SAT, you attach the new `.cer`/`.key`
to the fiscal profile, and then **call `POST /v1/fiscal_profiles/:id/sync` again**. Re-enabling
pushes the renewed certificate to the sync service (it does not just re-toggle), so the nightly job
recovers on its next run. No amount of retrying the *old* certificate will substitute. If a profile
stays `stalled` after a repair attempt, check the certificate's validity before anything else.

## Status reference

| `status`   | Meaning                                                                        |
| ---------- | ------------------------------------------------------------------------------ |
| `inactive` | Sync is off.                                                                   |
| `pending`  | Registered; the first sync has not been observed yet.                          |
| `healthy`  | The watermark is advancing normally.                                           |
| `stalled`  | The watermark has stopped. We are repairing; if it persists, suspect the FIEL. |

## Turning it off

```bash theme={null}
curl -s -X DELETE https://api.newinvoice.dev/v1/fiscal_profiles/$FP_ID/sync \
  -H "authorization: Bearer $API_KEY"
```

Already-mirrored documents are kept; nothing new is pulled.

## Next

* [Massive download](/guides/massive-download) — one-off pull for a specific range
* [Webhooks](/guides/webhooks) — subscribe to `fiscal_profile.sync_stalled`
* Error codes: [`../errors.md`](/concepts/errors) (the `sync.*` family)
