curl --request POST \
--url https://api.newinvoice.dev/v1/invoices/{id}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"reason": "02"
}'import requests
url = "https://api.newinvoice.dev/v1/invoices/{id}/cancel"
payload = { "reason": "02" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reason: '02'})
};
fetch('https://api.newinvoice.dev/v1/invoices/{id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.newinvoice.dev/v1/invoices/{id}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '02'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.newinvoice.dev/v1/invoices/{id}/cancel"
payload := strings.NewReader("{\n \"reason\": \"02\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.newinvoice.dev/v1/invoices/{id}/cancel")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"02\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.newinvoice.dev/v1/invoices/{id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason\": \"02\"\n}"
response = http.request(request)
puts response.read_body{
"object": "invoice",
"id": "obj_2P9K3sample",
"livemode": false,
"status": "draft",
"type": "I",
"fiscal_profile_id": "fp_2P9K3sample",
"serie": "string",
"folio": "string",
"customer": {
"rfc": "XAXX010101000",
"name": "string",
"zip": "string",
"tax_regime": "string",
"use": "string"
},
"currency": "MXN",
"subtotal": "116.00",
"total": "116.00",
"uuid": "5FB2822E-396D-4725-8521-CDC4BDD20CCF",
"stamped_at": "2026-07-10T18:25:43Z",
"sello_cfd": "string",
"sello_sat": "string",
"no_certificado_sat": "string",
"cancellation": {
"status": "none",
"reason": "string",
"substitute_invoice_id": "inv_2P9K3sample",
"acuse_available": true
},
"error": "string",
"xml_url": "https://example.com/webhooks/invoice",
"pdf_url": "https://example.com/webhooks/invoice",
"metadata": {},
"fiscal_profile": {
"object": "fiscal_profile",
"id": "obj_2P9K3sample",
"rfc": "XAXX010101000",
"legal_name": "string",
"tax_regime": "string",
"zip": "string",
"csd": {
"serial": "string",
"valid_from": "string",
"valid_to": "string",
"rfc": "XAXX010101000"
},
"status": "requires_action",
"has_fiel": true,
"manifest_status": "pending_signature",
"manifest_signed_at": "2026-07-10T18:25:43Z",
"next_actions": [
{
"type": "string",
"message": "string",
"endpoint": "string",
"requires": [
"string"
]
}
],
"pending_actions": [
{
"type": "string",
"message": "string",
"endpoint": "string",
"requires": [
"string"
]
}
],
"metadata": {},
"brand_color": "string",
"has_logo": true,
"livemode": false,
"created_at": "2026-07-10T18:25:43Z",
"updated_at": "2026-07-10T18:25:43Z"
},
"created_at": "2026-07-10T18:25:43Z",
"updated_at": "2026-07-10T18:25:43Z"
}{
"type": "https://errors.invoiceapi.mx/auth_unauthorized",
"title": "Authentication required",
"status": 401,
"code": "auth.unauthorized",
"detail": "Missing or invalid API key.",
"remediation": "Send `Authorization: Bearer sk_test_...` (or sk_live_...) with a valid API key.",
"doc_url": "/docs/errors#auth-unauthorized",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/resource_not_found",
"title": "Resource not found",
"status": 404,
"code": "resource.not_found",
"detail": "No such id in this org + mode.",
"remediation": "Verify the id and that the key mode (test/live) matches the id.",
"doc_url": "/docs/errors#resource-not_found",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/invoice_already_stamped",
"title": "Invoice already stamped",
"status": 409,
"code": "invoice.already_stamped",
"detail": "This invoice is already stamped.",
"remediation": "Fetch the stamped invoice; do not re-stamp.",
"doc_url": "/docs/errors#invoice-already_stamped",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/cfdi_invalid_input",
"title": "Invalid invoice",
"status": 422,
"code": "cfdi.invalid_input",
"detail": "items.0.unit_price: expected a decimal string.",
"remediation": "Fix every field listed in `errors` and resubmit. See /openapi.json for the schema.",
"doc_url": "/docs/errors#cfdi-invalid_input",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/rate_limit_exceeded",
"title": "Rate limit exceeded",
"status": 429,
"code": "rate_limit.exceeded",
"detail": "Too many requests for this key + request class (reads and writes are limited separately).",
"remediation": "Back off and retry after the number of seconds in the Retry-After header; batch work or lower your request rate.",
"doc_url": "/docs/errors#rate_limit-exceeded",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/internal_error",
"title": "Internal server error",
"status": 500,
"code": "internal.error",
"detail": "An unexpected error occurred.",
"remediation": "Retry later; contact support with the request_id if it persists.",
"doc_url": "/docs/errors#internal-error",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/pac_internal_error",
"title": "PAC internal error",
"status": 502,
"code": "pac.internal_error",
"detail": "The stamping service reported a transient internal error.",
"remediation": "Retry with backoff; idempotency guards against duplicate stamps.",
"doc_url": "/docs/errors#pac-internal_error",
"request_id": "req_2P9K3sample"
}Cancel an invoice
Request SAT cancellation of a stamped CFDI with a motivo (01-04). Reason “01” requires substitute_invoice_id. Cancellation may be asynchronous — the invoice cancellation.status moves to cancel_pending while the receptor/SAT window resolves; poll or watch webhooks for the final state.
curl --request POST \
--url https://api.newinvoice.dev/v1/invoices/{id}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"reason": "02"
}'import requests
url = "https://api.newinvoice.dev/v1/invoices/{id}/cancel"
payload = { "reason": "02" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reason: '02'})
};
fetch('https://api.newinvoice.dev/v1/invoices/{id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.newinvoice.dev/v1/invoices/{id}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '02'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.newinvoice.dev/v1/invoices/{id}/cancel"
payload := strings.NewReader("{\n \"reason\": \"02\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.newinvoice.dev/v1/invoices/{id}/cancel")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"02\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.newinvoice.dev/v1/invoices/{id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason\": \"02\"\n}"
response = http.request(request)
puts response.read_body{
"object": "invoice",
"id": "obj_2P9K3sample",
"livemode": false,
"status": "draft",
"type": "I",
"fiscal_profile_id": "fp_2P9K3sample",
"serie": "string",
"folio": "string",
"customer": {
"rfc": "XAXX010101000",
"name": "string",
"zip": "string",
"tax_regime": "string",
"use": "string"
},
"currency": "MXN",
"subtotal": "116.00",
"total": "116.00",
"uuid": "5FB2822E-396D-4725-8521-CDC4BDD20CCF",
"stamped_at": "2026-07-10T18:25:43Z",
"sello_cfd": "string",
"sello_sat": "string",
"no_certificado_sat": "string",
"cancellation": {
"status": "none",
"reason": "string",
"substitute_invoice_id": "inv_2P9K3sample",
"acuse_available": true
},
"error": "string",
"xml_url": "https://example.com/webhooks/invoice",
"pdf_url": "https://example.com/webhooks/invoice",
"metadata": {},
"fiscal_profile": {
"object": "fiscal_profile",
"id": "obj_2P9K3sample",
"rfc": "XAXX010101000",
"legal_name": "string",
"tax_regime": "string",
"zip": "string",
"csd": {
"serial": "string",
"valid_from": "string",
"valid_to": "string",
"rfc": "XAXX010101000"
},
"status": "requires_action",
"has_fiel": true,
"manifest_status": "pending_signature",
"manifest_signed_at": "2026-07-10T18:25:43Z",
"next_actions": [
{
"type": "string",
"message": "string",
"endpoint": "string",
"requires": [
"string"
]
}
],
"pending_actions": [
{
"type": "string",
"message": "string",
"endpoint": "string",
"requires": [
"string"
]
}
],
"metadata": {},
"brand_color": "string",
"has_logo": true,
"livemode": false,
"created_at": "2026-07-10T18:25:43Z",
"updated_at": "2026-07-10T18:25:43Z"
},
"created_at": "2026-07-10T18:25:43Z",
"updated_at": "2026-07-10T18:25:43Z"
}{
"type": "https://errors.invoiceapi.mx/auth_unauthorized",
"title": "Authentication required",
"status": 401,
"code": "auth.unauthorized",
"detail": "Missing or invalid API key.",
"remediation": "Send `Authorization: Bearer sk_test_...` (or sk_live_...) with a valid API key.",
"doc_url": "/docs/errors#auth-unauthorized",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/resource_not_found",
"title": "Resource not found",
"status": 404,
"code": "resource.not_found",
"detail": "No such id in this org + mode.",
"remediation": "Verify the id and that the key mode (test/live) matches the id.",
"doc_url": "/docs/errors#resource-not_found",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/invoice_already_stamped",
"title": "Invoice already stamped",
"status": 409,
"code": "invoice.already_stamped",
"detail": "This invoice is already stamped.",
"remediation": "Fetch the stamped invoice; do not re-stamp.",
"doc_url": "/docs/errors#invoice-already_stamped",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/cfdi_invalid_input",
"title": "Invalid invoice",
"status": 422,
"code": "cfdi.invalid_input",
"detail": "items.0.unit_price: expected a decimal string.",
"remediation": "Fix every field listed in `errors` and resubmit. See /openapi.json for the schema.",
"doc_url": "/docs/errors#cfdi-invalid_input",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/rate_limit_exceeded",
"title": "Rate limit exceeded",
"status": 429,
"code": "rate_limit.exceeded",
"detail": "Too many requests for this key + request class (reads and writes are limited separately).",
"remediation": "Back off and retry after the number of seconds in the Retry-After header; batch work or lower your request rate.",
"doc_url": "/docs/errors#rate_limit-exceeded",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/internal_error",
"title": "Internal server error",
"status": 500,
"code": "internal.error",
"detail": "An unexpected error occurred.",
"remediation": "Retry later; contact support with the request_id if it persists.",
"doc_url": "/docs/errors#internal-error",
"request_id": "req_2P9K3sample"
}{
"type": "https://errors.invoiceapi.mx/pac_internal_error",
"title": "PAC internal error",
"status": 502,
"code": "pac.internal_error",
"detail": "The stamping service reported a transient internal error.",
"remediation": "Retry with backoff; idempotency guards against duplicate stamps.",
"doc_url": "/docs/errors#pac-internal_error",
"request_id": "req_2P9K3sample"
}Authorizations
API key: Authorization: Bearer sk_test_... or sk_live_....
Headers
Pin a dated API release (Stripe-style). Omit to use the current version. An unknown value returns 400 request.invalid_version. Echoed back on every response.
"2026-07-10"
Connect: act on behalf of one of your connected accounts (its acct_…/org_… id). Omit to act as your own organization. Not honored on /v1/api_keys.
"org_2P9connectedacct"
Safely retry any POST. The first response is stored for 24h and replayed byte-for-byte for identical retries (the replay adds an idempotent-replayed: true header). Reusing the key with a different body is 409 idempotency.key_reuse; an in-flight duplicate is 409 idempotency.key_processing. This makes retrying a 502/429 safe — no duplicate stamp.
"a1b2c3d4-e5f6-4789-8abc-1234567890ab"
Path Parameters
Invoice id, e.g. "inv_2P9K3sample".
1Body
SAT motivo de cancelación: "01" con errores con relación (needs substitute_invoice_id), "02" con errores sin relación, "03" no se llevó a cabo la operación, "04" operación nominativa relacionada en una factura global.
01, 02, 03, 04 The replacement CFDI id; required when reason is "01" (con errores con relación).
1Response
Default Response
Always "invoice".
invoice Invoice id, e.g. "inv_2P9K3sample".
True if stamped with a live-mode key against the real SAT.
Lifecycle status: draft, stamping (in flight), stamped, or stamp_failed.
draft, stamping, stamped, stamp_failed CFDI TipoDeComprobante: "I" ingreso, "E" egreso (nota de crédito), "P" pago.
I, E, P Id of the fiscal profile (issuer) that stamped this invoice.
CFDI Serie, or null if unset.
CFDI Folio, or null if unset.
The receptor (customer) as stamped onto the CFDI.
Show child attributes
Show child attributes
c_Moneda currency code, e.g. "MXN".
Sum of line amounts before taxes, decimal string, e.g. "100.00".
Grand total including taxes, decimal string, e.g. "116.00".
SAT folio fiscal (UUID) once stamped, else null.
When the CFDI was stamped (ISO-8601 UTC), or null.
Issuer digital seal (Sello del CFDI), or null.
SAT digital seal from the TimbreFiscalDigital, or null.
SAT certificate serial that timbró the CFDI, or null.
Cancellation status for this CFDI.
Show child attributes
Show child attributes
The last stamping error (problem+json shape) when status is stamp_failed, else null.
Path to download the CFDI XML, e.g. "/v1/invoices/:id/xml".
Path to download the CFDI PDF, e.g. "/v1/invoices/:id/pdf".
Integrator-owned key-value map echoed back.
Show child attributes
Show child attributes
Creation timestamp, ISO-8601 UTC.
Last-update timestamp, ISO-8601 UTC.
The full issuer profile — present only when ?expand=fiscal_profile is requested.
Show child attributes
Show child attributes