Si estás integrando Ipsofactura en tu sistema, lo primero que necesitas es probar el flujo completo sin emitir facturas fiscalmente válidas ni consumir timbres. Para eso existe el sandbox.
Esta guía te lleva de cero a tu primera factura timbrada, cancelada y descargada — todo en el ambiente de pruebas.
¿Qué es el sandbox?
El sandbox de Ipsofactura es un ambiente completamente aislado de producción. Tiene su propia base de datos, se conecta a los ambientes de prueba de los PACs y no consume timbres de tu saldo real.
Lo que sí funciona igual que en producción:
- El flujo completo de timbrado y cancelación
- La descarga de XML y PDF
- La validación de estructura del CFDI
- Los errores y códigos de respuesta
Lo que no aplica en sandbox:
- Los CFDIs timbrados no son válidos ante el SAT
- No necesitas un CSD real: puedes usar el CSD de prueba oficial del SAT
Lo que necesitas antes de empezar
- Una cuenta en console.ipsofactura.com
- Una API key de sandbox, que crearemos en el paso 2
curl, Python, Node.js o cualquier cliente HTTP
Paso 1 — Accede a la consola
Entra a console.ipsofactura.com. El acceso es por magic link: ingresa tu email y recibirás un enlace directo en tu correo.

Una vez autenticado llegas al dashboard principal.

Paso 2 — Crea una API key de sandbox
En el menú lateral, ve a API Keys y asegúrate de estar en el tab Sandbox.

Haz clic en Nueva API key, asígnale un nombre descriptivo, por ejemplo mi-integracion-dev, y haz clic en Crear API key.

La key se muestra una sola vez. Cópiala y guárdala en un lugar seguro, como un gestor de contraseñas o una variable de entorno. No la pongas directamente en tu código.

ifk_test_xxxxxxxxxxxxxxxxxxxx. Nunca la expongas en repositorios públicos ni en código frontend.Después de confirmar que guardaste la key, volverás a verla en la lista con su valor oculto.

Paso 3 — Crea tu empresa emisora
Antes de timbrar necesitas registrar el RFC emisor. Usaremos EKU9003173C9, el RFC de prueba oficial del SAT que los PACs reconocen en sus ambientes de test.
curl -X POST https://sandbox.api.ipsofactura.com/empresas \
-H "x-api: TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rfc": "EKU9003173C9",
"nombre_fiscal": "ESCUELA KEMPER URGATE",
"nombre_empresa": "Escuela Kemper Urgate",
"regimen_fiscal": "601",
"codigo_postal": "06000"
}'Python
import requests
response = requests.post(
"https://sandbox.api.ipsofactura.com/empresas",
headers={"x-api": "TU_API_KEY"},
json={
"rfc": "EKU9003173C9",
"nombre_fiscal": "ESCUELA KEMPER URGATE",
"nombre_empresa": "Escuela Kemper Urgate",
"regimen_fiscal": "601",
"codigo_postal": "06000"
}
)
print(response.json())JavaScript
const response = await fetch("https://sandbox.api.ipsofactura.com/empresas", {
method: "POST",
headers: {
"x-api": "TU_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
rfc: "EKU9003173C9",
nombre_fiscal: "ESCUELA KEMPER URGATE",
nombre_empresa: "Escuela Kemper Urgate",
regimen_fiscal: "601",
codigo_postal: "06000"
})
});
console.log(await response.json());Respuesta esperada (201 Created):
{
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"rfc": "EKU9003173C9",
"nombre_empresa": "Escuela Kemper Urgate",
"nombre_fiscal": "ESCUELA KEMPER URGATE",
"regimen_fiscal": "601",
"codigo_postal": "06000",
"es_activo": true
}Guarda el id: lo necesitarás para el siguiente paso.
409 EMPRESA_ALREADY_EXISTS es porque ya registraste este RFC. Puedes consultarlo con GET /empresas.Paso 4 — Sube un CSD de prueba
El CSD (Certificado de Sello Digital) es el certificado criptográfico con el que se firma cada CFDI. Para sandbox necesitas el CSD oficial de prueba del SAT para el RFC EKU9003173C9.
Descarga el CSD de prueba del SAT: obtén los archivos .cer y .key del RFC EKU9003173C9 desde el portal del SAT, en Pruebas → Genera CSD de prueba, o usa los que ya tengas del SAT. La contraseña estándar de este CSD de prueba es 12345678a.
Convierte los archivos a Base64:
# En macOS/Linux
CER_B64=$(base64 -i CSD_EKU9003173C9.cer)
KEY_B64=$(base64 -i CSD_EKU9003173C9.key)import base64
with open("CSD_EKU9003173C9.cer", "rb") as f:
cer_b64 = base64.b64encode(f.read()).decode()
with open("CSD_EKU9003173C9.key", "rb") as f:
key_b64 = base64.b64encode(f.read()).decode()Luego súbelo:
curl -X POST https://sandbox.api.ipsofactura.com/empresas/{EMPRESA_ID}/certificados \
-H "x-api: TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"certificado_cer_base64": "'"$CER_B64"'",
"llave_key_base64": "'"$KEY_B64"'",
"password_key": "12345678a",
"numero_certificado": "20001000000300022323",
"fecha_fin": "2027-05-18T00:00:00"
}'import requests, base64
with open("CSD_EKU9003173C9.cer", "rb") as f:
cer_b64 = base64.b64encode(f.read()).decode()
with open("CSD_EKU9003173C9.key", "rb") as f:
key_b64 = base64.b64encode(f.read()).decode()
response = requests.post(
f"https://sandbox.api.ipsofactura.com/empresas/{EMPRESA_ID}/certificados",
headers={"x-api": "TU_API_KEY"},
json={
"certificado_cer_base64": cer_b64,
"llave_key_base64": key_b64,
"password_key": "12345678a",
"numero_certificado": "20001000000300022323",
"fecha_fin": "2027-05-18T00:00:00"
}
)
print(response.json())fecha_fin requiere formato datetime (2027-05-18T00:00:00), no solo la fecha.Respuesta esperada (201 Created):
{
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"empresa_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"numero_certificado": "30001000000500003416",
"estado": "VALIDO",
"fecha_fin": "2027-05-18",
"es_activo": true
}El API extrae el número de certificado directamente del archivo .cer. El numero_certificado que envíes en el body es ignorado; cuenta el que viene en el archivo.
Paso 5 — Timbra tu primera factura
Con la empresa y el CSD registrados ya puedes timbrar. El siguiente ejemplo es una factura de tipo Ingreso (I) con pago en una sola exhibición (PUE).
curl -X POST https://sandbox.api.ipsofactura.com/cfdi/timbrar \
-H "x-api: TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rfc_emisor": "EKU9003173C9",
"tipo_comprobante": "I",
"forma_pago": "03",
"metodo_pago": "PUE",
"moneda": "MXN",
"exportacion": "01",
"subtotal": 1000.00,
"total": 1160.00,
"receptor": {
"rfc": "URE180429TM6",
"nombre": "UNIVERSIDAD ROBOTICA ESPANOLA",
"domicilio_fiscal": "65000",
"regimen_fiscal": "601",
"uso_cfdi": "G03"
},
"conceptos": [{
"clave_prod_serv": "84111506",
"cantidad": 1,
"clave_unidad": "ACT",
"descripcion": "Servicio de desarrollo de software",
"valor_unitario": 1000.00,
"importe": 1000.00,
"objeto_imp": "02",
"impuestos": {
"traslados": [{
"base": 1000.00,
"impuesto": "002",
"tipo_factor": "Tasa",
"tasa_o_cuota": "0.160000",
"importe": 160.00
}]
}
}],
"impuestos": {
"total_impuestos_trasladados": 160.00,
"traslados": [{
"base": 1000.00,
"impuesto": "002",
"tipo_factor": "Tasa",
"tasa_o_cuota": "0.160000",
"importe": 160.00
}]
}
}'Python
import requests
payload = {
"rfc_emisor": "EKU9003173C9",
"tipo_comprobante": "I",
"forma_pago": "03",
"metodo_pago": "PUE",
"moneda": "MXN",
"exportacion": "01",
"subtotal": 1000.00,
"total": 1160.00,
"receptor": {
"rfc": "URE180429TM6",
"nombre": "UNIVERSIDAD ROBOTICA ESPANOLA",
"domicilio_fiscal": "65000",
"regimen_fiscal": "601",
"uso_cfdi": "G03"
},
"conceptos": [{
"clave_prod_serv": "84111506",
"cantidad": 1,
"clave_unidad": "ACT",
"descripcion": "Servicio de desarrollo de software",
"valor_unitario": 1000.00,
"importe": 1000.00,
"objeto_imp": "02",
"impuestos": {"traslados": [{
"base": 1000.00,
"impuesto": "002",
"tipo_factor": "Tasa",
"tasa_o_cuota": "0.160000",
"importe": 160.00
}]}
}],
"impuestos": {
"total_impuestos_trasladados": 160.00,
"traslados": [{
"base": 1000.00,
"impuesto": "002",
"tipo_factor": "Tasa",
"tasa_o_cuota": "0.160000",
"importe": 160.00
}]
}
}
response = requests.post(
"https://sandbox.api.ipsofactura.com/cfdi/timbrar",
headers={"x-api": "TU_API_KEY"},
json=payload
)
print(response.json())JavaScript
const response = await fetch("https://sandbox.api.ipsofactura.com/cfdi/timbrar", {
method: "POST",
headers: {
"x-api": "TU_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
rfc_emisor: "EKU9003173C9",
tipo_comprobante: "I",
forma_pago: "03",
metodo_pago: "PUE",
moneda: "MXN",
exportacion: "01",
subtotal: 1000.00,
total: 1160.00,
receptor: {
rfc: "URE180429TM6",
nombre: "UNIVERSIDAD ROBOTICA ESPANOLA",
domicilio_fiscal: "65000",
regimen_fiscal: "601",
uso_cfdi: "G03"
},
conceptos: [{
clave_prod_serv: "84111506",
cantidad: 1,
clave_unidad: "ACT",
descripcion: "Servicio de desarrollo de software",
valor_unitario: 1000.00,
importe: 1000.00,
objeto_imp: "02",
impuestos: { traslados: [{
base: 1000.00,
impuesto: "002",
tipo_factor: "Tasa",
tasa_o_cuota: "0.160000",
importe: 160.00
}]}
}],
impuestos: {
total_impuestos_trasladados: 160.00,
traslados: [{
base: 1000.00,
impuesto: "002",
tipo_factor: "Tasa",
tasa_o_cuota: "0.160000",
importe: 160.00
}]
}
})
});
console.log(await response.json());Respuesta esperada (201 Created):
{
"id": "52e9959d-3fbf-4580-988a-39ca1138dd52",
"uuid": "7b72fe22-7b95-48cd-bd64-63c8d1925555",
"folio": "1",
"fecha_timbrado": "2026-07-28T14:35:18",
"rfc_pac": "AAA010101AAA",
"pac_provider": "SandboxPacHandler",
"numero_certificado_sat": "30001000000500003416",
"sello_cfdi": "SANDBOX_sello_cfdi_...",
"sello_sat": "..."
}Guarda el campo id (ID interno): lo necesitas para consultar el estado, descargar archivos y cancelar.
Puedes ver el CFDI recién creado en la consola:

Paso 6 — Consulta el estado y descarga el XML/PDF
Consultar estado
curl https://sandbox.api.ipsofactura.com/cfdi/{CFDI_ID}/estatus \
-H "x-api: TU_API_KEY"response = requests.get(
f"https://sandbox.api.ipsofactura.com/cfdi/{CFDI_ID}/estatus",
headers={"x-api": "TU_API_KEY"}
)
print(response.json())
# {"id": "...", "estado": "vigente"}Descargar XML
curl https://sandbox.api.ipsofactura.com/cfdi/{CFDI_ID}/xml \
-H "x-api: TU_API_KEY"Respuesta:
{
"id": "52e9959d-3fbf-4580-988a-39ca1138dd52",
"xml_url": "https://sandbox.api.ipsofactura.com/sandbox/files/..."
}Descargar PDF
curl https://sandbox.api.ipsofactura.com/cfdi/{CFDI_ID}/pdf \
-H "x-api: TU_API_KEY"Respuesta:
{
"id": "52e9959d-3fbf-4580-988a-39ca1138dd52",
"pdf_url": "https://sandbox.api.ipsofactura.com/sandbox/files/..."
}Paso 7 — Cancela la factura
La cancelación en México requiere especificar un motivo. Los más comunes para pruebas son:
| Motivo | Descripción |
|---|---|
01 | Comprobante emitido con errores con relación |
02 | Comprobante emitido con errores sin relación |
03 | No se llevó a cabo la operación |
04 | Operación nominativa relacionada en una factura global |
curl -X POST https://sandbox.api.ipsofactura.com/cfdi/cancelar \
-H "x-api: TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "{CFDI_ID}",
"rfc_emisor": "EKU9003173C9",
"motivo": "03"
}'Python
response = requests.post(
"https://sandbox.api.ipsofactura.com/cfdi/cancelar",
headers={"x-api": "TU_API_KEY"},
json={
"id": CFDI_ID,
"rfc_emisor": "EKU9003173C9",
"motivo": "03"
}
)
print(response.json())JavaScript
const response = await fetch("https://sandbox.api.ipsofactura.com/cfdi/cancelar", {
method: "POST",
headers: {
"x-api": "TU_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
id: CFDI_ID,
rfc_emisor: "EKU9003173C9",
motivo: "03"
})
});
console.log(await response.json());Respuesta esperada (200 OK):
{
"id": "7b72fe22-7b95-48cd-bd64-63c8d1925555",
"estatus": "cancelado",
"fecha_cancelacion": "2026-07-28T14:35:19",
"acuse": "<Acuse>SANDBOX</Acuse>",
"estatus_cancelacion": "201"
}id en el body es el ID interno que devuelve /cfdi/timbrar, no el UUID del SAT.Paso 8 — Revisa los logs en la consola
La consola tiene una sección de API Logs donde puedes ver todas las requests, el status code de cada una y el payload de respuesta. Es muy útil para depurar tu integración.

También puedes ver tus emisores registrados:

Referencia rápida: endpoints del sandbox
| Método | Endpoint | Descripción |
|---|---|---|
GET | /empresas | Listar empresas registradas |
POST | /empresas | Crear empresa emisora |
POST | /empresas/{id}/certificados | Subir CSD |
DELETE | /empresas/{id}/certificados/{num} | Eliminar CSD |
GET | /cfdi | Listar CFDIs (paginado) |
POST | /cfdi/timbrar | Timbrar un CFDI |
GET | /cfdi/{id}/estatus | Consultar estado del CFDI |
GET | /cfdi/{id}/xml | Descargar XML |
GET | /cfdi/{id}/pdf | Descargar PDF |
POST | /cfdi/cancelar | Cancelar un CFDI |
POST | /cfdi/complemento-pago | Emitir complemento de pago |
https://sandbox.api.ipsofactura.comAutenticación: header
x-api: TU_API_KEYBuenas prácticas al integrar
Nunca pongas tu API key en el código. Usa variables de entorno:
export IPSO_API_KEY="ifk_test_xxxxxxxxxxxx"import os
api_key = os.environ["IPSO_API_KEY"]const apiKey = process.env.IPSO_API_KEY;Maneja los errores por código. Ipsofactura retorna errores estructurados:
{
"code": "CERTIFICATE_NOT_FOUND",
"message": "No se encontró un certificado activo para este RFC"
}Los códigos más comunes que verás al probar:
| Código | Cuándo ocurre |
|---|---|
CERTIFICATE_NOT_FOUND | Intentas timbrar sin CSD registrado |
EMPRESA_ALREADY_EXISTS | El RFC ya está registrado para tu cuenta |
DUPLICATE_CERTIFICATE | El número de certificado ya está registrado |
INVALID_API_KEY | La API key es incorrecta o fue revocada |
VALIDATION_ERROR | La estructura del CFDI es inválida |
¿Listo para producción?
Cuando tu integración funcione bien en sandbox, el cambio a producción es un one-liner: reemplaza la base URL.
| Sandbox | Producción | |
|---|---|---|
| Base URL | https://sandbox.api.ipsofactura.com | https://api.ipsofactura.com |
| API key | ifk_test_… | ifk_live_… |
| CSD | De prueba del SAT | Tu CSD real y vigente |
| Timbres consumidos | No | Sí |
| Validez fiscal | No | Sí |
Genera una API key de producción desde la misma consola, sube tu CSD real y apunta tus requests a api.ipsofactura.com.