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

# Descarga Masiva SAT Guide

> Integration guide for Descarga Masiva SAT Guide

Download all your issued and received CFDI invoices directly from SAT using your FIEL (Firma Electrónica Avanzada). Descarga Masiva gives you a complete history of every invoice associated with your RFC — whether you issued it in gigstack or not.

## 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:

1. Your team must have a valid RFC configured — and it must be **the same RFC the FIEL belongs to**. See [Step 3](#step-3-—-upload-your-fiel); this is the single most common way onboarding fails.
2. You need an active paid gigstack plan
3. 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.

```http theme={null}
GET /v2/invoices/download/activate/status
```

```bash theme={null}
curl -X GET "https://api.gigstack.io/v2/invoices/download/activate/status" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json theme={null}
{
    "success": true,
    "data": {
        "status": "needs_activation",
        "planIncludesFeature": true,
        "isActivated": false,
        "pricing": {
            "perDownload": "$0.20 MXN",
            "addonMonthly": null
        }
    }
}
```

**Status values:**

| Status | Meaning | Next step |
| - | - | - |
| `active` | Ready to use | Go to Step 3 |
| `needs_activation` | Your plan includes it, not yet turned on | Call `POST /v2/invoices/download/activate` |
| `needs_addon` | Paid plan, feature not included | Call `POST /v2/invoices/download/activate` — adds the per-download meter only |
| `needs_upgrade` | Free plan | Upgrade your plan first |

***

### Step 2 — Activate

```http theme={null}
POST /v2/invoices/download/activate
```

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/activate" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json theme={null}
{
    "success": true,
    "message": "Descarga Masiva activada. Cada descarga de XML consumirá créditos SAT.",
    "data": {
        "type": "included",
        "activated": true
    }
}
```

**Billing:**

* 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 $0.20 MXN per XML downloaded. There is no monthly base fee — the former $400 MXN/RFC/month charge was removed, and `pricing.addonMonthly` now returns `null`.

***

### 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 with **`400 "RFC mismatch"`** unless the RFC inside the `.cer` is **exactly** the `rfc` stored on the team being addressed by the request. The error names both values:
>
> ```json theme={null}
> &#123;
>     "success": false,
>     "message": "RFC mismatch",
>     "error": "FIEL certificate RFC (…) does not match your team RFC (…). Each RFC requires its own team."
> &#125;
> ```
>
> Three consequences worth internalizing:
>
> 1. **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.
> 2. **Each RFC requires its own team.** There is no way to attach a second RFC to an existing team. One RFC ⇄ one team, always.
> 3. **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.
>
> **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 (`POST /v2/teams`, or the `rfc` field of `POST /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.

```http theme={null}
POST /v2/invoices/download/fiel
Content-Type: multipart/form-data
```

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/fiel?team=team_connected123" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "cert=@/path/to/your.cer" \
  -F "key=@/path/to/your.key" \
  -F "password=YOUR_FIEL_PASSWORD" \
  -F "phone=+5215512345678"
```

**Parameters:**

| Field | Type | Required | Description |
| - | - | - | - |
| `cert` | file | Yes | `.cer` file from SAT. Must be DER-encoded, unexpired, and its RFC must equal the team's `rfc`. |
| `key` | file | Yes | `.key` file from SAT (DER-encoded) |
| `password` | string | Yes | Password for the `.key` file |
| `phone` | string | Recommended | Contact phone in international format (`+52...`) |

**Query parameters:**

| Field | Required | Description |
| - | - | - |
| `team` | Under Connect | The team the FIEL is being attached to. The RFC check runs against **this** team. Omit it and the credentials go to the API key's own team. |

> `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`:

| `message` | What to do |
| - | - |
| `Team RFC not configured` | The team has no `rfc`. Set it before uploading — and set it to the RFC of the FIEL. |
| `Missing certificate file` / `Missing private key file` / `Missing password` | Supply all three multipart parts. |
| `Invalid certificate file format` / `Invalid private key file format` | The file is not DER-encoded. Upload the originals from SAT, not a converted or re-exported copy. |
| `Could not extract RFC from certificate` | The certificate parsed but carries no RFC — usually not a FIEL. |
| `RFC mismatch` | See the box above. Fix the team, not the certificate. |
| `Certificate expired` | The FIEL is past `notAfter`. Renew it with SAT. |
| `Failed to generate PFX` | The `.key` could not be decrypted — almost always a wrong password. |

**Response:**

```json theme={null}
{
    "success": true,
    "message": "FIEL credentials stored successfully. Business registered with SAT sync enabled.",
    "data": {
        "rfc": "AAA010101AAA",
        "expires_at": 1893456000000,
        "expires_at_readable": "2029-12-31T00:00:00.000Z",
        "serial_number": "00001000000504465028",
        "sync_start_date": "2019-09-01",
        "phone": "+5215512345678",
        "registered": true,
        "registered_at": 1743600000000
    }
}
```

When `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 the `rfc` on the team being addressed — the `?team=` target under Connect, otherwise the API key's own team — or the call returns `400 "RFC mismatch"`. See [the box in Step 3](#step-3-—-upload-your-fiel): 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's `supportPhone` (falling back to a generic number), so there is no `phone` field to send.

```http theme={null}
POST /v2/invoices/download/pfx
Content-Type: application/json
```

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/pfx" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "pfx": "BASE64_ENCODED_PFX_STRING",
    "pfx_password": "your-pfx-password"
  }'
```

**Parameters:**

| Field | Type | Required | Description |
| - | - | - | - |
| `pfx` | string | Yes | Base64-encoded PFX (PKCS#12) containing the FIEL certificate and private key |
| `pfx_password` | string | Yes | Password to decrypt the PFX |

**Response:**

```json theme={null}
{
    "success": true,
    "message": "FIEL credentials stored successfully. Business registered with Prodigia and SAT sync enabled.",
    "data": {
        "rfc": "AAA010101AAA",
        "expires_at": 1893456000000,
        "expires_at_readable": "2029-12-31T00:00:00.000Z",
        "serial_number": "00001000000504465028",
        "sync_start_date": "2019-09-01",
        "registered": true,
        "registered_at": 1743600000000
    }
}
```

The sync start date is always set to the maximum allowed window (71 months back) — it is not configurable via this endpoint.

If `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.

```http theme={null}
PUT /v2/invoices/download/schedule
```

```bash theme={null}
curl -X PUT "https://api.gigstack.io/v2/invoices/download/schedule" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "time": "21:00",
    "download_types": ["received"],
    "days_back": 1
  }'
```

**Parameters:**

| Field | Type | Required | Description |
| - | - | - | - |
| `enabled` | boolean | Yes | Enable or disable the schedule |
| `time` | string | Yes | Run time in `HH:mm` format (America/Mexico\_City timezone) |
| `download_types` | array | Yes | `["issued"]`, `["received"]`, or `["issued", "received"]`. Must be non-empty when `enabled` is `true`; may be `[]` when turning the schedule off |
| `days_back` | integer | Yes | How many days back to look on each run (1–90) |

**Response:**

```json theme={null}
{
    "success": true,
    "message": "Scheduled download enabled successfully",
    "data": {
        "schedule": {
            "enabled": true,
            "time": "21:00",
            "downloadTypes": ["received"],
            "daysBack": 1,
            "lastRunAt": null,
            "lastRunStatus": null,
            "lastRunError": null
        }
    }
}
```

To check your current schedule configuration and FIEL status:

```bash theme={null}
curl -X GET "https://api.gigstack.io/v2/invoices/download/schedule" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

***

### Option B — Manual Download Request

Submit a request for a specific date range. SAT processes it asynchronously.

```http theme={null}
POST /v2/invoices/download/request
```

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/request" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "start_date": "2024-01-01",
    "end_date": "2024-01-31",
    "rfc_type": "received"
  }'
```

**Parameters:**

| Field | Type | Required | Description |
| - | - | - | - |
| `start_date` | string | Yes | Start of date range (`YYYY-MM-DD`) |
| `end_date` | string | Yes | End of date range (`YYYY-MM-DD`) |
| `rfc_type` | string | No | `"issued"` or `"received"` (default: `"received"`) |
| `request_type` | string | No | `"cfdi"` (XML files) or `"metadata"` (default: `"cfdi"`) |
| `invoice_type` | string | No | Filter by CFDI type: `I`, `E`, `P`, `N`, `T` |
| `invoice_status` | string | No | `"active"`, `"cancelled"`, or `"all"` (default: `"all"`) |
| `third_party_rfc` | string | No | Filter by counterparty RFC |

> **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:**

```json theme={null}
{
    "success": true,
    "message": "2 solicitud(es) creada(s) para 2 período(s) mensual(es)",
    "data": {
        "created": ["satreq_Ab12Cd34", "satreq_Ef56Gh78"],
        "duplicates": [],
        "chunks": 2,
        "duplicate": false
    }
}
```

`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:

```http theme={null}
GET /v2/invoices/download/schedule/history
```

```bash theme={null}
curl -X GET "https://api.gigstack.io/v2/invoices/download/schedule/history" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json theme={null}
{
    "success": true,
    "data": {
        "history": [
            {
                "id": "req_abc123",
                "rfcType": "received",
                "startDate": "2024-01-01",
                "endDate": "2024-01-31",
                "status": "completed",
                "invoiceCount": 143,
                "processedCount": 143,
                "source": "manual",
                "latestIssueDate": "2024-01-30",
                "earliestIssueDate": "2024-01-02",
                "createdAt": 1706745600000,
                "completedAt": 1706832000000
            }
        ]
    }
}
```

**Status values:**

| Status | Meaning |
| - | - |
| `pending` | Request queued, being sent to SAT |
| `accepted` | SAT accepted the request |
| `processing` | SAT is generating the package |
| `completed` | Done — invoice count available |
| `failed` | SAT rejected (check `statusMessage`) |
| `expired` | Package expired before download |

***

### Checking a Single Request

```http theme={null}
GET /v2/invoices/download/status/:request_id
```

Returns the SAT status of one request (`status`: `pending`, `accepted`, `processing`, `completed`, `failed`, `expired`), `invoice_count`, and — once `completed` — `packages`, a list of `{ "id", "index" }`. Download each package with:

```http theme={null}
GET /v2/invoices/download/package/:package_id
```

The ZIP comes back **base64-encoded inside JSON** (`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

```http theme={null}
POST /v2/invoices/download/preview
```

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/preview" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "start_date": "2026-01-01", "end_date": "2026-08-31", "directions": ["received"] }'
```

`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

| Endpoint | Purpose |
| - | - |
| `GET /v2/invoices/download/jobs` | Your last 20 jobs, newest first, with progress and cost estimate |
| `GET /v2/invoices/download/jobs/:id` | One job, plus its month windows and each window's state |
| `POST /v2/invoices/download/jobs/:id/cancel` | Stop a job; it finishes the window in flight, then stops |

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

```http theme={null}
POST /v2/invoices/download/import
```

**This is the billed call:** \$0.20 MXN per XML, charged once per CFDI.

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/import" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "uuids": ["6741A863-04BE-49FE-BB76-E1657AB6B7EA"], "confirm_cost_mxn": 0.2 }'
```

* Up to 500 UUIDs per call.
* The server always computes the cost itself. If you send `confirm_cost_mxn` and it does not match, the call returns `409` instead of charging a different amount. Above 100 invoices, `confirm_cost_mxn` is required.
* UUIDs that cannot be imported are listed in `skipped` with a reason: `not_found`, `wrong_team`, `already_imported`, `already_queued`, `is_nomina` (nómina XMLs contain employee data and are never downloadable) or `not_importable`.

The response reports `queued`, `estimated_cost_mxn` and `skipped`. Each XML that arrives triggers a `sat.invoice.synced` [webhook](/guides/webhooks).

***

## Sync Progress

```http theme={null}
GET /v2/invoices/download/progress
```

The SAT hands over your history in month-sized windows, starting at your sync start date. Until the backfill reaches the present, a query for recent dates succeeds but returns nothing — which looks exactly like having no invoices. This endpoint tells the two apart. The values live under `data.provider`, which is `null` until the first background refresh:

| Field | Meaning |
| - | - |
| `percent` | How much of the requested history has arrived |
| `covered_through` | Last date the backfill has reached |
| `months_remaining` | Roughly how much history is still pending |
| `current` | History is close enough to the present to be usable |
| `stalled` | The backfill has not advanced in over two days |
| `enabled` | Sync is active; when `false` it will not advance on its own |
| `eta_at` | Projected completion (epoch ms); omitted while `stalled` |

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:

```http theme={null}
POST /v2/invoices/download/deactivate
```

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/download/deactivate" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

Scheduled downloads stop immediately. If the feature was billed as an add-on, the charge is removed from your subscription (prorated). Any XML downloads already recorded in the current period are still billed at period end.

***

## Common Errors

| Error | Cause | Fix |
| - | - | - |
| `RFC mismatch` | Certificate RFC doesn't match your team RFC | Upload the FIEL that belongs to this team's RFC |
| `Certificate expired` | FIEL has expired | Renew your FIEL at SAT's portal |
| `Failed to decrypt private key` | Wrong password for the `.key` file | Use the password created when generating the FIEL |
| `Invalid certificate file format` | Wrong file type | Upload `.cer` and `.key` files directly from SAT — do not convert |
| `Team RFC not configured` | Team has no RFC | Set up your fiscal information in gigstack settings first |
| `No active subscription` | No paid plan | Activate a paid plan before using Descarga Masiva |
| `duplicate: true` | Same request already submitted | No action needed — the existing request is still processing |
| `rate_limit_exceeded` (`429`) | More than 10 manual download requests today (Mexico City time) | Wait until midnight Mexico City time, or rely on the scheduled daily sync |
| `billing_not_activated` (`403`) | Descarga Masiva is not activated for the team | Call `POST /v2/invoices/download/activate` first |

***

***

## Browsing Downloaded Invoices

Once invoices are downloaded and saved, retrieve them with:

### List SAT Invoices

```http theme={null}
GET /v2/invoices/sat
```

```bash theme={null}
curl "https://api.gigstack.io/v2/invoices/sat?direction=received&from=2025-01-01&to=2025-12-31&limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Query Parameters:**

| Parameter | Type | Description |
| - | - | - |
| `direction` | `issued` \| `received` | Filter by invoice direction |
| `status` | `Vigente` \| `Cancelado` | Filter by SAT status |
| `invoice_type` | `I` \| `E` \| `P` \| `N` \| `T` | Filter by CFDI type |
| `issuer_rfc` | string | Filter by issuer RFC |
| `receiver_rfc` | string | Filter by receiver RFC |
| `from` | `YYYY-MM-DD` | Issue date from (inclusive) |
| `to` | `YYYY-MM-DD` | Issue date to (inclusive) |
| `limit` | integer | Results per page, 1–100 (default: 20) |
| `starting_after` | string | UUID cursor for next page |

**Response:**

```json theme={null}
{
    "success": true,
    "data": [
        {
            "id": "6741A863-04BE-49FE-BB76-E1657AB6B7EA",
            "uuid": "6741A863-04BE-49FE-BB76-E1657AB6B7EA",
            "direction": "received",
            "issuer": { "rfc": "AAA010101AAA", "name": "SOME SUPPLIER SA DE CV" },
            "receiver": { "rfc": "BBB020202BBB", "name": "MI EMPRESA SA DE CV" },
            "invoice_type": "I",
            "subtotal": 103.44,
            "total": 120.0,
            "currency": "MXN",
            "exchange_rate": null,
            "issue_date": "2025-01-15",
            "stamp_date": "2025-01-15T12:34:56",
            "cancellation_date": null,
            "status": "Vigente",
            "pac_rfc": "SAT970701NN3",
            "version": "4.0",
            "certificate_number": "00001000000513342038",
            "download_request_id": "satreq_abc123",
            "has_xml": false,
            "created_at": 1736900000000,
            "updated_at": 1736900000000
        }
    ],
    "has_more": true,
    "next": "6741A863-04BE-49FE-BB76-E1657AB6B7EA",
    "count": 20
}
```

### Get a Single SAT Invoice

```http theme={null}
GET /v2/invoices/sat/:uuid
```

```bash theme={null}
curl "https://api.gigstack.io/v2/invoices/sat/6741A863-04BE-49FE-BB76-E1657AB6B7EA" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

***

### Retry an XML Download

```http theme={null}
POST /v2/invoices/sat/:uuid/retry-xml
```

Retries the XML download for a **received** SAT invoice stuck in processing or error. If the XML is already stored, it answers `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

```http theme={null}
POST /v2/invoices/sat/:uuid/pdf
```

Generates a PDF from the stored XML of a received SAT invoice (the XML must have been downloaded). The result is cached. The response is **not** enveloped: it is `{ "pdf": "<base64>" }`.

***

### Import XMLs You Already Have

```http theme={null}
POST /v2/invoices/import
```

Uploads up to 50 stamped CFDI XMLs you already hold (from the SAT portal, a supplier, another system). **Free**, and it needs no FIEL or Descarga Masiva activation. Not to be confused with `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.

Send each file as `xml` (text) or `content` (base64):

```bash theme={null}
curl -X POST "https://api.gigstack.io/v2/invoices/import" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "files": [ { "filename": "proveedor-a12.xml", "xml": "<?xml version=\"1.0\"?><cfdi:Comprobante ...>" } ] }'
```

Each result carries `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.

***

## Full Endpoint Reference

| Method | Endpoint | Description |
| - | - | - |
| `GET` | `/v2/invoices/download/activate/status` | Check activation status |
| `POST` | `/v2/invoices/download/activate` | Activate billing |
| `POST` | `/v2/invoices/download/deactivate` | Deactivate billing |
| `POST` | `/v2/invoices/download/fiel` | Upload FIEL credentials (.cer + .key, multipart) |
| `POST` | `/v2/invoices/download/pfx` | Upload FIEL via PFX (JSON body, alternative to .cer + .key) |
| `POST` | `/v2/invoices/download/register` | Manual SAT registration (fallback) |
| `GET` | `/v2/invoices/download/schedule` | Get schedule + FIEL status |
| `PUT` | `/v2/invoices/download/schedule` | Save schedule config |
| `GET` | `/v2/invoices/download/schedule/history` | Download request history |
| `POST` | `/v2/invoices/download/request` | Submit manual download request |
| `GET` | `/v2/invoices/download/status/:request_id` | Check specific request |
| `GET` | `/v2/invoices/download/package/:package_id` | Download a package (base64 ZIP inside JSON) |
| `GET` | `/v2/invoices/download/invoice/:uuid` | Get single CFDI from SAT |
| `GET` | `/v2/invoices/download/debug` | Debug SAT connectivity |
| `GET` | `/v2/invoices/sat` | List downloaded SAT invoices |
| `GET` | `/v2/invoices/sat/:uuid` | Get a single SAT invoice |
| `GET` | `/v2/invoices/download/progress` | SAT history sync progress |
| `POST` | `/v2/invoices/download/preview` | Preview a date range (free, runs in the background, `202`) |
| `GET` | `/v2/invoices/download/jobs` | List preview and import jobs |
| `GET` | `/v2/invoices/download/jobs/:id` | Get one job with its month windows |
| `POST` | `/v2/invoices/download/jobs/:id/cancel` | Stop a running job |
| `POST` | `/v2/invoices/download/import` | Download the XMLs of chosen CFDIs (billed) |
| `PUT` | `/v2/invoices/download/sync-period` | Re-register to sync from the earliest allowed date |
| `POST` | `/v2/invoices/download/enable-sync` | Turn on automatic sync |
| `POST` | `/v2/invoices/sat/:uuid/retry-xml` | Retry a stuck XML download |
| `POST` | `/v2/invoices/sat/:uuid/pdf` | Generate a PDF for a received SAT invoice |
| `POST` | `/v2/invoices/import` | Import CFDI XMLs you already hold, filed as issued or received (free) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.