CPDS API integrácia — sprievodca pre vývojárov
Tento článok je technický sprievodca pre vývojárov, ktorí integrujú slovenský účtovný systém, ERP alebo SaaS aplikáciu s CPDS poskytovateľom pre povinnú elektronickú fakturáciu od 1.1.2027. Pokrýva REST API, autentizáciu, validáciu UBL XML, webhooks, sandbox testovanie a security model.
Pre koho je článok: vývojári, integrační inžinieri, IT architekti slovenských firiem alebo ich dodávateľov. Predpokladá znalosť HTTP/REST, JSON, XML a základov OAuth 2.0.
Architektonický prehľad
Z pohľadu integrácie má Peppol e-fakturácia 4 vrstvy, s ktorými sa stretnete:
- Vaša aplikácia — generuje obchodné dáta (faktúry).
- CPDS REST API — rozhranie medzi vašou aplikáciou a Peppol Access Pointom (vaším CPDS poskytovateľom).
- AS4 protokol — komunikácia medzi CPDS-CPDS, riešená poskytovateľom, neviditeľná pre vás.
- SML/SMP — globálna service discovery vrstva pre Peppol.
Vy implementujete len vrstvu 1 a komunikáciu na vrstvu 2. Vrstvy 3 a 4 sú zodpovednosťou CPDS poskytovateľa.
Pre detailný architektonický rozbor pozri Pre IT oddelenia a Peppol BIS 3.0 vs UBL 2.1 — vysvetlenie.
Voľba CPDS poskytovateľa pre integráciu
Pri výbere CPDS pre integráciu zhodnoťte:
- Kvalita dokumentácie — existuje OpenAPI 3.0 / Swagger spec? Code samples?
- Sandbox prostredie — môžem testovať bez kreditnej karty?
- Webhook support — async callback pri udalostiach?
- Throughput a rate limits — koľko TPS zvládne API?
- SDK alebo libraries — pre Java, .NET, Node.js, Python?
- Slovenský technický support — pri integrácii to ušetrí týždne.
Slovenskí CPDS poskytovatelia ako ePošťák API ponúkajú REST API s OpenAPI 3.1 dokumentáciou, sandbox prostredím, SDK v 6 jazykoch a Peppol Directory lookup nástrojom pre overenie účastníkov pred odoslaním. Pri ostatných poskytovateľoch sa pýtajte explicitne.
Autentizácia — OAuth 2.0 vs API kľúče
Štandardné CPDS API podporujú dva mechanizmy:
OAuth 2.0 Client Credentials Flow
Najbezpečnejší pre produkciu. Postup:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=invoices:read invoices:write
Response:
{
"access_token": "eyJhbGc...",
"token_type": "Bearer",
"expires_in": 3600
}
Token použijete v hlavičke Authorization: Bearer eyJhbGc... pre všetky ďalšie volania. Po expirácii (typicky 1 hodina) získate nový.
Best practices:
- Cache token na dobu jeho platnosti, neget-tokenujte pri každom requeste.
- Použite secret manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) pre client_secret.
- Rotujte client_secret každých 90 dní.
API kľúče
Jednoduchšie, ale menej bezpečné. Vhodné pre interné servisy a sandbox testovanie.
GET /v1/invoices
Authorization: Bearer YOUR_API_KEY
Best practices:
- Nikdy nehardcodujte v zdrojovom kóde.
- Nikdy necommitujte do gitu (overte
.gitignore). - Použite rôzne kľúče pre sandbox a produkciu.
- Implementujte rotáciu cez automatizáciu (najmenej raz ročne).
Príjem dokumentov — webhook pattern
Recommended pattern pre prijímanie e-faktúr je webhook, nie polling. CPDS pošle HTTP POST na váš endpoint pri každej príchodzej faktúre.
Registrácia webhook
POST /v1/webhooks
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"url": "https://your-app.com/webhooks/cpds",
"events": ["invoice.received", "invoice.delivered", "invoice.rejected"],
"secret": "webhook_signing_secret_min_32_chars"
}
Spracovanie webhook
CPDS pošle:
POST /webhooks/cpds HTTP/1.1
Host: your-app.com
Content-Type: application/json
X-CPDS-Signature: sha256=abc123...
X-CPDS-Event: invoice.received
X-CPDS-Delivery-Id: deliv_xyz
{
"event": "invoice.received",
"invoice_id": "inv_abc123",
"received_at": "2026-05-09T10:30:00Z",
"sender": {
"participant_id": "0245:SK1234567890",
"name": "Acme s.r.o."
},
"metadata": {
"amount": 1234.56,
"currency": "EUR",
"due_date": "2026-06-08"
}
}
Validácia HMAC podpisu
Vždy validujte signature pred spracovaním. Pseudokód v Node.js:
import crypto from 'crypto';
function validateSignature(rawBody, signatureHeader, secret) {
const [algo, hash] = signatureHeader.split('=');
const expected = crypto
.createHmac(algo, secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(hash, 'hex'),
Buffer.from(expected, 'hex')
);
}
Pozor: používa sa HMAC SHA-256 alebo SHA-512 podľa konkrétneho CPDS. Niektorí poskytovatelia migrovali z SHA-256 na SHA-512 — overte aktuálny štandard v dokumentácii.
Idempotenica a retry
Webhook môže byť doručený viackrát (CPDS retry-uje pri 5xx alebo timeoutoch). Vaš handler musí byť idempotentný:
async function handleWebhook(payload) {
const deliveryId = payload.headers['x-cpds-delivery-id'];
// Check if already processed (DB unique constraint or Redis SET NX)
const isDuplicate = await db.webhookDeliveries.exists(deliveryId);
if (isDuplicate) return { status: 'already_processed' };
// Process
await db.transaction(async (tx) => {
await tx.webhookDeliveries.insert({ id: deliveryId, processed_at: new Date() });
await tx.invoices.upsert({ id: payload.body.invoice_id, ... });
});
return { status: 'ok' };
}
Rýchla odpoveď
Vráťte HTTP 2xx do 10 sekúnd. Inak CPDS retry-uje. Najjednoduchšie:
- Webhook handler validuje signature a enqueueuje úlohu (BullMQ, Sidekiq, AWS SQS).
- Vráti 200 hneď.
- Background worker spracuje úlohu mimo HTTP cyklu.
Sťahovanie obsahu faktúry
Webhook obsahuje len ID a metadata. Pre samotný UBL XML zavolajte:
GET /v1/invoices/{id}
Authorization: Bearer YOUR_TOKEN
Accept: application/xml
Response:
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>2026-001</cbc:ID>
<cbc:IssueDate>2026-05-09</cbc:IssueDate>
<!-- ... rest of invoice ... -->
</Invoice>
Pre PDF (ak ho CPDS generuje) typicky Accept: application/pdf.
Odosielanie dokumentov
Validácia pred odoslaním
Vždy zavolajte dry-run validation pred ostrým submitom. Šetrí to zbytočné rejected faktúry:
POST /v1/invoices/validate
Authorization: Bearer YOUR_TOKEN
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<Invoice ...>...</Invoice>
Response (úspech):
{ "valid": true, "warnings": [] }
Response (chyba):
{
"valid": false,
"errors": [
{
"rule": "BR-S-08",
"level": "fatal",
"location": "/Invoice/cac:InvoiceLine[1]/cac:Item/cac:ClassifiedTaxCategory",
"message": "Invoice line VAT category code is required"
}
]
}
Submit
POST /v1/invoices
Authorization: Bearer YOUR_TOKEN
Content-Type: application/xml
Idempotency-Key: invoice-2026-001-v1
<?xml version="1.0" encoding="UTF-8"?>
<Invoice ...>...</Invoice>
Response:
{
"id": "inv_xyz789",
"status": "submitted",
"tracking_id": "trk_abc123",
"submitted_at": "2026-05-09T10:35:00Z"
}
Sledovanie stavu
Status sa mení asynchrónne (delivered → acknowledged alebo delivered → rejected). Sledovať môžete cez:
- Webhook
invoice.delivered/invoice.rejected(preferované). - Polling
GET /v1/invoices/{id}/status(fallback).
Generovanie UBL XML
Generovanie UBL XML zo svojich obchodných dát zvládnete buď cez knižnicu, alebo manuálne (pri jednoduchých faktúrach).
Odporúčané knižnice
- Java/Kotlin: Mustang Project, Helger UBL libraries.
- .NET: UBL.NET, Helger PEPPOL.
- Node.js / TypeScript: node-ubl, peppolio.
- Python: python-ubl, generická XML s ručným mapovaním.
- PHP: num-num/ubl-invoice.
Mapovanie polí
Najčastejšie polia, ktoré potrebujete vyplniť:
| UBL pole | Význam | Príklad |
|---|---|---|
| cbc:ID | Číslo faktúry | ”2026-001” |
| cbc:IssueDate | Dátum vystavenia | ”2026-05-09” |
| cbc:DueDate | Dátum splatnosti | ”2026-06-08” |
| cbc:DocumentCurrencyCode | Mena | ”EUR” |
| cac:AccountingSupplierParty | Predajca | celý blok s IČO/DIČ/adresou |
| cac:AccountingCustomerParty | Kupujúci | celý blok |
| cac:InvoiceLine | Položky faktúry | jeden blok per položka |
| cac:TaxTotal | DPH summary | sadzby a celková DPH |
| cac:LegalMonetaryTotal | Súčty | netto, DPH, brutto |
Pre slovenské špecifiká (CIUS) povinné polia:
- IČO predajcu —
cac:PartyLegalEntity/cbc:CompanyID(formát: 8 číslic) - DIČ predajcu —
cac:PartyTaxScheme/cbc:CompanyID(formát: SK + 10 číslic) - IČ DPH predajcu —
cac:PartyTaxScheme/cbc:CompanyID(ak platiteľ DPH) - Bankové spojenie —
cac:PaymentMeans/cac:PayeeFinancialAccount
Sandbox testovanie
Setup
- Zaregistrujte sandbox účet u CPDS (typicky zadarmo).
- Získajte sandbox endpoint (typicky
https://sandbox.cpds.example.com) a sandbox API kľúče. - Získajte test Peppol participant ID v slovenskom formáte
0245:DIČ(napr.0245:SKTEST123v sandboxe).
Test plan
Minimálny set testov pre integráciu:
- T1: Smoke test —
GET /v1/health→ 200. - T2: Auth test —
POST /oauth/token→ bearer token. - T3: Round-trip — pošlite faktúru sami sebe (sender = receiver). Príde späť cez webhook.
- T4: Validation negative — pošlite zámerne chybnú XML, očakávajte rejected stav.
- T5: Webhook signature — overte, že váš handler validuje HMAC.
- T6: Idempotency — pošlite ten istý webhook 2x, overte, že druhý raz nespracujete.
- T7: Performance — synthetic load test (100 faktúr za minútu) — sledujte p95 latency a error rate.
- T8: Retry resilience — simulujte 503 odpovede CPDS, overte, že vaša aplikácia retry-uje s exponential backoff.
Test pre slovenský trh
Pre testovanie slovenských špecifík odporúčame:
- Použiť testovacie IČO/DIČ kombinácie z OpenPeppol Testbed dokumentácie.
- Otestovať všetky platné slovenské DPH sadzby — pre rok 2026 a neskôr: 23 % základná, 19 % znížená (potraviny, lieky a vybrané položky), 5 % a 0 % (oslobodené plnenia). Sadzby sa môžu meniť novelizáciou — pred testom si overte aktuálne znenie v zákone č. 222/2004 Z. z.
- Otestovať dobropis (Credit Note) s referenciou na pôvodnú faktúru.
- Otestovať faktúru s prílohou (PDF embedded).
Production deployment checklist
Pred prepnutím na produkciu:
- Production API kľúče v secret manager, nie v repo.
- Webhook endpoint na HTTPS (certifikát platný, nie self-signed).
- HMAC signature validation enabled.
- Idempotency keys pre všetky write operácie.
- Retry s exponential backoff (1s → 5s → 30s → 5m → 1h → kill).
- Circuit breaker pre prípad výpadku CPDS (Resilience4j, Polly, opossum).
- Monitoring: rate, error, duration metrics per endpoint.
- Alerting: error rate > 5 %, p95 > 5s, daily volume drop > 50 %.
- Audit log: append-only, retention 10 rokov.
- Disaster recovery test: čo robíme pri 24-hodinovom výpadku CPDS?
- Runbook: kontakt na CPDS support, eskalačná matica.
Code sample — kompletný flow v Node.js
Zjednodušený end-to-end flow pre prijímanie a odosielanie:
import express from 'express';
import crypto from 'crypto';
import { Queue } from 'bullmq';
const app = express();
const queue = new Queue('cpds-invoices');
const CPDS_API = 'https://api.your-cpds.com';
const CPDS_TOKEN = process.env.CPDS_TOKEN;
const WEBHOOK_SECRET = process.env.CPDS_WEBHOOK_SECRET;
// Webhook handler
app.post('/webhooks/cpds', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.header('X-CPDS-Signature');
const event = req.header('X-CPDS-Event');
const deliveryId = req.header('X-CPDS-Delivery-Id');
// 1. Validate signature
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(req.body).digest('hex');
const provided = signature.replace('sha256=', '');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided))) {
return res.status(401).send('invalid signature');
}
// 2. Enqueue async processing
await queue.add('process-event', {
event,
deliveryId,
payload: JSON.parse(req.body.toString()),
}, {
jobId: deliveryId, // idempotency
attempts: 5,
backoff: { type: 'exponential', delay: 1000 },
});
// 3. Acknowledge fast
res.status(200).send('ok');
});
// Send invoice
async function sendInvoice(ublXml) {
// Validate first
const validation = await fetch(`${CPDS_API}/v1/invoices/validate`, {
method: 'POST',
headers: {
Authorization: `Bearer ${CPDS_TOKEN}`,
'Content-Type': 'application/xml',
},
body: ublXml,
});
const validationResult = await validation.json();
if (!validationResult.valid) {
throw new Error(`Validation failed: ${JSON.stringify(validationResult.errors)}`);
}
// Submit
const idempotencyKey = `invoice-${extractInvoiceId(ublXml)}`;
const submit = await fetch(`${CPDS_API}/v1/invoices`, {
method: 'POST',
headers: {
Authorization: `Bearer ${CPDS_TOKEN}`,
'Content-Type': 'application/xml',
'Idempotency-Key': idempotencyKey,
},
body: ublXml,
});
return await submit.json();
}
app.listen(3000);
Pre ďalšie čítanie
- Pre IT oddelenia — REST API, AS4 protokol, security — širší architektonický pohľad.
- Peppol BIS 3.0 vs UBL 2.1 — vysvetlenie — formát faktúry detailne.
- Ako prejsť na CPDS bez zmeny účtovného systému — middleware patterns.
- Kompletný checklist e-fakturácie 2027 — projektový pohľad.
- Slovník Peppol a CPDS pojmov — terminológia.
- Pre veľké podniky — SAP, Dynamics, batch processing — enterprise integrácie.