Start with shared fields, then the Mexico or Colombia guide. The CFDI, RFC, SAT catalog, and payment-complement examples below describe Mexico; they are not universal country requirements.
Overview
The Invoices API provides comprehensive invoicing capabilities with Mexican tax compliance. Create PUE (single payment) or PPD (partial payments) invoices, manage payment complements, and handle cancellations with SAT.Key Features
- CFDI 4.0 Compliance - Full SAT specification support
- Automatic Stamping - Real-time SAT integration
- Payment Complements - PPD invoice management
- Cancellation Support - SAT-compliant cancellation process
- Global Invoices - Monthly global invoice generation
- File Generation - Automatic PDF and XML creation
- Draft Invoices - Prepare, preview, and approve invoices before stamping
- Batches - Up to 1,000 income invoices per request, stamped in the background (Invoice Batches)
- Income-invoice retry protection - Use the documented
idempotency_keyflow on income creation; other operations have their own retry rules - Automation Options - Flexible workflow automation
Endpoints
List CFDI Errors
code(string) - Filter by exact error code (e.g., CFDI140223)q(string) - Search across code, description, explanation, and solutiontype(string) - Filter by error type:invoice,receiver,sender,unknownlimit(integer, 1-100) - Number of results per page (default: 50)page(integer) - Page number for pagination (default: 1)
List Income Invoices
Metadata Filtering:
You can filter invoices by any metadata key using either dot notation or underscore notation. Both formats are equivalent:
page parameter instead of the next cursor.
Example Requests:
Create Income Invoice
payment- Create invoice with payment automationnone- No automation, create invoice only
POST /invoices/income/batch, up to 1,000 per request.
Idempotency and safe retries
Send your own identifier for the invoice, for example your order id, asidempotency_key. gigstack claims the key before it charges a credit or stamps, so sending the same request again can never issue a second CFDI or charge twice. What a request with a used key gets:
The duplicate answer looks like this:
GET /invoices/income?idempotency_key=…. Without an idempotency_key none of this protection applies (see 503).
Get Income Invoice
Create Egress Invoice
payment_method is optional and defaults to PUE, which is how every egress invoice was
stamped before the field was accepted. Set it to PPD when the credit note applies to a
partial/deferred-payment invoice. With PPD, SAT requires payment_form to be 99
(“Por definir”); any other value you send is overridden to 99 rather than rejected.
A PPD egress invoice is not a valid target for a payment complement — ppd_invoice_id
on POST /payments only accepts income invoices.
List Egress Invoices
Metadata Filtering:
You can filter egress invoices by any metadata key using either dot notation or underscore notation. Both formats are equivalent:
page parameter instead of the next cursor.
Example Requests:
Get Invoice Files
file_type(string) - Type of file: “pdf”, “xml” (optional, returns both if not specified)team(string) - gigstack Connect: Target team ID
Cancel Invoice
Use the motive that represents the actual transaction. For motive
01, first issue
and retain the correct replacement and its relationship to the original; then cancel
the original using the replacement’s UUID. The motive catalog
explains the codes.
Use the environment and key from the quickstart. Set INVOICE_UUID to
the reviewed invoice in that team and mode. The following is a source-checked
request example, not a claim that cancellation was live-tested:
01, the body is {"motive":"01","substitution_uuid":"REPLACEMENT_UUID"}.
The endpoint returns the provider result directly, rather than a standard data
envelope. HTTP 200 acknowledges a result, not necessarily final cancellation.
Afterward, retrieve GET /invoices/{id} again. The current Mexican flow may store
cancellation_status: "pending" and keep status: "valid" if its SAT status check
does not agree with the provider’s answer, even when the immediate response says
accepted. Report cancellation as final only after reconciling the stored status
and the provider/SAT outcome. Preserve the cancellation receipt when available.
There is no documented cancellation idempotency key. Do not create a replacement or
repeat cancellation merely because the first response was lost. See error handling
for provider errors.
Create Payment Complement (Complemento de Pago)
complements[].data is one payment, and each payment lists the PPD invoices it pays in related_documents.
The SAT fixes some values, so you do not send them: the comprobante currency (XXX), the receptor’s UsoCFDI (CP01) and the line concept. The series defaults to the team’s payments series.
Request Body:
Each payment (
complements[].data[]):
Each related document:
uuid (the PPD invoice’s folio fiscal), amount (paid now), installment (1 for the first payment against that invoice), last_balance (balance before this payment) and currency; optionally exchange, series, folio_number and taxes.
Example Request:
200 with { "message": "Payment complement created", "data": { …invoice } }. This endpoint returns a raw body, not the standardized envelope. A body that fails validation returns 400 with { "message": "Invalid body", "errors": [ … ] }.
List and read complements with GET /invoices/payment and GET /invoices/payment/{id}, which work like the income-invoice list and get.
Create Transfer Invoice (Traslado with Carta Porte)
XXX, Total 0, UsoCFDI S01, no payment method. There are no items; the CFDI concepts are built from carta_porte.Mercancias.Mercancia. The series defaults to T.
Only teams that stamp through gigstack’s own PAC (CSD uploaded in gigstack) can use this endpoint.
Request Body:
carta_porte (numbers can be numbers or numeric strings):
Example:
400 lists the fields that failed, including the Carta Porte rules (for example carta_porte.Ubicaciones[1].DistanciaRecorrida is required on a Destino). PAC rejections come back with the PAC’s message.
Search Invoices
data plus found (total matches), page and per_page; there is no cursor. Search must be enabled for your team — otherwise the call returns 400 with error.code: missing_typesense_key. A missing q returns 400 missing_query.
Support Documents
/clients/{id}/support-documents) and payments (/payments/{id}/support-documents).
Upload as multipart/form-data:
201 with the stored document in data (id, document_type, file_url, compliance_status: "pending_review", …) in the standardized envelope. file_url is a Firebase download-token URL — it does not expire, but treat it as opaque and re-read the document instead of building the link yourself. GET lists the invoice’s documents, newest first. An unknown invoice returns 404. To review, link or analyze documents independently of one invoice, use the Documents API.
End-of-Month Global Invoicing
- A live API key. Test keys get
403(error.code: operation_not_allowed). - It must be the last calendar day of the month in
America/Mexico_City. Any other day returns400(operation_not_allowed), with today’s date and the next eligible date inerror.details. - It must be before 23:00 in
America/Mexico_City. From 23:00 the automatic end-of-month run takes over, and manual calls return400(operation_not_allowed).
502 (external_service_error) and nothing is invoiced.
Draft Invoices (Pre-Facturas)
Draft invoices allow you to prepare and review an invoice before stamping it with SAT. This is useful for approval workflows, client review, or building invoices incrementally over time. A draft follows this lifecycle:- Create a draft with partial or complete data.
- Update the draft as needed (add items, set client, change payment method).
- Preview the draft to generate a PDF with a “Sin Validez Fiscal” watermark.
- Stamp the draft to finalize it into a real CFDI invoice.
invoices collection with a draft: true flag and do not consume credits until stamped.
Create Draft
invoice_type is required at creation; all other fields can be added later via update.
Request Body:
List Drafts
Example Request:
Get Draft
Update Draft
items in the staging verification. Do not treat this operation as
a partial PATCH.
Keep your original request JSON in your application. Using draft-body.json from
the invoice recipe, create a complete revised request:
Delete Draft
Stamp Draft (Finalize)
Check the stored draft mode before stamping. A test key alone does not convert a live draft to test mode. Use a draft created with your test key for the tutorial.clientwith valid fiscal data- At least one item
use(CFDI use code)payment_formpayment_methodcurrency
send_email(boolean) - Set tofalseto suppress email delivery. Default:true.return_files(boolean) - Set totrueto include base64-encoded XML and PDF in the response.
Preview Draft
PREFACTURA-0000-0000-0000-SINVALIDEZ). The preview is saved to the draft’s files subcollection.
The draft must have at least a client and one item.
Example Request:
Draft Workflow Example
Follow Draft, preview, and issue for one complete sequence with saved request files, captured IDs, a decoded preview, and retrieval of the issued invoice. For an approval workflow:- Save the intended request JSON and create the draft.
- If anything changes, send the full revised body as described in Update Draft.
- Fetch and compare the draft, then generate and review a fresh preview.
- Verify the stored draft’s
livemodeand issue only the reviewed draft. - Save
data.uuidfrom the stamping response and retrieve the issued invoice with that UUID. The former draft ID returned404after stamping in the verified flow.
Invoice Structure
Invoice Types
- I - Ingreso (Income)
- E - Egreso (Expense / Credit note)
- P - Pago (Payment complement)
- T - Traslado (Transfer of goods, with Carta Porte)
- N - Nomina (Payroll)
Payment Methods
- PUE - Pago en Una sola Exhibicion (Single payment)
- PPD - Pago en Parcialidades o Diferido (Partial or deferred payment)
Payment Forms (Formas de Pago)
Complex Invoice Examples
Invoice with Multiple Items and Taxes
Invoice with Withholding Taxes
PPD Invoice (Partial Payments)
Global Invoice
XAXX010101000, regime 616, use S01, your own postal code). See Global Invoices for the periodicity and month codes.
Related Documents
Creating Related Invoices
01- Nota de credito de los documentos relacionados02- Nota de debito de los documentos relacionados03- Devolucion de mercancia sobre facturas o traslados previos04- Sustitucion de los CFDI previos07- CFDI por aplicacion de anticipo
Common Scenarios
1. Simple Sale Invoice
2. Service Invoice with Email
3. USD Invoice with Exchange Rate
Best Practices
- Use idempotency keys - Prevent duplicate invoices by including unique identifiers.
- Validate clients first - Ensure the client’s fiscal data (RFC, tax system, address) is correct before creating an invoice.
- Configure email settings - Set up BCC addresses for your accounting department.
- Use correct payment methods - Use PUE for immediate single payments and PPD for partial or deferred payments.
- Include all required taxes - Apply IVA, ISR, and IEPS as applicable to each line item.
- Set proper CFDI use - Match the CFDI use code to the client’s tax requirements.
- Keep series organized - Use different series for different invoice types or business units.
- Use drafts for review - For high-value invoices, use the draft workflow to generate a preview before stamping.
Related Resources
- CFDI Errors Reference - Comprehensive error code catalog
- Clients API - Manage invoice recipients
- Services API - Configure invoice items
- Payments API - Process invoice payments
- Teams API - Configure invoice settings
Error Handling
When working with invoices, you may encounter various CFDI-specific error codes. Use the CFDI Errors endpoint to look up detailed explanations and solutions for any error codes you receive.At a glance
Two error shapes
CFDI failures use a dedicated shape:code— the SAT/PAC error code, or one of gigstack’s own (SAT_NOT_CONNECTED,PAC_UNAVAILABLE,PAC_OUTCOME_UNKNOWN,STAMP_NEEDS_REVIEW,CSD_VALIDATION_ERROR,STAMPING_ERROR, …).providerMessage— present only on400stamping rejections. It is the PAC’s verbatim text; log it, it names the offending field and value.retryable—truefor503PAC_UNAVAILABLE, and for503PAC_OUTCOME_UNKNOWNonly when the request carried anidempotency_key. Treat it as authoritative: nothing else is worth retrying unchanged.duplicateanduuid— only on the400for anidempotency_keywhose invoice already exists (see Idempotency and safe retries).- The trailing
[pcs_…]is the process-log id. Include it in any support request.
Shape gotcha: on the create endpoints (Everything else uses the standard envelope:POST /invoices/income,/egress,/payment,/draft/{id}/stamp) this object arrives nested undermessage—{"message": {"error": …, "code": …}}. OnDELETE /invoices/{id}(cancel) it arrives at the top level. Parse defensively:body.message?.code ?? body.code.
412 — SAT not connected
code: "CSD_VALIDATION_ERROR"). This is a precondition on the team, not a problem with your payload; the request never reached the PAC.
Do: stop retrying. Upload/refresh the CSD via POST /v2/teams/{id}/sat-connection, or send the team owner to the integrations page. Under Connect, check that ?team= points at a team that has actually finished onboarding — this is the usual cause when one connected team works and another doesn’t.
503 — PAC unavailable or outcome unknown
A503 from a create endpoint carries one of two codes. They mean different things, so read code.
PAC_UNAVAILABLE
idempotency_key. Don’t show a validation error to your end user; nothing they entered is at fault.
PAC_OUTCOME_UNKNOWN
PAC_UNAVAILABLE; they are now this code.
- With an
idempotency_key(retryable: true): retry with the same key. gigstack resends the exact same XML with the same folio, so the PAC either stamps it now or returns the stamp it already made. It can’t stamp twice, and the retry isn’t charged again. If the PAC can’t tell, the retry answers409STAMP_NEEDS_REVIEW. - Without one (
retryable: false): a new request would take a new folio and could issue a second CFDI. Check whether the invoice exists (inGET /invoices/income, or in the SAT) before sending it again. Sending anidempotency_keyon every create avoids this.
409 — Stamp needs review
idempotency_key reached the PAC, and gigstack could not prove whether it was stamped: the PAC said it already stamped the document but returned no UUID, the SAT’s 72-hour window for the document had passed, or the invoice was stamped but couldn’t be saved. Every later request with the key gets this answer, so the invoice can’t be issued twice.
Do: contact support with the idempotency_key and the [pcs_…] id. Don’t send the invoice again under a new key.
429 — Team credit limit reached
creditLimit on issued documents and has consumed it. The counter increments before stamping, so this is checked and rejected up front — no folio is consumed.
Note the shape: credit_limit and used_credits are returned at the top level so you can display the exact quota. POST /v2/receipts reports the same condition in the standard envelope with error.code = "team_credit_limit_reached".
Do: stop and raise the limit (credit_limit on PUT /v2/teams/{id}) or contact gigstack. Backing off does not help — the counter only moves when the limit is raised.
422 — Imported invoice cannot be cancelled
DELETE /invoices/{id} when the invoice arrived via import (source: "import") and carries no gigstack stamp. gigstack holds no credentials for the PAC that issued it, so it cannot cancel it on your behalf.
Do: cancel it in the system that issued it. There is no request you can change to make this succeed. POST /invoices/sat/{uuid}/retry-xml also uses 422 for a failed XML fetch from SAT, with data.resource_status: "error" — that one is worth retrying later.
502 — Upstream service error
POST /invoices/sat/{uuid}/pdf. Your invoice data is fine and, for PDF generation, the CFDI itself is untouched.
Do: retry with backoff. If it persists, the XML is still available through GET /invoices/{id}/files.
409 — Concurrent resource creation
client.search.auto_create / item search.auto_create) at the same time. One won; yours was told to wait rather than create a duplicate.
Do: retry once after a short delay — the resource will exist by then. POST /v2/clients also returns 409 for a different reason: a search that matched more than one client. That one is not retryable; narrow the search or pass the client id.
409 — Idempotency key in progress
POST /invoices/income with the same idempotency_key is being processed right now, for example a retry sent while the first request was still waiting for the PAC. Nothing was charged or stamped by this request.
Do: wait a few seconds and retry with the same key. Once the first request finishes you get its outcome, for example the 400 duplicate with the invoice’s uuid. If the first request died mid-way, the key stays held for up to 10 minutes.
Missing Required Fields (400)
Cancellation Error
Note the flat shape here —DELETE /invoices/{id} does not nest the CFDI error under message:
providerMessage to identify the actual cause, such as a dependent document or a recipient-acceptance requirement. Do not interpret an error as a universal cancellation deadline. Resolve the stated cause and reconcile the invoice status before repeating the request.
For additional help with invoice management, refer to the support documentation or contact support@gigstack.io.