# Subir CSD > `POST https://api.cfdi.express/v1/merchants/{id}/csd` — 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. URL de esta página: https://cfdi.express/docs/api/subir-csd Autenticación: requiere API key (`Authorization: Bearer sk_test_…` o `sk_live_…`) 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. ### Parámetros de ruta | Campo | Tipo | Requerido | Descripción | |---|---|---|---| | `id` | string | sí | Id del emisor. | ### Cuerpo (JSON) | Campo | Tipo | Requerido | Descripción | |---|---|---|---| | `cer` | string | sí | Archivo .cer (DER) codificado en base64. | | `key` | string | sí | Archivo .key (DER, PKCS#8 encriptado) en base64. | | `password` | string | sí | Contraseña de la llave privada. | ## Ejemplos ### cURL ```bash 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 ```javascript 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 ```json { "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 ```json { "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 | Status | code | Cuándo aparece | |---|---|---| | 400 | `validation_error` | El 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. | | 401 | `unauthorized` | Falta el header Authorization o la llave es inválida. | | 404 | `not_found` | El emisor no existe en tu cuenta. | | 503 | `pac_unavailable` | El 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](https://cfdi.express/docs/api/reemplazar-csd). ## Relacionados - [PUT /v1/merchants/{id}/csd](https://cfdi.express/docs/api/reemplazar-csd) — Reemplazar CSD - [GET /v1/merchants/{id}/csd](https://cfdi.express/docs/api/obtener-csd) — Consultar CSD - [POST /v1/merchants](https://cfdi.express/docs/api/crear-emisor) — Crear emisor --- Referencia completa: https://cfdi.express/docs/api · OpenAPI: https://api.cfdi.express/openapi.json · Llaves: https://dash.cfdi.express