https://api.newinvoice.dev. Do it all in test mode (an sk_test_... key) — test-mode stamps
go to the PAC flagged prueba=true, so they are not reported to the SAT and no real CFDI is issued.
To stamp a test invoice right away with no profile at all, see
Stamp with no setup. When you’re ready for real invoices, swap the test key for a
live one (see Going live).
0. Get your API key
Sign up athttps://api.newinvoice.dev/signup and open the
dashboard. Each organization gets a test key (sk_test_...) and a live key
(sk_live_...); copy the test key — it’s shown once.
/v1 call needs Authorization: Bearer $API_KEY. The key’s mode scopes all data: a test key
never sees live resources and vice-versa, and livemode appears on every resource.
1. Register a fiscal profile (emitter + CSD)
An invoice is issued by a fiscal profile: an RFC plus its CSD (Certificado de Sello Digital). Upload the.cer and .key files directly (multipart) with the private-key password; the RFC,
serial and validity window are read from the certificate. The key material is AES-256-GCM encrypted
at rest and never returned.
csd_cer, csd_key, csd_password for the CSD, and fiel_cer,
fiel_key, fiel_password if you also attach a FIEL. Prefer JSON? Send the same data as
application/json with csd: { cer_base64, key_base64, password } — both content types are accepted.
Testing without a production CSD? Test mode tolerates expired and SAT test certificates, so
you can onboard with a free SAT test CSD. Live mode rejects expired CSDs (422
fiscal_profile.csd_expired) — use a current CSD to go live.
A test profile can stamp straight away, so continue at step 3.
A live profile comes back on hold. It has not yet been authorized to stamp, so its response
carries status: "requires_action" and a single next_actions entry telling you exactly what to do
next:
pending_actions. When the profile is
authorized both arrays are [] and status is "active".
2. Sign the carta manifiesto (live mode)
The PAC requires a signed carta manifiesto per RFC to authorize timbrado (invoice stamping) for that taxpayer — this is the SAT/PAC authorization, not the CSD. This API signs it for you; there is no browser portal to visit. The gate applies in live mode only, so skip this step in test mode.
Warning: the manifiesto has no test mode. Signing with a real FIEL creates a real authorization
with the PAC for that RFC, even with a sk_test_ key. See Testing.
The manifiesto is signed with the taxpayer’s FIEL (e.firma):
-F cer= -F key= -F password= (or fiel: { cer_base64, key_base64, password \} in JSON), or reuses a FIEL already stored on the profile. On success the
manifiesto is signed once and for all: the profile flips to status: "active" (manifest_status: "signed"), next_actions/pending_actions empty out, and a fiscal_profile.manifest_signed
webhook fires. Signing is idempotent — an “already signed” result is treated as success.
Skip this step in live mode and the very next stamp attempt returns 409
fiscal_profile.manifest_required, with remediation pointing back to this endpoint. Every stamp call checks
the manifiesto, so you only ever sign it once per profile.
3. Create + stamp an invoice
Send friendly JSON. You do not send the issuer — it comes from the profile. Omit taxes and each item defaults to 16% IVA traslado;subtotal/total are computed and rounded to the
currency’s decimals.
status: "stamped", a real uuid (folio fiscal), total: "116.00", serie: "A",
folio: "1", the sello_cfd/sello_sat, and xml_url/pdf_url.
Required fields, briefly
Common coherence rules the API enforces (returned as 422
cfdi.invalid_input with a path):
payment_method: "PPD"forcespayment_form: "99"(Por definir).- Type
Pmust not carry items orpayment_method; it needspayments. - Type
Eshould carryrelation(type01nota de crédito or03devolución) + original UUID. - Receptor
XAXX010101000(público en general) requiresglobal_information. - Non-MXN
currencyrequiresexchange_rate.
4. Draft, then stamp later (optional)
Pass"draft": true to build and sign without stamping; the invoice returns status: "draft".
Stamp it when ready:
5. Fetch the artifacts
Idempotency
SendIdempotency-Key: <unique> on POSTs. A retry with the same key + identical body after a
successful (2xx) response replays it (idempotent-replayed: true) instead of stamping twice; a
different body under that key → 409 idempotency.key_reuse. A non-2xx releases the key, so you
can fix the cause and retry with the same key (see
Idempotency and retries for the three errors that stay held).
The PAC’s own duplicate code 307 is also resolved as a success-replay of the prior stamp.
Next
- Cancellation · Webhooks · Going live
- Full field reference:
/openapi.json· error codes:../errors.md