Subir CSD
Sube los archivos del Certificado de Sello Digital (CSD) para una sucursal. Esto es necesario para poder sellar (firmar) las facturas.
Endpoint
POST /organizations/branches/{branchId}/csd
Path Parameters
| Nombre | Tipo | Descripción |
|---|---|---|
branchId | string | UUID de la sucursal. |
Headers
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
Authorization | string | Sí | Token de autenticación Bearer (JWT). |
x-user-id | string | Sí | ID del usuario que realiza la acción. |
x-idempotency | string | Sí | UUID v7 único para garantizar idempotencia. |
Content-Type | string | Sí | multipart/form-data |
Body Parameters (Multipart)
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
cerFile | file | Sí | Archivo .cer del CSD. |
keyFile | file | Sí | Archivo .key del CSD. |
password | string | Sí | Contraseña de la clave privada. |
Ejemplos de Código
- cURL
- Node.js (TypeScript)
- Python
- PHP (Guzzle)
curl -X POST https://sandbox-api.lummy.com/organizations/branches/${BRANCH_ID}/csd \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "x-user-id: ${LUMMY_USER_ID}" \
-H "x-idempotency: $(uuidgen)" \
-F "cerFile=@/path/to/certificado.cer" \
-F "keyFile=@/path/to/llave.key" \
-F "password=contraseña_csd"
Define ACCESS_TOKEN y LUMMY_USER_ID en tu shell:
export ACCESS_TOKEN="your-access-token"
export LUMMY_USER_ID="your-user-id"
El ACCESS_TOKEN se obtiene después de que el usuario se autentica en la plataforma Lummy.
import axios, { AxiosError } from 'axios';
import FormData from 'form-data';
import fs from 'fs';
import { v4: uuidv4 } from 'uuid';
interface CsdResponse {
id: string;
status: string;
}
async function subirCsd(): Promise<CsdResponse> {
const BRANCH_ID = 'your-branch-uuid'; // Reemplazar con ID real
const API_URL = `https://sandbox-api.lummy.com/organizations/branches/${BRANCH_ID}/csd`;
const ACCESS_TOKEN = process.env.ACCESS_TOKEN!;
const USER_ID = process.env.LUMMY_USER_ID!;
const form = new FormData();
form.append('cerFile', fs.createReadStream('./certificado.cer'));
form.append('keyFile', fs.createReadStream('./llave.key'));
form.append('password', 'contraseña_csd');
try {
const response = await axios.post<CsdResponse>(API_URL, form, {
headers: {
...form.getHeaders(),
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'x-user-id': USER_ID,
'x-idempotency': uuidv4(),
},
});
console.log('✅ CSD subido exitosamente');
console.log('ID:', response.data.id);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError;
console.error('❌ Error al subir CSD:', axiosError.response?.data);
throw axiosError;
}
throw error;
}
}
// Ejecutar
subirCsd();
npm install axios form-data uuid
npm install -D @types/uuid @types/node
Define ACCESS_TOKEN y LUMMY_USER_ID en tu shell. El ACCESS_TOKEN se obtiene después de que el usuario se autentica en la plataforma Lummy.
import os
import requests
from uuid import uuid4
def subir_csd():
branch_id = "your-branch-uuid"
api_url = f"https://sandbox-api.lummy.com/organizations/branches/{branch_id}/csd"
access_token = os.getenv("ACCESS_TOKEN")
user_id = os.getenv("LUMMY_USER_ID")
if not access_token or not user_id:
raise ValueError("Debes definir ACCESS_TOKEN y LUMMY_USER_ID")
headers = {
"Authorization": f"Bearer {access_token}",
"x-user-id": user_id,
"x-idempotency": str(uuid4()),
}
# Nota: requests calcula el Content-Type multipart automáticamente
files = {
'cerFile': ('certificado.cer', open('./certificado.cer', 'rb'), 'application/x-x509-ca-cert'),
'keyFile': ('llave.key', open('./llave.key', 'rb'), 'application/octet-stream')
}
data = {
'password': 'contraseña_csd'
}
try:
response = requests.post(api_url, files=files, data=data, headers=headers, timeout=60)
response.raise_for_status()
result = response.json()
print("✅ CSD subido exitosamente")
print(f"ID: {result['id']}")
return result
except requests.exceptions.RequestException as e:
print(f"❌ Error al subir CSD: {e}")
if e.response:
print(f"Detalle: {e.response.text}")
raise
finally:
files['cerFile'][1].close()
files['keyFile'][1].close()
if __name__ == "__main__":
subir_csd()
pip install requests
Define ACCESS_TOKEN y LUMMY_USER_ID en tu shell. El ACCESS_TOKEN se obtiene después de que el usuario se autentica en la plataforma Lummy.
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Ramsey\Uuid\Uuid;
function subirCsd()
{
$branchId = 'your-branch-uuid';
$apiUrl = "https://sandbox-api.lummy.com/organizations/branches/{$branchId}/csd";
$accessToken = getenv('ACCESS_TOKEN');
$userId = getenv('LUMMY_USER_ID');
if (!$accessToken || !$userId) {
throw new Exception('Debes definir ACCESS_TOKEN y LUMMY_USER_ID');
}
$client = new Client(['timeout' => 60]);
try {
$response = $client->post($apiUrl, [
'headers' => [
'Authorization' => 'Bearer ' . $accessToken,
'x-user-id' => $userId,
'x-idempotency' => Uuid::uuid4()->toString(),
],
'multipart' => [
[
'name' => 'password',
'contents' => 'contraseña_csd'
],
[
'name' => 'cerFile',
'contents' => fopen('./certificado.cer', 'r'),
'filename' => 'certificado.cer'
],
[
'name' => 'keyFile',
'contents' => fopen('./llave.key', 'r'),
'filename' => 'llave.key'
],
]
]);
$data = json_decode($response->getBody(), true);
echo "✅ CSD subido exitosamente\n";
echo "ID: " . $data['id'] . "\n";
return $data;
} catch (RequestException $e) {
echo "❌ Error al subir CSD: " . $e->getMessage() . "\n";
if ($e->getResponse()) {
echo "Detalle: " . $e->getResponse()->getBody() . "\n";
}
throw $e;
}
}
try {
subirCsd();
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
exit(1);
}
composer require guzzlehttp/guzzle ramsey/uuid
Define ACCESS_TOKEN y LUMMY_USER_ID en tu shell. El ACCESS_TOKEN se obtiene después de que el usuario se autentica en la plataforma Lummy.
Respuestas
201 Created
CSD subido y validado exitosamente.
{
"id": "123e4567-e89b-12d3-a456-426614174000"
}
400 Bad Request
- Archivos faltantes.
- Contraseña incorrecta.
- El RFC del certificado no coincide con el RFC de la sucursal.
- Certificado caducado o revocado.
404 Not Found
La sucursal especificada no existe.