Dokladík API dokumentace
Kompletní reference REST API pro vystavování faktur, správu klientů a produktů. Verze v1, JSON, Bearer auth, 256-bit zabezpečení.
Production stable
Rate limit per klíč
Endpointů v M1
Základní informace
- Base URL:
https://dokladik.cz - Verze:
v1(všechny endpointy pod/v1/<resource>) - Formát: JSON (request body i response body)
- Encoding: UTF-8
- Datumy: ISO 8601 (
YYYY-MM-DDpro datum,YYYY-MM-DDTHH:MM:SS.sssZpro datetime) - Měny: ISO 4217 (
CZK,EUR,USDa další)
Co API umožňuje
- Vystavovat faktury (POST), získávat seznam i detail (GET), aktualizovat (PATCH), mazat drafty (DELETE)
- Stahovat PDF faktury jako binární data pro archivaci
- Označovat faktury jako zaplacené, odeslané, zrušené
- Odesílat faktury e-mailem klientovi (s vlastní zprávou nebo šablonou)
- CRUD operace nad klienty (synchronizace adresáře)
- CRUD operace nad produkty a službami (synchronizace katalogu)
- Test connection přes
GET /v1/ping
Autentizace
Bearer token
Každý request musí obsahovat hlavičku:
Authorization: Bearer dk_live_<64 znaků hex>Formát klíče
- Prefix:
dk_live_(8 znaků) - Random hex: 64 znaků (256-bit entropy z
crypto.randomBytes(32)) - Celkem: 72 znaků
Jak server validuje klíč (8 kroků)
- SHA-256 hash plaintext klíče
- Lookup v DB tabulce
api_keyspřes hash - Kontrola, že klíč není revokovaný (
revoked_at IS NULL) - Kontrola expirace (
expires_at IS NULL OR expires_at > NOW()) - Plan gate, feature
public_apimusí být povolená v tarifu Pro - Subscription lock check, organizace nesmí být
past_due,canceled,incompleteanitrial_expired - Scope check, klíč musí mít potřebné oprávnění pro daný endpoint (read, write, admin)
- Rate limit per-key (60 za minutu, 5000 za hodinu)
Bezpečnost
Pozor: Nikdy nezveřejňujte klíč v klientském kódu (browser bundle). API je primárně server-to-server. CORS hlavičky nejsou nastavené, volání z prohlížeče vrátí CORS chybu. Klíč držte v .env souboru nebo správci hesel a používejte ho jen z backendu.Scopes (oprávnění klíče)
| Scope | Oprávnění |
|---|---|
read | Čtení (GET endpointy). Pro účetní software, exporty, monitoring. |
write | Vytváření a úprava (POST, PATCH). Implicitně zahrnuje read. |
admin | Včetně mazání (DELETE). Implicitně zahrnuje read a write. |
Doporučené použití
- Účetní software: klíč jen s
read(čte faktury, klienty, produkty) - Rezervační systém / e-shop: klíč s
read + write(vystavuje faktury, označuje zaplacené) - Plně automatizovaná integrace: klíč s
admin(umí mazat draft doklady, smazat klienta nebo produkt)
Rate limity
| Bucket | Limit | Účel |
|---|---|---|
| Per API klíč | 60 req / minutu | Burst protection |
| Per API klíč | 5000 req / hodinu | Hodinová quota |
| Per organizace | 1000 POST /v1/invoices / hodinu | Spike protection |
| Per IP (failed auth) | 20 / minutu | Anti-enum, brute force |
| Per organizace (e-mail) | 50 / hodinu | Anti-spam protection |
Při překročení
- Status:
429 rate_limit_exceeded - Header:
Retry-After: <seconds> - Body obsahuje
error.code = "rate_limit_exceeded"arequest_id
Klient by měl při 429 počkat dle hlavičky Retry-After a pak request opakovat. Ideálně použít exponential backoff.
Stránkování (cursor-based)
List endpointy (GET /v1/invoices, GET /v1/clients, GET /v1/products) podporují cursor-based pagination.
Query parametry
?limit=N, max 250, default 50?cursor=<base64>, cursor z předchozí odpovědi
Tvar response
{
"data": [...],
"next_cursor": "eyJ0cyI6IjIwMjYtMDQtMjVUMTA6MDA6MDBaIiwiaWQiOiJ4eHgifQ",
"limit": 50
}next_cursor je null, pokud je to poslední stránka.
Inkrementální sync
Pro účetní systémy, které pravidelně stahují změny:
GET /v1/invoices?updated_since=2026-04-20T00:00:00Z&limit=250Vrátí jen faktury změněné od daného datetime. Doporučujeme synchronizovat každých 5 až 15 minut a držet si poslední timestamp lokálně.
Error formát
Tvar error response
{
"error": {
"code": "validation_failed",
"message": "Field 'items[0].quantity' must be > 0",
"details": [
{ "field": "items[0].quantity", "issue": "must_be_positive" }
]
},
"request_id": "req_a8f3d4c5b6e7f8a9",
"upgrade_required": "pro"
}request_id
- V každé odpovědi (success i error) v hlavičce
X-Request-Ida v body - Při kontaktu supportu uveďte request_id, najdeme váš request v logu během sekund
upgrade_required
- Jen u 403
feature_not_available - Hodnoty:
"start","pro"
Status kódy a kódy chyb
| HTTP | Code | Význam |
|---|---|---|
| 200 | - | OK (GET, PATCH) |
| 201 | - | Created (POST) |
| 400 | validation_failed | Zod validace selhala |
| 401 | unauthorized | Chybí, neplatný nebo expirovaný klíč |
| 402 | payment_required | Účet locked (problém s platbou) |
| 403 | feature_not_available | Vyžaduje vyšší tarif |
| 403 | insufficient_scope | Klíč nemá potřebný scope |
| 404 | not_found | Resource neexistuje |
| 409 | conflict | Duplicita (například IČO klienta) |
| 422 | business_rule_violation | Business pravidlo (uzamčené období, immutability) |
| 429 | rate_limit_exceeded | Příliš mnoho requestů, viz Retry-After |
| 500 | internal_error | Interní chyba, kontaktujte support s request_id |
Standardní hlavičky
Request
| Header | Hodnota | Povinný |
|---|---|---|
Authorization | Bearer dk_live_<64 hex> | Ano |
Content-Type | application/json | Ano (pro POST a PATCH) |
User-Agent | libovolná identifikace klienta | Doporučené |
Response
| Header | Hodnota |
|---|---|
X-Request-Id | req_<16 hex> |
Content-Type | application/json (kromě /pdf) |
Retry-After | <seconds> (jen u 429) |
Endpoint: GET /v1/ping
Účel: Test klíče a connection. Bez vedlejších efektů (žádný audit, žádný DB write).
- Scope:
read - Request: žádný body, žádné query params
Response 200
{
"ok": true,
"organization": {
"id": "uuid",
"name": "Moje firma s. r. o."
},
"scopes": ["read", "write"],
"server_time": "2026-04-25T10:00:00.000Z"
}Errors
401 unauthorized,402 payment_required,403 feature_not_available,429 rate_limit_exceeded,500 internal_error
Příklad volání
curl -i https://dokladik.cz/v1/ping \
-H "Authorization: Bearer dk_live_..."Endpoint: GET /v1/invoices (list faktur)
- Scope:
read - Stránkování: cursor-based, viz výše
Query parametry
| Param | Typ | Default | Popis |
|---|---|---|---|
status | string | - | draft, sent, paid, overdue, cancelled |
client_id | UUID | - | Filtr na konkrétního klienta |
issue_date_from | date | - | YYYY-MM-DD |
issue_date_to | date | - | YYYY-MM-DD |
due_date_from | date | - | YYYY-MM-DD |
due_date_to | date | - | YYYY-MM-DD |
paid_date_from | date | - | YYYY-MM-DD |
paid_date_to | date | - | YYYY-MM-DD |
currency | string | - | CZK, EUR, USD a další |
updated_since | datetime | - | ISO 8601, pro inkrementální sync |
search | string | - | Hledání v invoice_number (fulltext) |
limit | integer | 50 | Max 250 |
cursor | string | - | base64 cursor z předchozí odpovědi |
Response 200
{
"data": [
{
"id": "uuid",
"invoice_number": "2026-04-001",
"public_token": "...",
"status": "paid",
"invoice_type": "regular",
"currency": "CZK",
"exchange_rate": 1,
"issue_date": "2026-04-25",
"due_date": "2026-05-09",
"duzp": "2026-04-25",
"paid_date": "2026-04-25",
"sent_at": "2026-04-25T10:00:00Z",
"subtotal": 1239.67,
"vat_amount": 260.33,
"total": 1500.00,
"variable_symbol": "20260400001",
"constant_symbol": "0308",
"specific_symbol": null,
"order_number": null,
"client_id": "uuid",
"billing_name": null,
"billing_ico": null,
"billing_dic": null,
"payment_method": "transfer",
"reverse_charge": false,
"bank_account_id": "uuid",
"note": null,
"created_at": "2026-04-25T09:55:00Z",
"updated_at": "2026-04-25T10:00:00Z",
"clients": {
"id": "uuid",
"name": "Jan Klient",
"ico": "12345678",
"dic": "CZ12345678",
"email": "klient@firma.cz"
}
}
],
"next_cursor": "eyJ0cyI6Ii4uLiIs...",
"limit": 50
}Endpoint: POST /v1/invoices (vytvoření faktury)
- Scope:
write - Content-Type:
application/json
Body fields
| Field | Typ | Default | Povinný | Popis |
|---|---|---|---|---|
invoice_type | enum | "regular" | - | regular, proforma |
proforma_mode | enum | "completed" | - | completed, supplemented, ddpp, none |
client_id | UUID | null | - | Klient z /v1/clients |
issue_date | date | - | Ano | YYYY-MM-DD |
due_date | date | - | Ano | YYYY-MM-DD |
taxable_date | date | null | - | DUZP, fallback issue_date |
currency | string | "CZK" | - | CZK, EUR, USD a další |
exchange_rate | number | 1 | - | Kurz vůči CZK |
variable_symbol | string | auto | - | Auto-generated, 10 číslic |
order_number | string | null | - | Číslo objednávky |
specific_symbol | string | null | - | Specifický symbol |
constant_symbol | string | "0308" | - | Konstantní symbol |
note | string | null | - | Veřejná poznámka na faktuře |
invoice_info | string | null | - | Informace na faktuře |
internal_note | string | null | - | Interní (jen pro vás) |
payment_method | enum | "transfer" | - | transfer, cash |
reverse_charge | boolean | false | - | EU B2B reverse charge |
billing_name | string | null | - | Override jména na adrese |
billing_ico | string | null | - | Override IČO |
billing_dic | string | null | - | Override DIČ |
billing_street | string | null | - | Ulice |
billing_city | string | null | - | Město |
billing_zip | string | null | - | PSČ |
bank_account_id | UUID | null | - | Z bank účtů organizace |
items | array | - | Ano | Min 1 položka |
Položky faktury (každý prvek items[])
| Field | Typ | Default | Povinný | Popis |
|---|---|---|---|---|
product_id | UUID | null | - | Reference na /v1/products |
description | string | - | Ano | Min 1 znak |
quantity | number | - | Ano | >= 0 |
unit | string | "ks" | - | ks, h, kg, m a další |
unit_price | number | - | Ano | Bez DPH |
vat_rate | number | - | Ano | 0, 12, 21 |
sort_order | integer | 0 | - | Pořadí v PDF |
note | string | null | - | Poznámka k položce |
Příklad request body
{
"client_id": "abc-123",
"issue_date": "2026-04-25",
"due_date": "2026-05-09",
"currency": "CZK",
"items": [
{
"description": "Rezervace pokoje 25.4.",
"quantity": 1,
"unit": "ks",
"unit_price": 1239.67,
"vat_rate": 21
}
]
}Response 201
Vrátí kompletní fakturu (stejný shape jako GET detail) plus pole invoice_items[] a clients (embedded).
Errors
400 validation_failed, Zod selhal403 feature_not_available, limitinvoices_per_month(vyžaduje upgrade),proforma_invoices,multi_currency422 business_rule_violation, uzamčené účetní období429 rate_limit_exceeded, 1000 POST/hod/org500 internal_error, race condition po 5 retry
Side efekty
- Audit log (
invoice.createds metadatyapi_key_id,request_id) - API call log
- Revenue threshold check (fire-and-forget, neblokuje response)
Další /v1/invoices endpointy
GET /v1/invoices/:id (detail faktury)
- Scope:
read - Path param:
id(UUID) - Response 200: kompletní faktura plus
invoice_items[]plusclients(embedded objekt) - Errors: 401, 402, 403, 404, 500
PATCH /v1/invoices/:id (úprava faktury)
- Scope:
write - Body: stejné fields jako POST, ale všechny volitelné (částečný update)
Immutability rules
- Pokud
status === "draft", lze měnit cokoli kromě immutable fields - Pokud
statusjesent,paid,overdue,cancelled, lze měnit POUZE:status,paid_date,internal_note,sent_at - Pokus o změnu jiného pole na issued faktuře vrací
422 business_rule_violationsblocked_fields: [...]
Audit: invoice.updated s polem changes: [{ field, old, new }]
PATCH /v1/invoices/:id/status (změna stavu)
- Scope:
write
Body fields
| Field | Typ | Povinný | Popis |
|---|---|---|---|
status | enum | Ano | draft, sent, paid, overdue, cancelled |
paid_date | date | - | Pokud null a status=paid, použije se today |
sent_at | datetime | - | Pokud null a status=sent, použije se now |
Allowed transitions
| Z | Do |
|---|---|
draft | sent, paid, cancelled |
sent | paid, overdue, cancelled |
overdue | paid, cancelled |
paid | -, final stav |
cancelled | -, final stav |
Idempotence: pokud status === oldStatus, vrací 200 (no-op). Pro retry-safe webhooks.
Audit: invoice.marked_paid, invoice.marked_sent, invoice.cancelled, invoice.status_changed
GET /v1/invoices/:id/pdf (binární PDF)
- Scope:
read - Response 200:
Content-Type: application/pdf,Content-Disposition: attachment; filename="<invoice-number>.pdf",Cache-Control: private, max-age=300, binary PDF data - Side efekt:
pdf_downloaded_atse nastaví při prvním stažení (best effort) - Errors: 401, 402, 403, 404, 500
POST /v1/invoices/:id/send (odeslat e-mailem)
- Scope:
write
Body fields (volitelné)
| Field | Typ | Default | Popis |
|---|---|---|---|
emails | string[] | - | Pokud null, pošle na client.email. Max 10. |
custom_message | string | null | Vlastní zpráva, jinak šablona z Nastavení Emaily |
Příklad
{
"emails": ["klient@firma.cz", "uctarna@firma.cz"],
"custom_message": "Děkujeme za rezervaci. V příloze faktura, splatnost 14 dní."
}Response 200
{
"sent": true,
"recipients": ["klient@firma.cz", "uctarna@firma.cz"]
}Side efekty: sent_at se nastaví na now, status se změní z draft na sent (pokud byl draft), audit invoice.sent
Plan gate: numeric limit emails_per_month
Rate limit: 50/hod/org, 3/hod/recipient
Errors: 400 (žádné e-maily), 401, 402, 403, 404, 429, 500
DELETE /v1/invoices/:id (soft delete)
- Scope:
admin - Business rule: lze pouze pokud
status === "draft". Issued doklady nelze mazat. - Response 200:
{ "deleted": true, "id": "uuid" } - Errors: 401, 402, 403, 404, 422 (nebyl draft), 500
- Audit:
invoice.deleteds plným snapshotem
Endpointy /v1/clients (klienti, CRUD)
GET /v1/clients (list)
- Scope:
read
Query parametry
| Param | Typ | Default | Popis |
|---|---|---|---|
search | string | - | Hledání v name (ilike) |
updated_since | datetime | - | ISO 8601 |
limit | integer | 50 | Max 250 |
cursor | string | - | cursor z předchozí odpovědi |
Item shape (response data[])
{
"id": "uuid",
"name": "ACME s. r. o.",
"ico": "12345678",
"dic": "CZ12345678",
"is_vat_payer": true,
"email": "info@acme.cz",
"phone": "+420123456789",
"street": "Hlavní 1",
"city": "Praha",
"zip": "11000",
"country": "CZ",
"bank_account": null,
"entity_type": "sro",
"default_currency": "CZK",
"notes": null,
"created_at": "2026-04-25T10:00:00Z",
"updated_at": "2026-04-25T10:00:00Z"
}POST /v1/clients (create)
- Scope:
write
Body fields
| Field | Typ | Default | Povinný | Popis |
|---|---|---|---|---|
name | string | - | Ano | Min 1, max 255 |
ico | string | null | - | Unique per org (409 conflict při duplicitě) |
dic | string | null | - | DIČ |
is_vat_payer | boolean | false | - | Plátce DPH |
email | string | null | - | Validovaný e-mail |
phone | string | null | - | Telefon |
street | string | null | - | Ulice |
city | string | null | - | Město |
zip | string | null | - | PSČ |
country | string | "CZ" | - | ISO 3166-1 alpha-2 |
notes | string | null | - | Interní poznámka |
bank_account | string | null | - | Bankovní účet klienta |
entity_type | enum | null | - | osvc, sro, as, other |
default_currency | string | "CZK" | - | Default měna pro fakturaci |
Response 201: klient (stejný shape jako detail)
Errors: 400, 401, 402, 403, 409 (duplicate IČO), 500
Plan gate: numeric limit clients (Free a Start mají limity, Pro je unlimited)
GET /v1/clients/:id (detail)
Scope: read. Vrací full client object.
PATCH /v1/clients/:id (update)
Scope: write. Body: stejná pole jako POST, všechna volitelná. Audit client.updated.
DELETE /v1/clients/:id (soft delete)
Scope: admin. Audit client.deleted se snapshotem.
Endpointy /v1/products (produkty a služby, CRUD)
GET /v1/products (list)
- Scope:
read
Query parametry
| Param | Typ | Default | Popis |
|---|---|---|---|
type | enum | - | item, service, hourly |
active | boolean | true | Defaultně jen aktivní |
search | string | - | Hledání v name, sku, barcode |
updated_since | datetime | - | ISO 8601 |
limit | integer | 50 | Max 250 |
cursor | string | - | cursor z předchozí odpovědi |
Item shape
{
"id": "uuid",
"name": "Hodina konzultace",
"description": null,
"type": "hourly",
"unit": "h",
"unit_price": 1200,
"currency": "CZK",
"vat_rate": 21,
"is_active": true,
"sku": null,
"barcode": null,
"sort_order": 0,
"created_at": "...",
"updated_at": "..."
}POST /v1/products (create)
- Scope:
write
Body fields
| Field | Typ | Default | Povinný | Popis |
|---|---|---|---|---|
name | string | - | Ano | Min 1, max 255 |
description | string | null | - | Veřejný popis |
internal_note | string | null | - | Interní poznámka |
type | enum | "item" | - | item, service, hourly |
unit | string | "ks" | - | Jednotka |
unit_price | number | - | Ano | >= 0, bez DPH |
currency | string | "CZK" | - | Měna |
vat_rate | number | 21 | - | 0 až 100 |
is_active | boolean | true | - | Aktivní v katalogu |
sort_order | integer | 0 | - | Pořadí v katalogu |
sku | string | null | - | Skladová jednotka |
barcode | string | null | - | Čárový kód |
Plan gate: numeric limit products
GET /v1/products/:id (detail)
Scope: read.
PATCH /v1/products/:id (update)
Scope: write. Audit product.updated.
DELETE /v1/products/:id (soft delete)
Scope: admin. Audit product.deleted.
Příklad: Node.js / TypeScript (Express)
Kompletní wrapper a use-case rezervační systém po platbě. Funkční, kopírovatelný kód.
import fetch from "node-fetch";
const DOKLADIK_API_KEY = process.env.DOKLADIK_API_KEY!;
const API_BASE = "https://dokladik.cz/v1";
const HEADERS = {
"Authorization": `Bearer ${DOKLADIK_API_KEY}`,
"Content-Type": "application/json",
};
interface InvoiceItem {
description: string;
quantity: number;
unit_price: number;
vat_rate: 0 | 12 | 21;
unit?: string;
}
// === Klienti ===
async function findOrCreateClient(params: {
name: string;
email: string;
ico?: string;
}): Promise<{ id: string }> {
if (params.email) {
const r = await fetch(`${API_BASE}/clients?search=${encodeURIComponent(params.name)}`, { headers: HEADERS });
const list = await r.json();
const match = list.data.find((c: any) => c.email === params.email);
if (match) return match;
}
const r = await fetch(`${API_BASE}/clients`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(params),
});
if (!r.ok) throw new Error(`Create client failed: ${r.status}`);
return await r.json();
}
// === Faktura ===
async function createInvoice(params: {
client_id: string;
items: InvoiceItem[];
due_date: string;
}) {
const r = await fetch(`${API_BASE}/invoices`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
client_id: params.client_id,
issue_date: new Date().toISOString().slice(0, 10),
due_date: params.due_date,
items: params.items,
}),
});
if (!r.ok) {
const err = await r.json();
throw new Error(`Dokladík: ${err.error.code} - ${err.error.message}`);
}
return await r.json();
}
async function markPaid(invoiceId: string, paidDate?: string) {
const r = await fetch(`${API_BASE}/invoices/${invoiceId}/status`, {
method: "PATCH",
headers: HEADERS,
body: JSON.stringify({
status: "paid",
paid_date: paidDate?? new Date().toISOString().slice(0, 10),
}),
});
if (!r.ok) throw new Error("Mark paid failed");
return await r.json();
}
async function sendByEmail(invoiceId: string, options?: { emails?: string[]; message?: string }) {
const body: any = {};
if (options?.emails) body.emails = options.emails;
if (options?.message) body.custom_message = options.message;
const r = await fetch(`${API_BASE}/invoices/${invoiceId}/send`, {
method: "POST",
headers: HEADERS,
body: Object.keys(body).length > 0? JSON.stringify(body) : undefined,
});
if (!r.ok) throw new Error("Send failed");
return await r.json();
}
// === Use-case: rezervační systém po platbě ===
app.post("/reservation/paid", async (req, res) => {
const { reservation, customer, amount } = req.body;
const client = await findOrCreateClient({
name: customer.name,
email: customer.email,
ico: customer.ico,
});
const invoice = await createInvoice({
client_id: client.id,
due_date: new Date(Date.now() + 14*24*3600*1000).toISOString().slice(0,10),
items: [{
description: `Rezervace ${reservation.code} (${reservation.date})`,
quantity: 1,
unit_price: amount,
vat_rate: 21,
}],
});
await markPaid(invoice.id);
await sendByEmail(invoice.id, { message: "Děkujeme za rezervaci. Faktura v příloze." });
res.json({ ok: true, invoice_number: invoice.invoice_number });
});Příklad: PHP (curl, e-shop)
Wrapper přes curl_init() a vystavení faktury po objednávce.
<?php
function dokladik_request(string $method, string $path,?array $body = null): array {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://dokladik.cz/v1{$path}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . getenv('DOKLADIK_API_KEY'),
"Content-Type: application/json",
]);
if ($body!== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode >= 400) {
throw new \Exception("Dokladík: " . $data['error']['code'] . " - " . $data['error']['message']);
}
return $data;
}
// Po objednávce v e-shopu:
$invoice = dokladik_request('POST', '/invoices', [
'client_id' => $clientId,
'issue_date' => date('Y-m-d'),
'due_date' => date('Y-m-d', strtotime('+14 days')),
'items' => array_map(function($item) {
return [
'description' => $item['name'],
'quantity' => $item['qty'],
'unit_price' => $item['price'],
'vat_rate' => 21,
];
}, $orderItems),
]);
echo "Faktura vytvořena: " . $invoice['invoice_number'];Příklad: Python (rezervační aplikace, sync, PDF)
Wrapper pro requests, vystavení faktury, inkrementální sync a stažení PDF.
import os
import requests
from datetime import date, timedelta
API_KEY = os.environ["DOKLADIK_API_KEY"]
BASE = "https://dokladik.cz/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_invoice(client_id: str, items: list[dict], due_days: int = 14) -> dict:
payload = {
"client_id": client_id,
"issue_date": date.today().isoformat(),
"due_date": (date.today() + timedelta(days=due_days)).isoformat(),
"items": items,
}
r = requests.post(f"{BASE}/invoices", json=payload, headers=HEADERS)
r.raise_for_status()
return r.json()
def mark_paid(invoice_id: str, paid_date: str = None) -> dict:
payload = {"status": "paid", "paid_date": paid_date or date.today().isoformat()}
r = requests.patch(f"{BASE}/invoices/{invoice_id}/status", json=payload, headers=HEADERS)
r.raise_for_status()
return r.json()
def send_email(invoice_id: str, emails: list[str] = None, message: str = None) -> dict:
body = {}
if emails: body["emails"] = emails
if message: body["custom_message"] = message
r = requests.post(f"{BASE}/invoices/{invoice_id}/send", json=body, headers=HEADERS)
r.raise_for_status()
return r.json()
# Inkrementální sync pro účetní:
def sync_invoices_since(updated_since: str) -> list[dict]:
invoices = []
cursor = None
while True:
params = {"updated_since": updated_since, "limit": 250}
if cursor: params["cursor"] = cursor
r = requests.get(f"{BASE}/invoices", params=params, headers=HEADERS)
r.raise_for_status()
data = r.json()
invoices.extend(data["data"])
if not data["next_cursor"]:
break
cursor = data["next_cursor"]
return invoices
# Stáhnout PDF do souboru
def download_pdf(invoice_id: str, path: str) -> None:
r = requests.get(f"{BASE}/invoices/{invoice_id}/pdf", headers=HEADERS, stream=True)
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)Příklad: cURL, auto-vystavit a odeslat (sekvenční flow)
API nemá jeden vystaveno-plus-odesláno endpoint. Místo toho se volají 2 endpointy v sekvenci (záměrně).
# Krok 1: vystavit fakturu (vrátí id)
INVOICE_ID=$(curl -X POST https://dokladik.cz/v1/invoices \
-H "Authorization: Bearer dk_live_..." \
-H "Content-Type: application/json" \
-d '{
"client_id": "<uuid klienta>",
"issue_date": "2026-04-25",
"due_date": "2026-05-09",
"items": [{ "description": "Rezervace", "quantity": 1, "unit_price": 1500, "vat_rate": 21 }]
}' | jq -r '.id')
# Krok 2: ihned odeslat e-mailem
curl -X POST https://dokladik.cz/v1/invoices/$INVOICE_ID/send \
-H "Authorization: Bearer dk_live_..."Volitelné body pro /send
curl -X POST https://dokladik.cz/v1/invoices/$INVOICE_ID/send \
-H "Authorization: Bearer dk_live_..." \
-H "Content-Type: application/json" \
-d '{
"emails": ["klient@firma.cz", "uctarna@firma.cz"],
"custom_message": "Děkujeme za rezervaci. V příloze faktura, splatnost 14 dní."
}'Proč 2 endpointy a ne 1
- Flexibilita: vystavit teď, odeslat až za týden po dokončení služby
- Spolehlivost: pokud Postmark spadne, faktura už je vystavená, vidíte v UI a můžete poslat znovu
- Audit: každý krok má vlastní záznam (
invoice.created,invoice.sent) - Plan limity:
emails_per_monthse počítá zvlášť odinvoices_per_month
Ve vašem kódu si tato 2 volání zabalte do funkce createAndSend(), pak je to 1 řádek.
Příklad: cURL, plný flow rezervačního systému
Čtyři requesty, zhruba 500 ms celkem. Klient dostane fakturu už označenou jako zaplacenou plus děkovku.
# 1. Vyhledat nebo vytvořit klienta
CLIENT_ID=$(curl -X POST https://dokladik.cz/v1/clients \
-H "Authorization: Bearer dk_live_..." \
-d '{"name":"Jan Klient","email":"jan@klient.cz","ico":"12345678"}' \
| jq -r '.id')
# 2. Vystavit fakturu
INVOICE_ID=$(curl -X POST https://dokladik.cz/v1/invoices \
-H "Authorization: Bearer dk_live_..." \
-d "{
\"client_id\": \"$CLIENT_ID\",
\"issue_date\": \"2026-04-25\",
\"due_date\": \"2026-05-09\",
\"items\": [{\"description\": \"Rezervace\", \"quantity\": 1, \"unit_price\": 1500, \"vat_rate\": 21}]
}" | jq -r '.id')
# 3. Mark paid (po online platbě)
curl -X PATCH https://dokladik.cz/v1/invoices/$INVOICE_ID/status \
-H "Authorization: Bearer dk_live_..." \
-d '{"status": "paid", "paid_date": "2026-04-25"}'
# 4. Odeslat e-mailem
curl -X POST https://dokladik.cz/v1/invoices/$INVOICE_ID/send \
-H "Authorization: Bearer dk_live_..." \
-d '{"custom_message": "Děkujeme za platbu. Faktura v příloze."}'Inkrementální sync pro účetní software
# Stáhnout faktury změněné od poslední synchronizace
curl "https://dokladik.cz/v1/invoices?status=paid&updated_since=2026-04-20T00:00:00Z&limit=250" \
-H "Authorization: Bearer dk_live_..."
# Stáhnout PDF každé faktury pro archiv
for ID in $(...); do
curl "https://dokladik.cz/v1/invoices/$ID/pdf" \
-H "Authorization: Bearer dk_live_..." \
-o "archiv/$ID.pdf"
doneČasté otázky pro vývojáře
Šest nejčastějších technických dotazů.
Vytvořte si API klíč a začněte fakturovat automaticky
Registrace je zdarma. Klíč vytvoříte do 1 minuty. První fakturu vystavíte přes API do 1 hodiny vývoje.
Vytvořit účet a klíč