DoklaDík
v1 Production | Base URL: https://dokladik.cz/v1

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í.

Bearer auth
JSON UTF-8
Datum ISO 8601
Měna ISO 4217
v1

Production stable

0 req/h

Rate limit per klíč

0

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-DD pro datum, YYYY-MM-DDTHH:MM:SS.sssZ pro datetime)
  • Měny: ISO 4217 (CZK, EUR, USD a další)

Co API umožňuje

  1. Vystavovat faktury (POST), získávat seznam i detail (GET), aktualizovat (PATCH), mazat drafty (DELETE)
  2. Stahovat PDF faktury jako binární data pro archivaci
  3. Označovat faktury jako zaplacené, odeslané, zrušené
  4. Odesílat faktury e-mailem klientovi (s vlastní zprávou nebo šablonou)
  5. CRUD operace nad klienty (synchronizace adresáře)
  6. CRUD operace nad produkty a službami (synchronizace katalogu)
  7. 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ů)

  1. SHA-256 hash plaintext klíče
  2. Lookup v DB tabulce api_keys přes hash
  3. Kontrola, že klíč není revokovaný (revoked_at IS NULL)
  4. Kontrola expirace (expires_at IS NULL OR expires_at > NOW())
  5. Plan gate, feature public_api musí být povolená v tarifu Pro
  6. Subscription lock check, organizace nesmí být past_due, canceled, incomplete ani trial_expired
  7. Scope check, klíč musí mít potřebné oprávnění pro daný endpoint (read, write, admin)
  8. 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)

ScopeOprávnění
readČtení (GET endpointy). Pro účetní software, exporty, monitoring.
writeVytváření a úprava (POST, PATCH). Implicitně zahrnuje read.
adminVč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

BucketLimitÚčel
Per API klíč60 req / minutuBurst protection
Per API klíč5000 req / hodinuHodinová quota
Per organizace1000 POST /v1/invoices / hodinuSpike protection
Per IP (failed auth)20 / minutuAnti-enum, brute force
Per organizace (e-mail)50 / hodinuAnti-spam protection

Při překročení

  • Status: 429 rate_limit_exceeded
  • Header: Retry-After: <seconds>
  • Body obsahuje error.code = "rate_limit_exceeded" a request_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=250

Vrá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-Id a 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

HTTPCodeVýznam
200-OK (GET, PATCH)
201-Created (POST)
400validation_failedZod validace selhala
401unauthorizedChybí, neplatný nebo expirovaný klíč
402payment_requiredÚčet locked (problém s platbou)
403feature_not_availableVyžaduje vyšší tarif
403insufficient_scopeKlíč nemá potřebný scope
404not_foundResource neexistuje
409conflictDuplicita (například IČO klienta)
422business_rule_violationBusiness pravidlo (uzamčené období, immutability)
429rate_limit_exceededPříliš mnoho requestů, viz Retry-After
500internal_errorInterní chyba, kontaktujte support s request_id

Standardní hlavičky

Request

HeaderHodnotaPovinný
AuthorizationBearer dk_live_<64 hex>Ano
Content-Typeapplication/jsonAno (pro POST a PATCH)
User-Agentlibovolná identifikace klientaDoporučené

Response

HeaderHodnota
X-Request-Idreq_<16 hex>
Content-Typeapplication/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

ParamTypDefaultPopis
statusstring-draft, sent, paid, overdue, cancelled
client_idUUID-Filtr na konkrétního klienta
issue_date_fromdate-YYYY-MM-DD
issue_date_todate-YYYY-MM-DD
due_date_fromdate-YYYY-MM-DD
due_date_todate-YYYY-MM-DD
paid_date_fromdate-YYYY-MM-DD
paid_date_todate-YYYY-MM-DD
currencystring-CZK, EUR, USD a další
updated_sincedatetime-ISO 8601, pro inkrementální sync
searchstring-Hledání v invoice_number (fulltext)
limitinteger50Max 250
cursorstring-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

FieldTypDefaultPovinnýPopis
invoice_typeenum"regular"-regular, proforma
proforma_modeenum"completed"-completed, supplemented, ddpp, none
client_idUUIDnull-Klient z /v1/clients
issue_datedate-AnoYYYY-MM-DD
due_datedate-AnoYYYY-MM-DD
taxable_datedatenull-DUZP, fallback issue_date
currencystring"CZK"-CZK, EUR, USD a další
exchange_ratenumber1-Kurz vůči CZK
variable_symbolstringauto-Auto-generated, 10 číslic
order_numberstringnull-Číslo objednávky
specific_symbolstringnull-Specifický symbol
constant_symbolstring"0308"-Konstantní symbol
notestringnull-Veřejná poznámka na faktuře
invoice_infostringnull-Informace na faktuře
internal_notestringnull-Interní (jen pro vás)
payment_methodenum"transfer"-transfer, cash
reverse_chargebooleanfalse-EU B2B reverse charge
billing_namestringnull-Override jména na adrese
billing_icostringnull-Override IČO
billing_dicstringnull-Override DIČ
billing_streetstringnull-Ulice
billing_citystringnull-Město
billing_zipstringnull-PSČ
bank_account_idUUIDnull-Z bank účtů organizace
itemsarray-AnoMin 1 položka

Položky faktury (každý prvek items[])

FieldTypDefaultPovinnýPopis
product_idUUIDnull-Reference na /v1/products
descriptionstring-AnoMin 1 znak
quantitynumber-Ano>= 0
unitstring"ks"-ks, h, kg, m a další
unit_pricenumber-AnoBez DPH
vat_ratenumber-Ano0, 12, 21
sort_orderinteger0-Pořadí v PDF
notestringnull-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 selhal
  • 403 feature_not_available, limit invoices_per_month (vyžaduje upgrade), proforma_invoices, multi_currency
  • 422 business_rule_violation, uzamčené účetní období
  • 429 rate_limit_exceeded, 1000 POST/hod/org
  • 500 internal_error, race condition po 5 retry

Side efekty

  • Audit log (invoice.created s metadaty api_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[] plus clients (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 status je sent, 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_violation s blocked_fields: [...]

Audit: invoice.updated s polem changes: [{ field, old, new }]

PATCH /v1/invoices/:id/status (změna stavu)

  • Scope: write

Body fields

FieldTypPovinnýPopis
statusenumAnodraft, sent, paid, overdue, cancelled
paid_datedate-Pokud null a status=paid, použije se today
sent_atdatetime-Pokud null a status=sent, použije se now

Allowed transitions

ZDo
draftsent, paid, cancelled
sentpaid, overdue, cancelled
overduepaid, 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_at se 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é)

FieldTypDefaultPopis
emailsstring[]-Pokud null, pošle na client.email. Max 10.
custom_messagestringnullVlastní 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.deleted s plným snapshotem

Endpointy /v1/clients (klienti, CRUD)

GET /v1/clients (list)

  • Scope: read

Query parametry

ParamTypDefaultPopis
searchstring-Hledání v name (ilike)
updated_sincedatetime-ISO 8601
limitinteger50Max 250
cursorstring-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

FieldTypDefaultPovinnýPopis
namestring-AnoMin 1, max 255
icostringnull-Unique per org (409 conflict při duplicitě)
dicstringnull-DIČ
is_vat_payerbooleanfalse-Plátce DPH
emailstringnull-Validovaný e-mail
phonestringnull-Telefon
streetstringnull-Ulice
citystringnull-Město
zipstringnull-PSČ
countrystring"CZ"-ISO 3166-1 alpha-2
notesstringnull-Interní poznámka
bank_accountstringnull-Bankovní účet klienta
entity_typeenumnull-osvc, sro, as, other
default_currencystring"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

ParamTypDefaultPopis
typeenum-item, service, hourly
activebooleantrueDefaultně jen aktivní
searchstring-Hledání v name, sku, barcode
updated_sincedatetime-ISO 8601
limitinteger50Max 250
cursorstring-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

FieldTypDefaultPovinnýPopis
namestring-AnoMin 1, max 255
descriptionstringnull-Veřejný popis
internal_notestringnull-Interní poznámka
typeenum"item"-item, service, hourly
unitstring"ks"-Jednotka
unit_pricenumber-Ano>= 0, bez DPH
currencystring"CZK"-Měna
vat_ratenumber21-0 až 100
is_activebooleantrue-Aktivní v katalogu
sort_orderinteger0-Pořadí v katalogu
skustringnull-Skladová jednotka
barcodestringnull-Čá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_month se počítá zvlášť od invoices_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íč
14 dní zdarmaBez platební kartyČeský support do 4 hodin