{"openapi":"3.1.0","info":{"title":"API de MrFactura","version":"1.0.0","description":"La API de MrFactura te permite emitir y administrar CFDI 4.0 desde tu propio sistema, para una o\nmuchas empresas, con webhooks en tiempo real y un ambiente **sandbox** para probar sin validez fiscal.\n\n**URL base:** `https://admin.mrfactura.com/api/v1`\n\n## Autenticación\n\nCada empresa genera sus llaves en **Configuración → Integraciones → API para desarrolladores**.\nEnvía la llave en el encabezado `Authorization`:\n\n```http\nAuthorization: Bearer mrf_test_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n```\n\n| Prefijo | Modo | Qué hace |\n|---|---|---|\n| `mrf_test_` | Sandbox | Timbra en el ambiente de pruebas del PAC. Sin validez fiscal, no consume timbres. |\n| `mrf_live_` | Producción | Emite CFDI reales ante el SAT. Requiere el **plan Lord pagado y al corriente**. |\n\n- La llave se muestra **una sola vez** al crearla; guárdala en un gestor de secretos.\n- Cada llave pertenece a **una empresa** y tiene permisos (*scopes*): `invoices:read`, `invoices:write`, `invoices:cancel`, `customers:read`, `customers:write`.\n- Puedes revocarla en cualquier momento desde el panel.\n- El **sandbox** está disponible con cualquier plan. Las llaves de **producción** requieren el plan Lord pagado y al corriente (no aplica en periodo de prueba);\n  si el plan cambia, las llaves `mrf_live_` responden `403 plan_required` (las de prueba siguen funcionando).\n\n### Multiempresa\n\nCada empresa conectada tiene sus propias llaves, datos, certificados y permisos, aislados entre sí.\nPara operar varias empresas desde tu plataforma guarda una llave por empresa: la empresa sobre la\nque actúa cada petición **siempre** es la de la llave, nunca un parámetro del cuerpo.\n\n## Sandbox\n\nCon una llave `mrf_test_` las facturas se timbran de verdad en el ambiente de pruebas del PAC y\nreciben UUID, XML y PDF, pero:\n\n- **No tienen validez fiscal** y no aparecen en tus facturas, reportes ni folios reales.\n- El **emisor del XML** es el contribuyente de pruebas del SAT `EWE1709045U0`\n  (ESCUELA WILSON ESQUIVEL, régimen 601). El PDF muestra los datos de tu empresa con la marca\n  *SIN VALIDEZ FISCAL*.\n- El SAT de pruebas solo acepta **receptores de su lista de pruebas**. Receptor verificado:\n\n| RFC | Nombre | Régimen | C.P. |\n|---|---|---|---|\n| `MASO451221PM4` | MARIA OLIVIA MARTINEZ SAGAZ | 612 | 80290 |\n\n- La cancelación se simula (el ambiente de pruebas no emite acuses del SAT).\n- Los webhooks se envían solo al **endpoint de pruebas** con `\"livemode\": false`.\n\n## Producción\n\nCon una llave `mrf_live_` las facturas se emiten ante el SAT con el **RFC y el CSD de tu empresa**,\nconsumen timbres de tu cuenta y aparecen en tu panel de MrFactura como cualquier otra factura (mismo\nfolio consecutivo, mismo correo automático si el cliente lo tiene activado). Antes de emitir verificamos\nplan, timbres disponibles y CSD vigente.\n\n- **Una operación a la vez por empresa:** las emisiones y cancelaciones de una misma empresa se atienden\n  en orden; si otra sigue en curso por más de 25 s respondemos `409 concurrent_request` (reintenta con la\n  misma `Idempotency-Key`).\n- **Resultado incierto:** si el PAC no confirma a tiempo respondemos `502 stamp_outcome_unknown` y\n  **no hay que reintentar**: el CFDI pudo haberse timbrado. Consulta `GET /invoices?uuid=…` o contacta a\n  soporte con el `request_id`. Repetir con la misma `Idempotency-Key` devuelve la misma respuesta.\n- **Cancelación:** se solicita al SAT. Si el receptor debe aceptarla, la factura queda con\n  `cancellation.status = \"pending\"` hasta que el SAT la confirme.\n\n## Errores\n\nTodas las respuestas de error tienen el mismo formato y un código estable (`code`) para tu lógica:\n\n```json\n{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"parameter_invalid\",\n    \"message\": \"RFC con formato inválido.\",\n    \"param\": \"customer.rfc\",\n    \"request_id\": \"req_4f1c…\"\n  }\n}\n```\n\n| HTTP | `type` | Cuándo |\n|---|---|---|\n| 400 | `invalid_request_error` | Parámetros faltantes o inválidos. |\n| 401 | `authentication_error` | Llave ausente, inválida o revocada. |\n| 403 | `permission_error` | La llave no tiene el permiso o el modo no está habilitado. |\n| 404 | `not_found_error` | El recurso no existe **en esta empresa**. |\n| 409 | `idempotency_error` | Una petición con la misma `Idempotency-Key` sigue en curso. |\n| 402 | `invalid_request_error` | `insufficient_timbres`: sin timbres disponibles (producción). |\n| 422 | `fiscal_error` | El SAT/PAC rechazó el comprobante (`sat_validation_failed`, `details.sat_code` p. ej. `CFDI40147`), o `csd_missing` / `csd_expired`. |\n| 429 | `rate_limit_error` | Límite de peticiones alcanzado. |\n| 502 | `pac_error` | `stamp_outcome_unknown`: el PAC no confirmó; **no reintentes** (ver Producción). |\n| 5xx | `api_error` / `pac_error` | Error temporal; reintenta con la misma `Idempotency-Key`. |\n\nIncluye el `request_id` (también en el encabezado `X-Request-Id`) cuando contactes a soporte.\n\n## Límites de uso\n\n| Límite | Valor |\n|---|---|\n| General | 100 peticiones por minuto por empresa y modo |\n| Emisión y cancelación | 60 por minuto por empresa y modo |\n\nEl sandbox y producción tienen presupuestos separados. Cada respuesta incluye `RateLimit-Limit`,\n`RateLimit-Remaining` y `RateLimit-Reset`; al exceder el límite responde `429` con\n`Retry-After` (segundos). Los webhooks no cuentan contra el límite. Para volúmenes mayores\nescríbenos y ampliamos los límites de tu cuenta.\n\n## Idempotencia\n\n`POST /invoices` y `POST /invoices/{id}/cancel` **requieren** el encabezado `Idempotency-Key`\n(un valor único por operación, p. ej. un UUID v4). Si repites la petición con la misma llave y el\nmismo cuerpo recibes la misma respuesta (con `Idempotent-Replayed: true`) **sin timbrar dos veces**.\nLa misma llave con otro cuerpo responde `422 idempotency_key_reused`. Las llaves se conservan 30 días.\nEs opcional en `POST /customers`.\n\n## Paginación\n\nLos listados devuelven `{ \"object\": \"list\", \"data\": [...], \"has_more\": true }` ordenados del más\nreciente al más antiguo. Usa `limit` (1–100, por defecto 20) y `starting_after=<id del último elemento>`\npara pedir la siguiente página.\n\n## Webhooks\n\nConfigura la URL (HTTPS) en **Integraciones → API para desarrolladores** (una de pruebas y otra de\nproducción). Enviamos un `POST` JSON por evento:\n\n| Evento | Cuándo |\n|---|---|\n| `invoice.created` | Se timbró una factura. |\n| `invoice.cancelled` | Se canceló una factura. |\n| `ping` | Botón \"Enviar prueba\" del panel. |\n\n```json\n{\n  \"id\": \"evt_9b1e…\",\n  \"event\": \"invoice.created\",\n  \"livemode\": false,\n  \"api_version\": \"2026-09-30\",\n  \"timestamp\": \"2026-09-30T18:20:00.000Z\",\n  \"data\": { \"id\": \"…\", \"object\": \"invoice\", \"uuid\": \"…\", \"total\": 1241.5, \"…\": \"…\" }\n}\n```\n\nEncabezados: `X-MrFactura-Event`, `X-MrFactura-Delivery-Id` (único por entrega) y\n`X-MrFactura-Signature: sha256=<hex>`, el HMAC-SHA256 del **cuerpo crudo** con el secreto del\nendpoint (`whsec_…`). Responde `2xx` en menos de 8 s; si no, reintentamos hasta 3 veces.\nUsa `id` para no procesar un evento dos veces.\n\n**Verificar la firma (Node.js / Express):**\n\n```js\nconst crypto = require('crypto');\napp.post('/webhooks/mrfactura', express.raw({ type: 'application/json' }), (req, res) => {\n  const expected = 'sha256=' + crypto.createHmac('sha256', process.env.MRFACTURA_WEBHOOK_SECRET)\n    .update(req.body).digest('hex');\n  const received = req.get('X-MrFactura-Signature') || '';\n  if (received.length !== expected.length ||\n      !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {\n    return res.status(400).send('firma inválida');\n  }\n  const event = JSON.parse(req.body);\n  // ... procesa event.event / event.data\n  res.sendStatus(200);\n});\n```\n\n**Verificar la firma (Python / Flask):**\n\n```python\nimport hmac, hashlib, os\nfrom flask import request, abort\n\n@app.post(\"/webhooks/mrfactura\")\ndef mrfactura_webhook():\n    expected = \"sha256=\" + hmac.new(os.environ[\"MRFACTURA_WEBHOOK_SECRET\"].encode(),\n                                    request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(expected, request.headers.get(\"X-MrFactura-Signature\", \"\")):\n        abort(400)\n    event = request.get_json()\n    return \"\", 200\n```\n\n## Versionado\n\nLa versión va en la URL (`/v1`). Dentro de `v1` solo agregamos campos y endpoints nuevos\n(compatibles); tu integración debe ignorar campos que no conozca.","contact":{"name":"Soporte MrFactura","email":"soporte@mrfactura.com","url":"https://mrfactura.com"}},"servers":[{"url":"https://admin.mrfactura.com/api/v1","description":"Producción y sandbox (el modo lo define la llave)"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Cuenta","description":"Empresa y llave con las que estás autenticado."},{"name":"Facturas","description":"Emisión, consulta, descarga y cancelación de CFDI 4.0 de ingreso."},{"name":"Clientes","description":"Receptores guardados para reutilizarlos al facturar."}],"paths":{"/me":{"get":{"tags":["Cuenta"],"summary":"Cuenta de la llave","operationId":"getAccount","description":"Útil para validar la conexión: devuelve la empresa, el modo y los permisos de la llave.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X GET https://admin.mrfactura.com/api/v1/me \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\""}]}},"/invoices":{"post":{"tags":["Facturas"],"summary":"Emitir factura","operationId":"createInvoice","description":"Valida, calcula impuestos, timbra y devuelve la factura con su UUID. Si no envías `taxes` en un concepto se aplica IVA trasladado 16 %; `taxes: []` significa sin impuestos (ObjetoImp 01).","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Valor único por operación (p. ej. UUID v4). Repetirlo devuelve la misma respuesta sin repetir la operación."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceCreate"},"example":{"customer":{"rfc":"MASO451221PM4","legal_name":"MARIA OLIVIA MARTINEZ SAGAZ","tax_regime":"612","zip":"80290","cfdi_use":"G03"},"payment_form":"03","payment_method":"PUE","items":[{"description":"Servicio de consultoría","product_key":"80101500","unit_key":"E48","quantity":1,"unit_price":1000}]}}}},"responses":{"201":{"description":"Factura timbrada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Petición con la misma Idempotency-Key en curso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"El SAT/PAC rechazó el comprobante (details.sat_code) o Idempotency-Key reutilizada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"PAC temporalmente no disponible; reintenta con la misma Idempotency-Key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X POST https://admin.mrfactura.com/api/v1/invoices \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"customer\": {\n    \"rfc\": \"MASO451221PM4\",\n    \"legal_name\": \"MARIA OLIVIA MARTINEZ SAGAZ\",\n    \"tax_regime\": \"612\",\n    \"zip\": \"80290\",\n    \"cfdi_use\": \"G03\"\n  },\n  \"payment_form\": \"03\",\n  \"payment_method\": \"PUE\",\n  \"items\": [\n    {\n      \"description\": \"Servicio de consultoría\",\n      \"product_key\": \"80101500\",\n      \"unit_key\": \"E48\",\n      \"quantity\": 1,\n      \"unit_price\": 1000\n    }\n  ]\n}'"},{"lang":"JavaScript","label":"Node.js","source":"const res = await fetch('https://admin.mrfactura.com/api/v1/invoices', {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.MRFACTURA_API_KEY}`,\n    'Content-Type': 'application/json',\n    'Idempotency-Key': crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n    \"customer\": {\n      \"rfc\": \"MASO451221PM4\",\n      \"legal_name\": \"MARIA OLIVIA MARTINEZ SAGAZ\",\n      \"tax_regime\": \"612\",\n      \"zip\": \"80290\",\n      \"cfdi_use\": \"G03\"\n    },\n    \"payment_form\": \"03\",\n    \"payment_method\": \"PUE\",\n    \"items\": [\n      {\n        \"description\": \"Servicio de consultoría\",\n        \"product_key\": \"80101500\",\n        \"unit_key\": \"E48\",\n        \"quantity\": 1,\n        \"unit_price\": 1000\n      }\n    ]\n  }),\n});\nconst invoice = await res.json();\nif (!res.ok) throw new Error(`${invoice.error.code}: ${invoice.error.message}`);\nconsole.log(invoice.uuid, invoice.total);"},{"lang":"Python","source":"import os, uuid, requests\n\nres = requests.post(\n    \"https://admin.mrfactura.com/api/v1/invoices\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['MRFACTURA_API_KEY']}\",\n        \"Idempotency-Key\": str(uuid.uuid4()),\n    },\n    json={\n        \"customer\": {\n            \"rfc\": \"MASO451221PM4\",\n            \"legal_name\": \"MARIA OLIVIA MARTINEZ SAGAZ\",\n            \"tax_regime\": \"612\",\n            \"zip\": \"80290\",\n            \"cfdi_use\": \"G03\"\n        },\n        \"payment_form\": \"03\",\n        \"payment_method\": \"PUE\",\n        \"items\": [\n            {\n                \"description\": \"Servicio de consultoría\",\n                \"product_key\": \"80101500\",\n                \"unit_key\": \"E48\",\n                \"quantity\": 1,\n                \"unit_price\": 1000\n            }\n        ]\n    },\n    timeout=60,\n)\ninvoice = res.json()\nres.raise_for_status()\nprint(invoice[\"uuid\"], invoice[\"total\"])"}]},"get":{"tags":["Facturas"],"summary":"Listar facturas","operationId":"listInvoices","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"starting_after","in":"query","schema":{"type":"string"},"description":"id del último elemento de la página anterior."},{"name":"status","in":"query","schema":{"type":"string","enum":["valid","cancelled"]}},{"name":"customer","in":"query","schema":{"type":"string"},"description":"id de cliente"},{"name":"uuid","in":"query","schema":{"type":"string"}},{"name":"external_id","in":"query","schema":{"type":"string"}},{"name":"created[gte]","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"created[lte]","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceList"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X GET https://admin.mrfactura.com/api/v1/invoices?limit=10&status=valid \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\""}]}},"/invoices/{id}":{"get":{"tags":["Facturas"],"summary":"Consultar factura","operationId":"getInvoice","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No existe en esta empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/invoices/{id}/xml":{"get":{"tags":["Facturas"],"summary":"Descargar XML timbrado","operationId":"getInvoiceXml","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"XML CFDI 4.0","content":{"application/xml":{"schema":{"type":"string"}}}},"404":{"description":"No existe en esta empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X GET https://admin.mrfactura.com/api/v1/invoices/{id}/xml \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\" \\\n  -o factura.xml"}]}},"/invoices/{id}/pdf":{"get":{"tags":["Facturas"],"summary":"Descargar PDF","operationId":"getInvoicePdf","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"PDF","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No existe en esta empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X GET https://admin.mrfactura.com/api/v1/invoices/{id}/pdf \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\" \\\n  -o factura.pdf"}]}},"/invoices/{id}/cancel":{"post":{"tags":["Facturas"],"summary":"Cancelar factura","operationId":"cancelInvoice","description":"Motivos del SAT: `01` con relación (requiere `replacement_uuid`), `02` sin relación, `03` no se llevó a cabo la operación, `04` nominativa relacionada en factura global.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Valor único por operación (p. ej. UUID v4). Repetirlo devuelve la misma respuesta sin repetir la operación."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceCancel"},"example":{"motive":"02"}}}},"responses":{"200":{"description":"Factura cancelada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No existe en esta empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Ya estaba cancelada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X POST https://admin.mrfactura.com/api/v1/invoices/{id}/cancel \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"motive\": \"02\"\n}'"}]}},"/customers":{"post":{"tags":["Clientes"],"summary":"Crear cliente","operationId":"createCustomer","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Valor único por operación (p. ej. UUID v4). Repetirlo devuelve la misma respuesta sin repetir la operación."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerCreate"}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-codeSamples":[{"lang":"curl","source":"curl -X POST https://admin.mrfactura.com/api/v1/customers \\\n  -H \"Authorization: Bearer $MRFACTURA_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"rfc\": \"MASO451221PM4\",\n  \"legal_name\": \"MARIA OLIVIA MARTINEZ SAGAZ\",\n  \"tax_regime\": \"612\",\n  \"zip\": \"80290\",\n  \"cfdi_use\": \"G03\"\n}'"}]},"get":{"tags":["Clientes"],"summary":"Listar clientes","operationId":"listCustomers","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"starting_after","in":"query","schema":{"type":"string"},"description":"id del último elemento de la página anterior."},{"name":"rfc","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerList"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/customers/{id}":{"get":{"tags":["Clientes"],"summary":"Consultar cliente","operationId":"getCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Clientes"],"summary":"Actualizar cliente","operationId":"updateCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerUpdate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"400":{"description":"Parámetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Llave ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Sin permiso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Límite de peticiones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Clientes"],"summary":"Eliminar cliente","operationId":"deleteCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Eliminado"},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"invoice.created":{"post":{"summary":"Factura timbrada","parameters":[{"name":"X-MrFactura-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"sha256=<HMAC-SHA256 hex del cuerpo crudo>"},{"name":"X-MrFactura-Event","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-MrFactura-Delivery-Id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Recibido (cualquier 2xx)"}}}},"invoice.cancelled":{"post":{"summary":"Factura cancelada","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Recibido (cualquier 2xx)"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Llave `mrf_test_…` (sandbox, cualquier plan) o `mrf_live_…` (producción, plan Lord)."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","required":["type","code","message","request_id"],"properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","permission_error","not_found_error","idempotency_error","rate_limit_error","fiscal_error","pac_error","api_error"]},"code":{"type":"string","examples":["parameter_invalid","sat_validation_failed","rate_limited"]},"message":{"type":"string"},"param":{"type":"string","description":"Campo que causó el error, si aplica."},"details":{"type":"object","properties":{"sat_code":{"type":"string","examples":["CFDI40147"]}}},"request_id":{"type":"string"}}}}},"Account":{"type":"object","properties":{"object":{"const":"account"},"livemode":{"type":"boolean"},"company":{"type":"object","properties":{"id":{"type":"string"},"legal_name":{"type":"string"},"rfc":{"type":"string"}}},"key":{"type":"object","properties":{"id":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["invoices:read","invoices:write","invoices:cancel","customers:read","customers:write"]}}}}}},"Tax":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["IVA","ISR","IEPS"]},"rate":{"type":"number","description":"Tasa decimal (0.16 = 16 %). No aplica con factor Exento.","examples":[0.16]},"factor":{"type":"string","enum":["Tasa","Cuota","Exento"],"default":"Tasa"},"withholding":{"type":"boolean","default":false,"description":"true = retención (ISR siempre es retención)."}}},"InvoiceItem":{"type":"object","required":["description","product_key","unit_key","quantity","unit_price"],"properties":{"description":{"type":"string","maxLength":1000},"product_key":{"type":"string","pattern":"^\\d{8}$","description":"Clave de producto/servicio SAT.","examples":["81112100"]},"unit_key":{"type":"string","description":"Clave de unidad SAT.","examples":["E48","H87"]},"unit_name":{"type":"string"},"sku":{"type":"string"},"quantity":{"type":"number","exclusiveMinimum":0},"unit_price":{"type":"number","minimum":0,"description":"Precio unitario antes de impuestos."},"discount":{"type":"number","minimum":0,"description":"Descuento en importe (no porcentaje)."},"taxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"Por defecto IVA 16 % trasladado. `[]` = sin impuestos."}}},"CustomerCreate":{"type":"object","required":["rfc","legal_name","tax_regime","zip"],"properties":{"rfc":{"type":"string","examples":["MASO451221PM4"]},"legal_name":{"type":"string","description":"Nombre o razón social tal como está en la constancia del SAT (sin régimen societario)."},"tax_regime":{"type":"string","pattern":"^\\d{3}$","examples":["612","601"]},"zip":{"type":"string","pattern":"^\\d{5}$","description":"Código postal del domicilio fiscal."},"cfdi_use":{"type":"string","default":"G03"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"external_id":{"type":"string","description":"Tu id interno."},"metadata":{"type":"object","additionalProperties":true}}},"CustomerUpdate":{"allOf":[{"$ref":"#/components/schemas/CustomerCreate"}],"description":"Solo envía los campos a cambiar."},"Customer":{"allOf":[{"$ref":"#/components/schemas/CustomerCreate"},{"type":"object","properties":{"id":{"type":"string"},"object":{"const":"customer"},"livemode":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}]},"CustomerList":{"type":"object","properties":{"object":{"const":"list"},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}}}},"InvoiceCreate":{"type":"object","required":["customer","items"],"properties":{"customer":{"oneOf":[{"type":"string","description":"id de un cliente creado con POST /customers"},{"$ref":"#/components/schemas/CustomerCreate"}]},"payment_form":{"type":"string","description":"Forma de pago SAT (01 efectivo, 03 transferencia, 04 tarjeta de crédito, 28 tarjeta de débito, 99 por definir). Obligatoria con PUE.","examples":["03"]},"payment_method":{"type":"string","enum":["PUE","PPD"],"default":"PUE"},"currency":{"type":"string","default":"MXN"},"exchange_rate":{"type":"number","description":"Obligatorio si currency ≠ MXN."},"cfdi_use":{"type":"string","description":"Sobrescribe el uso CFDI del cliente."},"series":{"type":"string","description":"Serie del comprobante."},"items":{"type":"array","minItems":1,"maxItems":300,"items":{"$ref":"#/components/schemas/InvoiceItem"}},"notes":{"type":"string","maxLength":1000},"external_id":{"type":"string","description":"Tu id interno (filtrable en el listado)."},"metadata":{"type":"object","additionalProperties":true}}},"InvoiceCancel":{"type":"object","required":["motive"],"properties":{"motive":{"type":"string","enum":["01","02","03","04"]},"replacement_uuid":{"type":"string","description":"Obligatorio con motive 01."}}},"Invoice":{"type":"object","properties":{"id":{"type":"string"},"object":{"const":"invoice"},"livemode":{"type":"boolean"},"type":{"const":"I"},"status":{"type":"string","enum":["valid","cancelled"]},"uuid":{"type":"string","description":"Folio fiscal (UUID del timbre)."},"series":{"type":"string"},"folio_number":{"type":"integer"},"issued_at":{"type":"string","format":"date-time"},"stamped_at":{"type":"string","format":"date-time"},"customer":{"type":"object","properties":{"id":{"type":["string","null"]},"rfc":{"type":"string"},"legal_name":{"type":"string"},"tax_regime":{"type":"string"},"zip":{"type":"string"}}},"cfdi_use":{"type":"string"},"payment_form":{"type":"string"},"payment_method":{"type":"string"},"currency":{"type":"string"},"exchange_rate":{"type":["number","null"]},"subtotal":{"type":"number"},"discount":{"type":"number"},"taxes":{"type":"object","properties":{"transferred":{"type":"number"},"withheld":{"type":"number"}}},"total":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceItem"}},"cancellation":{"type":["object","null"],"properties":{"status":{"type":"string","enum":["cancelled","pending"],"description":"pending = el receptor debe aceptar la cancelación (solo producción)."},"sat_status":{"type":["string","null"],"description":"Estatus devuelto por el SAT/PAC (solo producción)."},"motive":{"type":"string"},"replacement_uuid":{"type":["string","null"]},"cancelled_at":{"type":"string","format":"date-time"}}},"notes":{"type":["string","null"]},"external_id":{"type":["string","null"]},"metadata":{"type":"object"},"links":{"type":"object","properties":{"xml":{"type":"string"},"pdf":{"type":"string"}}},"created_at":{"type":"string","format":"date-time"}}},"InvoiceList":{"type":"object","properties":{"object":{"const":"list"},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}}},"WebhookEvent":{"type":"object","properties":{"id":{"type":"string","description":"Id único del evento (evt_…). Úsalo para deduplicar."},"event":{"type":"string","enum":["invoice.created","invoice.cancelled","ping"]},"livemode":{"type":"boolean"},"api_version":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}}