Documentación / API / Emisores (merchants)

Subir CSD

POSThttps://api.cfdi.express/v1/merchants/{id}/csdAPI key

Sube el CSD del emisor (.cer y .key en base64 más la contraseña). Se valida, se encripta y se registra con el PAC.

Antes de tocar al PAC validamos localmente: que el .cer sea legible, que esté vigente, que la llave corresponda al certificado, que la contraseña abra la llave y que no estés subiendo una FIEL en lugar de un CSD.

Los archivos se guardan con encriptación de sobre AES-256-GCM. La llave privada nunca se devuelve por la API.

Autenticación
Authorization: Bearer sk_test_... | sk_live_...

El header x-account-id indica la cuenta. Si no eres miembro, la respuesta es 403.

Parámetros

Parámetros de ruta

CampoTipoDescripción
idreqstringId del emisor.

Cuerpo (JSON)

CampoTipoDescripción
cerreqstringArchivo .cer (DER) codificado en base64.
keyreqstringArchivo .key (DER, PKCS#8 encriptado) en base64.
passwordreqstringContraseña de la llave privada.

Ejemplos

cURL
curl -X POST https://api.cfdi.express/v1/merchants/mkq2f8v3x1t7p9d4n6c0b5rz/csd \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d "{
    \"cer\": \"$(base64 -w0 CSD_EKU9003173C9.cer)\",
    \"key\": \"$(base64 -w0 CSD_EKU9003173C9.key)\",
    \"password\": \"12345678a\"
  }"
Node.js
import { readFile } from "node:fs/promises";

const [cer, key] = await Promise.all([
  readFile("CSD_EKU9003173C9.cer"),
  readFile("CSD_EKU9003173C9.key"),
]);

const res = await fetch(
  `https://api.cfdi.express/v1/merchants/${merchantId}/csd`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CFDI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      cer: cer.toString("base64"),
      key: key.toString("base64"),
      password: "12345678a",
    }),
  },
);

const csd = await res.json();
console.log("Vigente hasta", csd.validTo);

Respuesta

201 Created
{
  "object": "csd",
  "serialNumber": "30001000000500003416",
  "rfc": "EKU9003173C9",
  "validFrom": "2023-05-18T00:00:00.000Z",
  "validTo": "2027-05-18T00:00:00.000Z",
  "status": "active",
  "registeredWithPac": true,
  "updatedAt": "2026-08-18T16:24:51.002Z"
}
400 — CSD inválido
{
  "type": "https://api.cfdi.express/docs/errors/validation_error",
  "title": "Validation error",
  "status": 400,
  "code": "validation_error",
  "detail": "The private key could not be opened with the given password",
  "subcode": "csd_key_unreadable"
}

Errores

StatuscodeCuándo aparece
400validation_errorEl subcode identifica el problema: csd_invalid_cer, csd_expired, csd_not_yet_valid, csd_key_unreadable, csd_key_mismatch, csd_is_fiel o csd_rejected_by_pac.
401unauthorizedFalta el header Authorization o la llave es inválida.
404not_foundEl emisor no existe en tu cuenta.
503pac_unavailableEl PAC no está disponible para registrar el certificado.

Notas

  • El RFC del certificado debe coincidir con el RFC del emisor.
  • Un CSD vencido no se puede subir. Renuévalo en el portal del SAT y usa PUT /v1/merchants/{id}/csd.

Continuar