Overview
The Descarga Masiva API lets you connect your RFC to SAT’s bulk download service and retrieve your full invoice history. You can run one-time requests for specific date ranges, or set up a daily schedule that automatically syncs new invoices.Key Features
- Full Invoice History - Download all issued and received CFDIs from SAT, regardless of origin
- Automatic Daily Sync - Schedule recurring downloads to keep your data up to date
- FIEL Authentication - Secure connection using your electronic signature (FIEL)
- Live Status Updates - Status polling refreshes pending requests in real time
- Metered Billing - Pay only for what you download ($0.20 MXN per XML)
Prerequisites
Before using Descarga Masiva:- Your team must have a valid RFC configured — and it must be the same RFC the FIEL belongs to. See Step 3; this is the single most common way onboarding fails.
- You need an active paid gigstack plan
- You must have your FIEL files (
.cer+.key) and their password ready
FIEL vs CSD: The FIEL (Firma Electrónica Avanzada) is your personal digital identity certificate used to authenticate with SAT. It is different from the CSD (Certificado de Sello Digital), which is used to stamp CFDI invoices. You need both for full gigstack functionality.
Setup Flow
Step 1 — Check Activation Status
Before anything else, check whether Descarga Masiva is activated for your team.Step 2 — Activate
- If your plan includes Descarga Masiva (
type: "included"): activation is free — you only pay $0.20 MXN per XML downloaded. - If activating as an add-on (
type: "addon"): also 400 MXN/RFC/month charge was removed, andpricing.addonMonthlynow returnsnull.
Step 3 — Upload your FIEL
This is the main setup step. Upload your.cer and .key files along with the password. gigstack validates your FIEL, securely stores the credentials, and automatically registers your RFC with SAT — all in one request.
⚠️ Read this before you upload: the FIEL’s RFC must equal the team’s RFC
The upload is rejected with400 "RFC mismatch"unless the RFC inside the.ceris exactly therfcstored on the team being addressed by the request. The error names both values:Three consequences worth internalizing:Practical rule: create the team with the RFC that the FIEL will belong to. For a business that is the business RFC, not the RFC of the director, the accountant, or whoever happens to hold the e-firma. This is decided at team creation (
- The team that matters is the one in
?team=, not the master team. Under gigstack Connect the request resolves against the connected team named by the query parameter. Uploading a connected client’s FIEL while pointed at your master team fails, and uploading with?team=set to a connected team whose RFC differs from the certificate fails too.- Each RFC requires its own team. There is no way to attach a second RFC to an existing team. One RFC ⇄ one team, always.
- A company FIEL cannot go on a team created with an individual’s RFC. A persona moral RFC is 12 characters; a persona física RFC is 13. If the team was created with the legal representative’s personal RFC and the FIEL is the company’s, every upload attempt will fail — and there is no way to fix it from this endpoint.
POST /v2/teams, or therfcfield ofPOST /v2/auth/signup) — long before anyone touches a certificate — which is exactly why it goes wrong: the mistake surfaces only here, at the very end of onboarding, after credentials have already been collected from the client. Verify the RFC on the team before asking anyone for their FIEL.
Query parameters:
sync_start_date is not accepted. The historical sync window is always set to the maximum SAT allows (71 months back); sending the field has no effect.
Other 400 responses from this endpoint, all with success: false:
Response:
registered: true, your RFC is connected to SAT and you can start downloading invoices immediately.
Registration only runs when you send phone. Without it the credentials are still stored, but the response comes back with registered: false and you must call POST /v2/invoices/download/register yourself. With phone present and registration failing (rare — usually a temporary SAT/PAC service issue) the message says so explicitly; retry with the same register endpoint.
Re-uploading a renewed FIEL is safe: the certificate fields are updated in place and the team’s Descarga Masiva activation and download schedule are preserved.
Connecting via API (PFX)
If you already have a PFX file (PKCS#12) — for example, one you generated programmatically or from a previous export — you can connect your FIEL directly using a JSON body instead of uploading.cer + .key files.
One team per RFC. gigstack connects one RFC per team. If you need to manage multiple RFCs (e.g. for different legal entities), create a separate gigstack team for each RFC. The same RFC check applies here. The RFC inside the PFX must equal therfcon the team being addressed — the?team=target under Connect, otherwise the API key’s own team — or the call returns400 "RFC mismatch". See the box in Step 3: a company FIEL cannot be attached to a team created with an individual’s RFC, and the only fix is a team whose RFC matches. This endpoint takes the registration phone from the team’ssupportPhone(falling back to a generic number), so there is nophonefield to send.
Response:
registered: false, registration with SAT failed. Use POST /v2/invoices/download/register to retry.
Downloading Invoices
Once activated and registered, you have two options: scheduled daily sync or manual requests.Option A — Scheduled Daily Sync
Set up a schedule and gigstack automatically downloads new invoices every day.
Response:
Option B — Manual Download Request
Submit a request for a specific date range. SAT processes it asynchronously.Date range: the SAT limits each request to one month, so gigstack splits the range into one request per calendar month. Months that already have a pending request are not submitted again.Response:
created holds the ids of the new requests (use them with GET /v2/invoices/download/status/:request_id); duplicates holds ids of identical requests already pending; duplicate is true when every month was already pending. At most 10 manual requests per team per day are accepted (429 rate_limit_exceeded).
Tracking Request Status
Check the status of your download history — pending requests are updated in real time:Checking a Single Request
status: pending, accepted, processing, completed, failed, expired), invoice_count, and — once completed — packages, a list of { "id", "index" }. Download each package with:
data.content, with content_type: "application/zip" and encoding: "base64"), not as a binary download. Packages expire after a period set by the SAT, so download them promptly.
To fetch one CFDI’s XML from the SAT without a bulk request, use GET /v2/invoices/download/invoice/:uuid.
Preview a Range, Then Import Selectively
Reading CFDI metadata from the SAT is free; only downloading an XML is billed. Use that to see what a date range contains before paying for anything.1. Preview
directions defaults to received only. The call answers 202 with a job id (satjob_…, status: "queued"): one month takes about twelve seconds at the SAT, so the work runs in the background. Only one job may run per team at a time (409 otherwise).
2. Follow the job
The CFDIs found are stored as rows in the
metadata stage. List them with GET /v2/invoices/sat?sync_state=metadata.
3. Import the XMLs you want
- Up to 500 UUIDs per call.
- The server always computes the cost itself. If you send
confirm_cost_mxnand it does not match, the call returns409instead of charging a different amount. Above 100 invoices,confirm_cost_mxnis required. - UUIDs that cannot be imported are listed in
skippedwith a reason:not_found,wrong_team,already_imported,already_queued,is_nomina(nómina XMLs contain employee data and are never downloadable) ornot_importable.
queued, estimated_cost_mxn and skipped. Each XML that arrives triggers a sat.invoice.synced webhook.
Sync Progress
data.provider, which is null until the first background refresh:
Pass
?refresh=true to recompute now (takes a few seconds).
Sync configuration and troubleshooting
GET /v2/invoices/download/schedule— the current schedule plus setup status:fiel_uploaded,registered,registered_at,sat_completed, and FIEL certificate metadata (RFC, expiry, serial number).PUT /v2/invoices/download/sync-period— re-registers the team so the provider syncs from the earliest date allowed. The body is ignored; the start date is always computed by gigstack. Requires a team RFC, a prior registration, stored FIEL credentials and a phone number on the FIEL record.POST /v2/invoices/download/enable-sync— lower-level switch that turns automatic sync on. In most cases, configure the schedule (PUT /v2/invoices/download/schedule) instead.GET /v2/invoices/download/debug— FIEL, registration, schedule and SAT connectivity details for troubleshooting.
Deactivation
To stop Descarga Masiva and remove billing:Common Errors
Browsing Downloaded Invoices
Once invoices are downloaded and saved, retrieve them with:List SAT Invoices
Response:
Get a Single SAT Invoice
Retry an XML Download
200 with message: "Invoice already has XML downloaded." and charges nothing. A successful retry triggers a sat.invoice.synced webhook with retried: true.
Generate a PDF
{ "pdf": "<base64>" }.
Import XMLs You Already Have
POST /v2/invoices/download/import, which fetches XMLs from the SAT and is billed.
Each XML is filed by your team’s RFC:
- Your RFC is the issuer → stored as an invoice, readable with
GET /v2/invoices/:id. - Your RFC is the receiver → stored with your received SAT invoices (
GET /v2/invoices/sat), already imported, so Descarga Masiva will never download or bill it. - Neither →
rejected, nothing is written.
xml (text) or content (base64):
action: created, completed (filled a Descarga Masiva row that was missing its XML), already_exists, conflict (the UUID belongs to another account) or rejected, plus direction (issued / received). Received nómina XMLs are rejected because they contain employee personal data. Status is stored as Vigente: an XML cannot show a later cancellation.