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

# Going live

> Everything up to now ran in test mode. To stamp CFDIs that are real at the SAT you switch your

Everything up to now ran in **test mode**. To stamp CFDIs that are real at the SAT you switch your
keys from test to live and register real fiscal material. This is the checklist and the gotchas.

## Test and live are separate worlds

Test and live are **fully isolated** — separate data, separate resources, and separate keys.
`sk_test_` keys route to the PAC's test environment; `sk_live_` keys route to live stamping at the
SAT. A test key never sees live data and vice-versa. Do the whole dance in test first, then switch
to your `sk_live_` keys.

## 1. Register live CSDs

* In **live mode** the API rejects an expired or not-yet-valid CSD (422 `fiscal_profile.csd_expired`)
  — upload a current CSD (Certificado de Sello Digital) issued to the emitter's RFC. (Test mode
  tolerates expired/test CSDs; live mode does not.)
* The CSD is used to compute the *sello* **locally**; the private key never leaves this server and
  never reaches the PAC.
* You also need the taxpayer's **FIEL (e.firma)** — it signs the carta manifiesto that authorizes
  stamping (step 2 below), and the same FIEL is reused for descarga masiva. You can attach it here
  at registration or supply it inline when you sign the manifiesto.

## 2. Sign the carta manifiesto (per taxpayer)

Every new profile is created **on hold**: it comes back `status: "requires_action"` with a single
`sign_manifest` action under `next_actions` (`pending_actions` on retrieve/list), and it **cannot
stamp** until the manifiesto is signed. The SAT requires each taxpayer to authorize a PAC to
certify their CFDIs, and a **separate carta manifiesto must be signed per RFC** before that RFC's
CFDIs can be stamped. This API signs it for you — no browser portal needed. The manifiesto is
signed with the taxpayer's **FIEL (e.firma)**, not the CSD.

This gate is not live-only — it applies in **test mode too**, so the `requires_action → active` flow
is identical in both worlds. Attempting to stamp against an on-hold profile returns
**409 `fiscal_profile.manifest_required`** in either mode.

**One call, uploading the FIEL files** (they are also stored on the profile for descarga masiva):

```bash theme={null}
curl -s -X POST https://api.newinvoice.dev/v1/fiscal_profiles/$FP_ID/manifest \
  -H "authorization: Bearer $API_KEY" \
  -F "email=legal@yourcompany.mx" \
  -F "password=$FIEL_PASS" \
  -F "cer=@fiel.cer" \
  -F "key=@fiel.key"
```

(Prefer JSON? POST `{ "fiel": { "cer_base64", "key_base64", "password" }, "email" }` as
`application/json`.) If the profile already has a stored FIEL, omit it and just POST
`{ "email": "..." }` (or `-F "email=..."`). On success the response is a `manifest` object
(`status: "signed"`, `signed_at`, the signed `xml_base64` + `pdf_base64`), the profile's
`manifest_status` moves `pending_signature → signed` (so the profile flips to `status: "active"`
and its `pending_actions` empty out), and a `fiscal_profile.manifest_signed` webhook fires. An
"already signed" result is treated as idempotent success. Signing is once per profile — every stamp
call re-checks it. `email` receives the signed XML + PDF; it
falls back to the `MANIFEST_EMAIL` env default when omitted.

Notes:

* Prefer a **current, non-expired FIEL** — an expired e.firma is rejected.
* For descarga masiva there is no separate manifiesto — uploading the FIEL is itself the
  authorization.

## 3. Switch to live keys

There is nothing to configure on your side — the live PAC is already wired. The moment you send an
`sk_live_` key, that traffic is routed to live stamping at the SAT; test keys stay in the test
environment. Point your production integration at your `sk_live_` key and keep the `sk_test_` key
for staging.

## 4. Verify before you cut over

* `GET /v1/balance` returns your real remaining stamps.
* Stamp a known-good invoice for a real emitter/receptor and confirm the UUID resolves at the SAT
  (`POST /v1/validations` enriches with live SAT status).
* Confirm your webhook endpoints are **https** and every POST carries an `Idempotency-Key`.

## Descarga masiva at go-live

Descarga masiva has **no sandbox** — it only runs in production against a live FIEL stored on the
profile. Design around the SAT limits before you rely on it: only **2 identical lifetime requests**,
a **200,000 XML** cap per request, and package URLs that **expire in \~5 minutes**. See
[`massive-download.md`](/guides/massive-download).

## Failure modes to expect (and how the API surfaces them)

* **Auth (code 1)** — non-retryable; fix creds, do not loop (repeated failures lock the account). → 401/403.
* **Contract expired / no transactions (code 2)** — renew stamping balance. → mapped `pac.contract_invalid`.
* **Certificate problems (30x)** — revoked/expired/mismatched CSD; re-register. → non-retryable.
* **Duplicate (307)** — not an error; resolved as a success-replay of the prior stamp.
* **Transient (4/5)** — retry with backoff; idempotency guards against double stamps.

Full mapping: [`../errors.md`](/concepts/errors).
