# Timbrar un CFDI de retenciones (plataformas digitales) > `POST https://api.cfdi.express/v1/retenciones` — Timbra la constancia mensual de Retenciones 2.0 con el complemento de Servicios de Plataformas Tecnológicas (CveRetenc 26). URL de esta página: https://cfdi.express/docs/api/timbrar-retencion Autenticación: requiere API key (`Authorization: Bearer sk_test_…` o `sk_live_…`) Header `x-account-id`: indica la cuenta. Si no eres miembro, la respuesta es 403. Idempotencia: requiere el header `Idempotency-Key`. Desde la v1.43.0. Es la constancia que una plataforma digital emite a cada prestador de servicios o vendedor, una por receptor y por mes. Mandas los servicios del periodo con la comisión de la plataforma en cada uno. La API deriva los montos que el SAT valida: MontoTotOperacion, el IVA trasladado de cada servicio, el ISR retenido, el IVA retenido, las exclusiones de efectivo y DifIVAEntregadoPrestServ. La serie del comprobante es `RET`. `folio` en el recurso es el folio numérico interno, de la misma secuencia del emisor. `folioText` es el FolioInt que mandaste, o null. Si no mandas `folio`, el FolioInt timbrado es `RET` seguido de ese número. La moneda del recurso es `MXN`. `tipoServicio` (c_TipoDeServ) decide las tasas. No lo adivines: está en [GET /v1/catalogs/retenciones/tipo-servicio](https://cfdi.express/docs/api/catalogo-retenciones) y, por MCP, en `get_catalog` con `retenciones/tipo-servicio`. Para una persona física prestadora la tasa de ISR depende del tipo y del Ejercicio: `06` (Otro tipo de servicios) es 2.5% en 2026 y retiene 50% del IVA; `09` (Enseñanza a distancia o test o ejercicios) no retiene ISR y retiene 100% del IVA. Los tipos `07` y `08` tampoco retienen ISR y retienen 100% del IVA. Una persona moral prestadora paga 2.5% de ISR en los tipos que sí retienen ISR, y `isrTipoPagoRet` queda en `04`. `XAXX010101000` es 20% de ISR y 100% de IVA. Un prestador `XEXX010101000` no lleva ISR y retiene 100% del IVA. Si el RFC del emisor tiene 13 caracteres, no hay retención de ISR. El timbrado es el mismo que el de las facturas: espera hasta unos 20 segundos por el UUID (201). Bajo carga extrema responde 202 y hay que consultar [GET /v1/retenciones/{id}](https://cfdi.express/docs/api/obtener-retencion). Exige `Idempotency-Key`. Un replay trae `Idempotency-Replayed: true`. Al timbrar, fallar o cancelarse se pueden avisar con los webhooks `retencion.stamped`, `retencion.stamp_failed` y `retencion.cancelled`, suscritos en `POST /v1/webhook_endpoints`. Por MCP, `create_retencion` acepta el mismo cuerpo. La guía de producto está en [retenciones de plataformas digitales](https://cfdi.express/docs/retenciones-plataformas-digitales). ### Headers | Campo | Tipo | Requerido | Descripción | |---|---|---|---| | `Idempotency-Key` | string | sí | Llave única de la operación lógica (por ejemplo periodo + id del prestador), de 1 a 255 caracteres. Los replays devuelven la respuesta original con Idempotency-Replayed: true. | ### Cuerpo (JSON) | Campo | Tipo | Requerido | Descripción | |---|---|---|---| | `merchantId` | string | sí | Emisor de la constancia: la plataforma. | | `folio` | string | no | FolioInt que se timbra en el CFDI. Opcional. De 1 a 20 letras o dígitos (^[0-9a-zA-Z]{1,20}$). Ejemplo: 202609C0042. El folio numérico interno se sigue asignando. | | `ejercicio` | integer | sí | Año del periodo que se reporta. De 2021 a 2100. | | `mes` | integer | sí | Mes del periodo (1 a 12). MesIni y MesFin son ese mismo mes. | | `periodicidad` | string | no | Clave de c_Periodicidad, dos dígitos. Default 02 (mensual). 01 semanal, 03 diario, 04 quincenal, 05 otro. El catálogo está en GET /v1/catalogs/retenciones/periodicidad. | | `tipoServicio` | string | sí | Clave de c_TipoDeServ, dos dígitos, la misma para todos los servicios de este CFDI. Decide las tasas de ISR e IVA retenido. Ejemplos del catálogo: 05 comercio de bienes, 06 otro tipo de servicios, 07 contenido digital, 08 clubes en línea, 09 enseñanza a distancia. Lista completa: GET /v1/catalogs/retenciones/tipo-servicio. | | `isrTipoPagoRet` | string | no | ISR retenido a personas físicas: 03 pago definitivo (default) o 04 pago provisional. En personas morales siempre es 04. | | `receiver` | object | sí | Prestador (receptor de la constancia). Ver receiver.rfc, receiver.name, receiver.zip y receiver.curp. | | `receiver.rfc` | string | sí | RFC del prestador. 13 caracteres si es persona física y 12 si es persona moral. XAXX010101000 cuando no dio RFC (ISR 20%, IVA retenido 100%). XEXX010101000 si el prestador es extranjero. | | `receiver.name` | string | sí | Nombre tal como está en la Constancia de Situación Fiscal del prestador: mayúsculas, sin acentos, de 1 a 300 caracteres, sin el carácter |. | | `receiver.zip` | string | no | Código postal del domicilio fiscal, 5 dígitos. Va en receptores nacionales. Se omite con XEXX010101000. | | `receiver.curp` | string | no | CURP del prestador. Opcional. Tiene que cumplir el formato de la CURP. | | `servicios` | array | sí | Cada venta o servicio del prestador en el periodo (DetallesDelServicio). De 1 a 2,000 elementos. La fecha de cada uno tiene que caer en el mes reportado. | | `servicios[].fecha` | string | sí | Fecha del pago (FechaServ), YYYY-MM-DD. Tiene que caer en el mes reportado. | | `servicios[].formaPago` | string | sí | Clave de c_FormaPagoServ, dos dígitos. 01 efectivo, 02 transferencia, 03 tarjeta de crédito, 06 tarjeta de débito, 08 intermediario de pagos, 09 premios o bonificaciones. Lista: GET /v1/catalogs/retenciones/forma-pago-servicio. Con 09 se ignora ivaTasa y la comisión no es obligatoria. | | `servicios[].precioSinIva` | number | sí | Precio de la venta o del servicio antes de IVA. Mayor que 0 y hasta 999999999999. | | `servicios[].ivaTasa` | number | no | Tasa de IVA trasladado: 0.16 (default), 0.08 en región fronteriza o 0. Se ignora cuando formaPago es 09. | | `servicios[].comision` | object | no | Comisión de la plataforma por esa venta (ComisionDelServicio). Obligatoria salvo formaPago 09. | | `servicios[].comision.importe` | number | no | Lo que la plataforma cobró al prestador por esa venta. Mayor que 0 y hasta 999999999999. Es el único campo obligatorio dentro de comision. La comisión misma es obligatoria salvo formaPago 09. | | `servicios[].comision.porcentaje` | number | no | Tasa de la comisión, de 0.001 a 1. | | `servicios[].comision.base` | number | no | Base de la comisión. Mayor que 0 y hasta 999999999999. | | `servicios[].subTipo` | string | no | Clave de c_SubTipoServ, dos dígitos. Sólo con tipoServicio 01: 01 Personal, 02 Compartido, 03 Plus, 04 Cancelación. Catálogo: GET /v1/catalogs/retenciones/subtipo-servicio. | | `servicios[].rfcTerceroAutorizado` | string | no | RFC del tercero autorizado: 4 letras, fecha y homoclave (13 caracteres). | | `servicios[].contribucionGubernamental` | object | no | Contribución gubernamental de ese servicio, por ejemplo el impuesto sobre hospedaje. Si va, exige importe y entidad. | | `servicios[].contribucionGubernamental.importe` | number | no | Importe de la contribución. Mayor que 1.00 y hasta 999999999999. | | `servicios[].contribucionGubernamental.entidad` | string | no | Clave de c_EntidadesFederativas (01 a 32) de la entidad donde se pagó. 09 es Ciudad de México. Catálogo: GET /v1/catalogs/retenciones/entidad-federativa. | ## Ejemplos ### cURL — constancia mensual (v1.43.0) ```bash curl -X POST https://api.cfdi.express/v1/retenciones \ -H "Authorization: Bearer sk_test_..." \ -H "Idempotency-Key: retencion-2026-09-prestador-42" \ -H "Content-Type: application/json" \ -d '{ "merchantId": "mkq2f8v3x1t7p9d4n6c0b5rz", "folio": "202609C0042", "ejercicio": 2026, "mes": 9, "periodicidad": "02", "tipoServicio": "05", "isrTipoPagoRet": "03", "receiver": { "rfc": "XAXX010101000", "name": "NOMBRE DEL PRESTADOR", "zip": "42501" }, "servicios": [ { "fecha": "2026-09-15", "formaPago": "02", "precioSinIva": 1000, "ivaTasa": 0.16, "comision": { "importe": 150, "porcentaje": 0.15 } } ] }' ``` ## Respuesta ### 201 Created ```json { "id": "ret7k3q9x2m4v1t8p6d0n5cb", "object": "retencion", "livemode": false, "status": "stamped", "merchantId": "mkq2f8v3x1t7p9d4n6c0b5rz", "uuid": "9f1b2c3d-4e5f-4a6b-8c9d-0e1f2a3b4c5d", "serie": "RET", "folio": 42, "folioText": "202609C0042", "fecha": "2026-10-01T10:00:00", "cveRetenc": "26", "ejercicio": 2026, "mes": 9, "periodicidad": "02", "tipoServicio": "05", "receiver": { "rfc": "XAXX010101000", "name": "NOMBRE DEL PRESTADOR", "zip": "42501" }, "servicios": [ { "fecha": "2026-09-15", "formaPago": "02", "precioSinIva": 1000, "ivaTasa": 0.16, "comision": { "importe": 150, "porcentaje": 0.15 } } ], "impRetenidos": [ { "impuesto": "001", "baseRet": 1000, "montoRet": 200, "tipoPagoRet": "03", "tasa": 0.2 }, { "impuesto": "002", "baseRet": 160, "montoRet": 160, "tipoPagoRet": "01", "tasa": 1 } ], "montoTotOperacion": 1000, "montoTotRet": 360, "isrRetenido": 200, "ivaRetenido": 160, "ivaTrasladado": 160, "difIvaEntregadoPrestServ": 0, "comisiones": 150, "currency": "MXN", "files": { "status": "pending" }, "stampedAt": "2026-10-01T16:00:10.512Z", "createdAt": "2026-10-01T16:00:08.907Z" } ``` ## Errores | Status | code | Cuándo aparece | |---|---|---| | 400 | `validation_error` | Datos inválidos: reglas cruzadas del SAT, claves de catálogo desconocidas u otro campo del cuerpo. El arreglo errors trae el path. subcode merchant_archived si el emisor está archivado (hay que restaurarlo con POST /v1/merchants/{id}/unarchive). subcode csd_missing si el emisor no tiene CSD. | | 401 | `unauthorized` | Falta el header Authorization o la llave es inválida. | | 402 | `insufficient_credits` | No hay saldo para el timbre, o se llegó al tope mensual (monthly_limit_reached). | | 404 | `not_found` | El emisor no existe en tu cuenta. | | 409 | `request_in_flight` | Otra petición con el mismo Idempotency-Key está en proceso. | | 422 | `idempotency_key_reuse` | Reusaste la llave de idempotencia con un cuerpo distinto. | | 422 | `sat_rejected` | El SAT rechazó la constancia. | | 503 | `pac_unavailable` | El PAC no está disponible. Reintenta con la misma llave de idempotencia. | ## Notas - Los montos del ejemplo 201 los calcula la API a partir de `precioSinIva`. `montoTotOperacion` es la suma de esos precios en los servicios que llevan IVA (`formaPago` `09` no entra). El efectivo (`formaPago` `01`) se excluye de las bases de ISR e IVA retenido. `impRetenidos[].impuesto` es `001` (ISR) o `002` (IVA). El IVA usa `tipoPagoRet` `01` (pago definitivo IVA). El ISR usa `03` o `04`. - Con RFC `XAXX010101000` el ISR es 20% y el IVA retenido es 100%, así que `difIvaEntregadoPrestServ` queda en 0. El ejemplo asume un emisor con RFC de 12 caracteres: si el emisor es persona física (13 caracteres) no hay partida de ISR. `XEXX010101000` es el prestador extranjero, no lleva `receiver.zip`, no retiene ISR y retiene 100% del IVA. - Un 202 significa que el timbrado sigue en curso (`status: "stamping"`). Consulta [GET /v1/retenciones/{id}](https://cfdi.express/docs/api/obtener-retencion). - Cuando `files.status` es `ready`, `files.xmlUrl`, `files.pdfUrl` y `files.zipUrl` son URLs firmadas de 15 minutos. `files.status` también puede ser `pending`, `processing` o `failed`. - La serie siempre es `RET`. `folioText` guarda el FolioInt del request. Si lo omites queda null y el FolioInt timbrado es `RET` más el folio numérico. - El recurso trae `object: "retencion"` y `status` en `stamping`, `stamped`, `stamp_failed`, `cancel_pending` o `cancelled`. ## Relacionados - [GET /v1/retenciones](https://cfdi.express/docs/api/listar-retenciones) — Listar retenciones - [GET /v1/retenciones/{id}](https://cfdi.express/docs/api/obtener-retencion) — Consultar retención - [POST /v1/retenciones/{id}/cancel](https://cfdi.express/docs/api/cancelar-retencion) — Cancelar retención - [GET /v1/catalogs/retenciones/{catalog}](https://cfdi.express/docs/api/catalogo-retenciones) — Catálogos de retenciones --- Referencia completa: https://cfdi.express/docs/api · OpenAPI: https://api.cfdi.express/openapi.json · Llaves: https://dash.cfdi.express