
Automate the Factura Global Inside the 24-Hour SAT Window: The Daily Job the PAC Blogs Skip
When you sell to público en general (retail, e-commerce, cash) and no individual invoice is requested, you don't issue a CFDI per sale — you aggregate them into one CFDI global under regla 2.7.1.21 RMF 2026, timbrado within 24 hours of the period's close. Build a daily scheduled job: pull público sales, exclude already-invoiced ones, assemble the CFDI 4.0 (RFC XAXX010101000), stamp at your PAC, store the UUID, alert on failure. Make it idempotent. RESICO emits monthly only. I'm an engineer, not a contador.
The operational reality: a missed 24-hour window is non-compliance
If you sell to público en general, you don’t issue a CFDI per sale — you aggregate the period’s non-invoiced sales into one factura global (CFDI global) and timbrar it within 24 hours of the close of operations (regla 2.7.1.21 of the RMF 2026). The reliable way to hit that deadline every period is a scheduled, idempotent job — not a human who remembers.
Now the detail. “Público en general” is any customer who buys and walks away without asking for an invoice: retail walk-ins, e-commerce checkouts, OXXO/cash tickets. For those, you do not issue an individual CFDI. Instead you aggregate them into a single factura global, one comprobante that stands in for all the non-invoiced sales of the period.
The rule that governs this is regla 2.7.1.21 of the RMF 2026 (RMF = Resolución Miscelánea Fiscal, the SAT’s yearly operating rulebook). It lets you emit one global comprobante for the period’s público-en-general operations, with the IVA (value-added tax) and IEPS (excise tax) amounts separated.
The teeth are in the deadline. The global CFDI has to reach the SAT — in practice, your PAC (Proveedor Autorizado de Certificación, the authorized stamping provider) — within 24 hours following the close of the operations. Close the day, and you have 24 hours to timbrar (stamp) the global that covers it.
I’m an engineer, not a contador (accountant), and I’ll keep saying that. But the part I do own is this: that 24-hour window is a reliability problem, not a fiscal one. You have público sales piling up every single day, a hard deadline, and a requirement that you never emit a duplicate. That is exactly the kind of task a human drops on a busy day — the day you most need it to run. So you don’t do it by hand when you remember. You build a scheduled, idempotent job that runs every period and screams when it fails. That job is what the PAC-marketing blogs gloss over, and it’s what this post is about.
Is the 24-hour deadline new for 2026? No — and here’s the honest history
You’ll see PAC blogs and accounting newsletters package the 24-hour deadline as breaking news for 2026. It isn’t, and I’d rather you trust the rest of this post.
The 24-hour window is real and current under regla 2.7.1.21 RMF 2026 — but the cut from 72 hours down to 24 hours happened back in 2022. RMF 2026 simply carries the same rule forward. There’s no new clock starting this year.
So treat it for what it is: a standing operational obligation. If you’ve been selling to público en general, you should already have this automated. If you don’t, the fix isn’t to panic about a 2026 change — it’s to build the job once and stop thinking about it.
One discipline I’d push on you, though: treat every SAT rule as time-sensitive. As of 2026, confirm current against primary sources before you hardcode a deadline, a clave (catalog code), or a threshold. The whole point of a reliable job is that it’s correct, and correctness starts with not trusting a blog’s stale copy of a catalog value — including this one.
Per-sale CFDI vs the factura global: when each one applies
The entire job hinges on one decision boundary, so let’s nail it.
An individual CFDI happens on request. A customer wants an invoice, hands you their RFC (their tax ID) and fiscal data, and you issue one CFDI for that single sale. If you’re already doing this from your payment flow, that’s the per-sale CFDI pipeline from Stripe and Mercado Pago — and the factura global aggregates everything that pipeline didn’t invoice.
The factura global is the opposite end: one aggregated comprobante covering all the público-en-general operations of the period where no individual invoice was requested.
The two are mutually exclusive per sale. A sale that already got an individual CFDI must not also land in the global — if it does, you’ve counted that income twice, and now you’ve got a fiscal mess to unwind. That dedupe is the load-bearing logic later in the job.
This is also why the global specifically needs a reliable scheduled job and the individual CFDI mostly doesn’t. The individual one is event-driven — it fires when a customer asks. The global is recurring, time-boxed to 24 hours, and aggregates a moving set of sales that changes every period. Manual emission doesn’t scale and breaks precisely on the high-volume days. And while the regla lets you choose to emit daily, weekly, monthly or bimonthly, the 24-hour-after-close window applies to whichever period you pick.
Per-sale CFDI
- Customer requests an invoice
- They give you RFC + fiscal data
- One CFDI per single sale
- Event-driven, fires on request
- Receptor = the customer
Factura global
- No individual invoice requested
- One comprobante for the whole period
- Aggregates all público sales
- Recurring, 24h-after-close window
- Receptor = PUBLICO EN GENERAL (XAXX)
The fields that make a CFDI global valid (RFC XAXX, Régimen 616, Uso S01)
Here’s the receiver block an engineer actually needs to assemble. Get these exact — claves are not negotiable — and confirm them against the SAT Guía de llenado before you ship, because catalog values do change.
- Format: CFDI 4.0.
- Receptor RFC:
XAXX010101000— the generic RFC for público en general. - Nombre:
PUBLICO EN GENERAL— keep the legal value all-caps and un-accented (no “Público”). - Régimen Fiscal del receptor:
616(Sin obligaciones fiscales). - UsoCFDI:
S01(Sin efectos fiscales). - DomicilioFiscalReceptor: the ISSUER’s código postal (your CP, not the customer’s — there is no customer).
- You must separate (desglosar) the IVA and IEPS amounts in the comprobante.
A minimal, illustrative sketch of the receiver block — confirm field placement against the SAT Guía:
<!-- ILLUSTRATIVE — CFDI 4.0 global receptor block. Confirm vs SAT Guía de llenado del CFDI global 4.0.
DomicilioFiscalReceptor = the ISSUER's CP (e.g. 72000), not the customer's. -->
<cfdi:Receptor
Rfc="XAXX010101000"
Nombre="PUBLICO EN GENERAL"
DomicilioFiscalReceptor="72000"
RegimenFiscalReceptor="616"
UsoCFDI="S01" />
That DomicilioFiscalReceptor catching the issuer’s CP trips people up constantly — there’s no real receiver, so the SAT wants your postal code there. Confirm the exact catalog values and placement against the SAT Guía de llenado del CFDI global 4.0 before you build.
The daily job, end to end: close → exclude → assemble → timbrar → store → alert
This is the moat — the actual pipeline. Think of it as six stages, each with its own failure mode.
Stage 1 — Pull. At the period’s close, pull all público-en-general sales for the period from your system (your orders or payments table). Failure mode: pulling the wrong window, or pulling before the day is actually closed.
Stage 2 — Exclude. Drop the sales that were already individually invoiced — that’s the double-counting guard from earlier. Also drop any sub-threshold tickets that weren’t requested (the threshold and the rules around it are contador territory — confirm current). Failure mode: a sale slips through into both the individual CFDI and the global.
Stage 3 — Assemble. Build the CFDI 4.0 global: the XAXX010101000 receiver block (PUBLICO EN GENERAL / 616 / S01 / issuer CP) plus the aggregated amounts as conceptos, with IVA and IEPS desglosado.
Stage 4 — Timbrar. Stamp it at your PAC, inside the 24-hour window. The stamping call is provider-specific — every PAC has its own SOAP or REST endpoint.
Stage 5 — Store + reconcile. Persist the returned UUID (the folio fiscal) and a link back to the underlying sales, so you can later prove exactly which sales the global covers when the SAT or your contador asks.
Stage 6 — Alert. If any stage fails, alert loudly. The entire reason you built this is to never silently blow the 24-hour window — a job that fails quietly is worse than no job, because you’ll think it ran.
Here’s an illustrative skeleton (vendor-neutral; the PAC call is yours to fill in):
# ILLUSTRATIVE — daily factura global job. Confirm fields/rules vs SAT Guía + your contador.
def run_global_job(period_key): # period_key e.g. "2026-06-29" or "2026-06"
if already_emitted(period_key): # idempotency guard: keyed on a STORED UUID
return log.info(f"global already timbrado for {period_key}, no-op")
sales = pull_publico_sales(period_key) # Stage 1: pull (deterministic)
sales = [s for s in sales if not s.individually_invoiced] # Stage 2: exclude
sales = exclude_non_requested_sub_threshold(sales) # confirm rules w/ contador
# Safe to re-assemble on retry: the guard above keys on a stored UUID, not job-start,
# so a timbrar failure resumes here without ever emitting a second global.
cfdi = build_global_cfdi_40( # Stage 3: assemble
receptor={"Rfc": "XAXX010101000",
"Nombre": "PUBLICO EN GENERAL",
"RegimenFiscalReceptor": "616",
"UsoCFDI": "S01",
"DomicilioFiscalReceptor": ISSUER_CP},
conceptos=aggregate_with_iva_ieps_desglosado(sales),
)
try:
uuid = pac_timbrar(cfdi) # Stage 4: timbrar (provider-specific)
except PacError as e:
alert(f"GLOBAL TIMBRADO FAILED {period_key}: {e}") # Stage 6: alert
raise
store_global(period_key, uuid, [s.id for s in sales]) # Stage 5: store + reconcile
Idempotency: why a re-run must never emit a second global
This is the single most important correctness property, and it’s the one PAC tutorials never cover.
A re-run — a retry after a transient error, a manual kick because someone got nervous, an overlapping cron that fired twice — must not emit a second global for the same period. Two globals for one day means duplicated income on your books and a fiscal cleanup you do not want.
Key idempotency on the period. Before you assemble anything, check whether a global already exists and was timbrado for that exact period key. If it does, no-op and move on. The stored UUID against the period is your “already done” guard — its presence is the whole signal.
Two more things make this robust:
- Make the pull-and-exclude deterministic, so a retry over the same period produces the same set of sales. If the input set drifts between runs, your idempotency check is lying to you.
- Handle the partial-failure case explicitly: assembled fine but the PAC timbrado failed. You want a safe retry that resumes from where it broke — not one that re-emits a fresh global. So the “already emitted” guard should key on a successfully stored UUID, not merely on “the job started for this period.”
This same scheduled-deadline-plus-idempotency shape shows up across the CFDI pipeline — the complemento de pago (REP) is another timed CFDI job with the same discipline, and refunds run the CFDI de Egreso / credit-note side of it. Build the pattern once and you reuse it everywhere.
RESICO and the edge cases I hand back to a contador
There’s one cadence exception you have to respect in code: RESICO (Régimen Simplificado de Confianza) taxpayers can only emit the global CFDI monthly — the total of the period — not daily or weekly.
The engineering consequence is simple: don’t hardcode daily. Make the job’s cadence configurable and let the régimen — i.e., the contador’s decision — drive it. The same codebase should run a daily global for one taxpayer and a monthly one for a RESICO taxpayer with a config flag, nothing more.
And here’s the line I draw, plainly. What I own as the engineer: the reliable, on-time, idempotent job and the audit trail — the UUID-to-sales link that proves what got covered. What the contador owns: which sales count, which cadence applies, which thresholds matter, and what your régimen’s obligations actually are.
The exact thresholds, the daily-vs-monthly choice, the rules for sub-threshold and non-requested tickets — that’s all SAT/contador territory, and it changes. While you’re mapping SAT obligations, note that this isn’t the only one for builders in this space — marketplace tax withholding is another one on the same plate. Treat every catalog value and threshold as confirm current against the SAT before you ship.
- Schedule the jobDaily (or the cadence your régimen requires)
- Pull the period's salesAll público-en-general operations for the window
- DedupeExclude anything already individually invoiced
- Assemble the globalCFDI 4.0, XAXX receptor, IVA/IEPS desglosado
- Timbrar within 24hStamp at the PAC before the window closes
- Store + reconcile + alertUUID to sales, idempotent on the period key
FAQ: factura global, the 24h window, and double-counting
What RFC and data go in the receiver?
RFC XAXX010101000, name PUBLICO EN GENERAL, Régimen 616, Uso S01, and DomicilioFiscalReceptor set to the issuer’s CP. Confirm against the SAT Guía de llenado.
How long do I have to timbrar it? Within 24 hours of the close of the operations for the period (regla 2.7.1.21 RMF 2026).
Is the 24-hour deadline new for 2026? No. It dropped from 72h to 24h back in 2022; RMF 2026 just carries it forward.
How do I avoid double-counting income? Exclude any sale that already got an individual CFDI before you aggregate, and store the UUID-to-sales link so you can prove which sales the global covers.
I’m on RESICO — can I run it daily? No. RESICO emits the global monthly only. Make the cadence a config flag.
Can I emit weekly instead of daily? Yes — the regla allows daily, weekly, monthly or bimonthly (RESICO excepted). Confirm the cadence with your contador.
Ship the job, not the heroics
The win here isn’t knowing the fields — claves you can look up. The win is the job that runs every period, on time, idempotently, and screams when it fails. Knowing XAXX010101000 doesn’t help you on the day you forgot to emit yesterday’s global.
Build it once: scheduled, deduped, stamped, stored, alerting. After that it’s not a deadline you can miss — it’s infrastructure. And it all lives in the same CFDI & e-invoicing pipeline as your per-sale invoices, your REPs, and your egresos.
Then go confirm the fiscal specifics — thresholds, cadence, your régimen — with your contador and the current SAT Guía de llenado del CFDI global 4.0 before you ship. I’m the engineer; that part’s theirs. See also Alegra’s walkthrough on emitting the factura global CFDI 4.0 for a non-engineer view of the same comprobante.