Catálogos

Productos y servicios

Tu catálogo con claves del SAT e impuestos por default, para no repetirlos en cada timbrado.

Registrar un producto es opcional: puedes mandar cada concepto completo al timbrar. Pero un catálogo te ahorra repetir las claves del SAT en cada factura y —más importante— evita que un typo en una clave de 8 dígitos se cuele en un CFDI.

Con un producto registrado, un concepto se reduce a:

{ "productId": "clxp01…", "quantity": 2 }

Listar y buscar

GET /api/authenticated/{org}/products

Scope: products.read

ParámetroTipoDescripción
qstringBusca en descripción y clave del SAT.
limitnúmero1–500. Default 50.
cursorcuidPaginación.
{
  "items": [
    {
      "id": "clxp01…",
      "claveProdServ": "81111500",
      "claveUnidad": "E48",
      "description": "Servicios de desarrollo de software",
      "unitPrice": "1500.00",
      "iva": "0.160000",
      "ivaExento": false,
      "ieps": null,
      "iepsCuota": null,
      "isr": null,
      "ivaRet": null,
      "iepsRet": null,
      "objetoImp": "02",
      "noIdentificacion": "DEV-001",
      "createdAt": "2026-02-02T10:00:00.000Z"
    }
  ],
  "nextCursor": null
}

Crear

POST /api/authenticated/{org}/products

Scope: products.create

CampoRequeridoReglas
claveProdServ8 dígitos del catálogo c_ClaveProdServ.
claveUnidad2–3 caracteres de c_ClaveUnidad (p. ej. E48 servicio, H87 pieza, KGM kilogramo).
description3–1000 caracteres. Es lo que se imprime en el CFDI.
unitPricePrecio unitario sin impuestos.
codenoTu propio SKU o referencia del ERP. Único dentro de la organización; es la llave que usa upsert.
ivanoTasa decimal: 0, 0.08 o 0.16.
ivaExentonoBooleano. Excluyente con una tasa de iva distinta de cero.
iepsnoTasa decimal de IEPS.
iepsCuotanoIEPS de cuota, en pesos por unidad. Excluyente con ieps.
isr, ivaRet, iepsRetnoTasas de retención por default.
objetoImpnoClave de c_ObjetoImp (0108). Si la omites, se deriva: 02 si el concepto lleva impuestos, 01 si no.
noIdentificacionnoNúmero de parte / SKU que se imprime en el CFDI. Máx. 100 caracteres.
fraccionArancelaria, claveUnidadAduananoDatos aduaneros, solo para exportación.
upsertnoVer abajo.
curl -s -X POST "$FACTY_HOST/api/authenticated/$FACTY_ORG/products" \
  -H "X-API-Key: $FACTY_KEY" -H "Content-Type: application/json" \
  -d '{
    "code": "DEV-001",
    "claveProdServ": "81111500",
    "claveUnidad": "E48",
    "description": "Servicios de desarrollo de software",
    "unitPrice": 1500,
    "iva": 0.16
  }' | jq

upsert con tu propio code

Igual que en clientes, "upsert": true convierte un code ya existente en el caso de éxito (200, created: false) en vez de crear un duplicado. Requiere que mandes code: sin él no hay contra qué empatar.

Tampoco sobrescribe el registro existente. Para empujar cambios de precio o de clave, usa PATCH /products/{productId}.

Encontrar las claves del SAT

No adivines las claves. Hay endpoints de búsqueda:

# Clave de producto/servicio (~52,000 entradas, búsqueda por texto)
curl -s "$FACTY_HOST/api/authenticated/catalogs/clave-prod-serv?q=software" \
  -H "X-API-Key: $FACTY_KEY" | jq

# Clave de unidad
curl -s "$FACTY_HOST/api/authenticated/catalogs/clave-unidad?q=servicio" \
  -H "X-API-Key: $FACTY_KEY" | jq

Ver Catálogos del SAT.

Herencia: producto → concepto

Al timbrar, cada campo del concepto que omites se hereda del producto. Cada campo que mandas gana:

{
  "items": [
    { "productId": "clxp01…", "quantity": 3 },
    { "productId": "clxp01…", "quantity": 1, "unitPrice": 1200, "descuento": 0.10 }
  ]
}

El primer concepto usa el precio y el IVA del producto. El segundo usa un precio negociado y le aplica 10% de descuento, conservando el IVA del catálogo.

Si no mandas productId, el concepto tiene que traer al menos claveProdServ, claveUnidad, description y unitPrice, o el timbrado responde 422.