Začínáme s API
Jak se přihlásit, poprvé zavolat API a zvládnout nejčastější scénáře. Všechny příklady jsou v curl, C#, JavaScriptu a TypeScriptu.
Úvod
API je dostupné na adrese https://{CompanyName}.api.ros.bclogia.cz, kde {CompanyName} je název vaší firmy (databáze). Podle této adresy API pozná, se kterou firmou pracujete.
Rozhraní jsou dvě, každé má vlastní dokumentaci a přihlášení:
| Rozhraní | Pro koho | Přihlášení | Dokumentace |
|---|---|---|---|
user | aplikace, ve kterých pracuje přihlášený uživatel | jméno a heslo, metoda auth/login | /doc/v1-user |
system | systémová integrace bez uživatele | OAuth2 client credentials (ClientID a ClientSecret) | /doc/v1-system |
Všechna volání se posílají přes HTTPS a v JSON. Příklady v C# předpokládají HttpClient http s nastaveným BaseAddress a hlavičkou Authorization z přihlášení, příklady v JavaScriptu a TypeScriptu konstanty BASE a headers.
Přihlášení – uživatel
Zavolejte POST /api/v1/auth/login s uživatelským jménem a heslem. Odpověď obsahuje přístupový token (access_token), obnovovací token (refresh_token) a platnost přístupového tokenu v sekundách (expires_in). Přístupový token posílejte v každém dalším volání v hlavičce Authorization: Bearer ….
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"Username":"jan.novak","Password":"tajne-heslo"}'
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
using var http = new HttpClient { BaseAddress = new Uri("https://{CompanyName}.api.ros.bclogia.cz") };
var response = await http.PostAsJsonAsync("/api/v1/auth/login",
new { Username = "jan.novak", Password = "tajne-heslo" });
response.EnsureSuccessStatusCode();
var tokens = await response.Content.ReadFromJsonAsync<JsonObject>();
var accessToken = (string)tokens!["access_token"]!;
var refreshToken = (string)tokens["refresh_token"]!;
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
const BASE = "https://{CompanyName}.api.ros.bclogia.cz";
const response = await fetch(`${BASE}/api/v1/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ Username: "jan.novak", Password: "tajne-heslo" }),
});
if (!response.ok) throw new Error(`Přihlášení selhalo (${response.status})`);
const tokens = await response.json();
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${tokens.access_token}`,
};
const BASE = "https://{CompanyName}.api.ros.bclogia.cz";
interface TokenResponse {
access_token: string;
refresh_token: string;
expires_in: number;
token_type: string;
scope: string;
}
const response = await fetch(`${BASE}/api/v1/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ Username: "jan.novak", Password: "tajne-heslo" }),
});
if (!response.ok) throw new Error(`Přihlášení selhalo (${response.status})`);
const tokens: TokenResponse = await response.json();
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer ${tokens.access_token}`,
};
Před vypršením přístupového tokenu ho vyměňte za nový voláním POST /api/v1/auth/refresh s tělem {"RefreshToken":"…"}. Odhlášení (POST /api/v1/auth/logout, stejné tělo) obnovovací token zneplatní.
Přihlášení – systém
Systémová integrace získá token z identity serveru metodou OAuth2 client credentials. Údaje client_id a client_secret vám přidělí správce. Kromě standardních polí pošlete i company – název firmy, pro kterou token vydáváte. Token platí pro rozhraní system (scope=ros).
curl -X POST "https://identity.bclogia.cz/connect/token" \
-d "grant_type=client_credentials" \
-d "client_id=MOJE_CLIENT_ID" \
-d "client_secret=MOJE_CLIENT_SECRET" \
-d "scope=ros" \
-d "company={CompanyName}"
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
using var identity = new HttpClient();
var tokenResponse = await identity.PostAsync("https://identity.bclogia.cz/connect/token",
new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "MOJE_CLIENT_ID",
["client_secret"] = "MOJE_CLIENT_SECRET",
["scope"] = "ros",
["company"] = "{CompanyName}",
}));
tokenResponse.EnsureSuccessStatusCode();
var token = (string)(await tokenResponse.Content.ReadFromJsonAsync<JsonObject>())!["access_token"]!;
using var http = new HttpClient { BaseAddress = new Uri("https://{CompanyName}.api.ros.bclogia.cz") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
const BASE = "https://{CompanyName}.api.ros.bclogia.cz";
const tokenResponse = await fetch("https://identity.bclogia.cz/connect/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "MOJE_CLIENT_ID",
client_secret: "MOJE_CLIENT_SECRET",
scope: "ros",
company: "{CompanyName}",
}),
});
if (!tokenResponse.ok) throw new Error(`Získání tokenu selhalo (${tokenResponse.status})`);
const { access_token } = await tokenResponse.json();
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${access_token}`,
};
const BASE = "https://{CompanyName}.api.ros.bclogia.cz";
const tokenResponse = await fetch("https://identity.bclogia.cz/connect/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "MOJE_CLIENT_ID",
client_secret: "MOJE_CLIENT_SECRET",
scope: "ros",
company: "{CompanyName}",
}),
});
if (!tokenResponse.ok) throw new Error(`Získání tokenu selhalo (${tokenResponse.status})`);
const { access_token } = (await tokenResponse.json()) as { access_token: string };
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer ${access_token}`,
};
client_secret nikdy neukládejte do kódu ani do webové aplikace v prohlížeči – token z identity serveru si vyžádejte na straně serveru.První volání: seznam s řazením a stránkováním
Seznamy se získávají voláním POST …/list (například POST /api/v1/company/list). V těle pošlete filtr a pole Skip a Take pro stránkování a Sorted pro řazení (název pole z odpovědi a směr Ascending nebo Descending).
Odpověď má tvar { "result": [...], "count": 123, "requestId": "…", "cached": true }: result jsou záznamy stránky, count celkový počet záznamů ve výběru.
Další strany: kdy se používá x-request-id
Hlavičku x-request-id používejte jen u seznamů, jejichž odpověď vedle requestId obsahuje i cached = true (například seznam firem). Takový seznam si API při prvním volání připraví a další strany, řazení i stránkování se pak provádějí nad připraveným výsledkem. U ostatních seznamů (cached = false) se každé volání vyhodnotí znovu a hlavičku nepoužívejte; stránkujte jen pomocí Skip a Take.
x-request-id se u připraveného seznamu vždy vrací první strana. Hodnota Skip se v takovém volání ignoruje a výsledek se připraví znovu. Chcete-li další strany, musíte poslat requestId z předchozí odpovědi v hlavičce x-request-id.Postup u připraveného seznamu: první volání (bez hlavičky) vrátí první stranu a requestId. Další strany vyžádejte se stejným výběrem, požadovaným Skip a hodnotou requestId v hlavičce x-request-id. Pokud platnost připraveného výsledku vypršela, API ho připraví znovu a vrátí nové requestId a informační zprávu v messages; v takovém případě je vrácená strana první a další strany pak žádejte s novým requestId.
# první strana
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/company/list" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"OrganizationName":"Novák","Skip":0,"Take":50,"Sorted":[{"Name":"OrganizationFullName","Direction":"Ascending"}]}'
# druhá strana: stejné tělo, jiné Skip a requestId z první odpovědi
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/company/list" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-H "x-request-id: REQUEST_ID" \
-d '{"OrganizationName":"Novák","Skip":50,"Take":50,"Sorted":[{"Name":"OrganizationFullName","Direction":"Ascending"}]}'
using System.Net.Http.Json;
using System.Text.Json.Nodes;
var filter = new
{
OrganizationName = "Novák",
Take = 50,
Sorted = new[] { new { Name = "OrganizationFullName", Direction = "Ascending" } },
};
string? requestId = null;
for (var skip = 0; ; skip += 50)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/company/list")
{
Content = JsonContent.Create(new { filter.OrganizationName, Skip = skip, filter.Take, filter.Sorted }),
};
if (requestId is not null) request.Headers.Add("x-request-id", requestId);
var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var page = (await response.Content.ReadFromJsonAsync<JsonObject>())!;
requestId = (string?)page["requestId"];
var records = page["result"]!.AsArray();
foreach (var record in records) Console.WriteLine(record!["OrganizationFullName"]);
if (skip + records.Count >= (int)page["count"]!) break;
}
const take = 50;
let requestId = null;
for (let skip = 0; ; skip += take) {
const response = await fetch(`${BASE}/api/v1/company/list`, {
method: "POST",
headers: requestId ? { ...headers, "x-request-id": requestId } : headers,
body: JSON.stringify({
OrganizationName: "Novák",
Skip: skip,
Take: take,
Sorted: [{ Name: "OrganizationFullName", Direction: "Ascending" }],
}),
});
if (!response.ok) throw new Error(`Seznam selhal (${response.status})`);
const page = await response.json();
requestId = page.requestId;
page.result.forEach((c) => console.log(c.OrganizationFullName));
if (skip + page.result.length >= page.count) break;
}
interface Page<T> {
result: T[];
count: number;
requestId: string;
cached: boolean;
messages: { code: string; message: string }[] | null;
}
interface CompanyRow {
CompanyId: number;
OrganizationFullName: string;
CompanyRegistrationNumber: string;
}
interface Sort {
Name: string;
Direction: "Ascending" | "Descending";
}
const take = 50;
let requestId: string | null = null;
for (let skip = 0; ; skip += take) {
const response: Response = await fetch(`${BASE}/api/v1/company/list`, {
method: "POST",
headers: requestId ? { ...headers, "x-request-id": requestId } : headers,
body: JSON.stringify({
OrganizationName: "Novák",
Skip: skip,
Take: take,
Sorted: [{ Name: "OrganizationFullName", Direction: "Ascending" }] satisfies Sort[],
}),
});
if (!response.ok) throw new Error(`Seznam selhal (${response.status})`);
const page: Page<CompanyRow> = await response.json();
requestId = page.requestId;
page.result.forEach((c) => console.log(c.OrganizationFullName));
if (skip + page.result.length >= page.count) break;
}
Ostatní seznamy (city, street, address, country, …) se volají stejně. Zda u nich hlavičku x-request-id použít, poznáte podle pole cached v odpovědi (viz výše); popis je i u příslušné metody v dokumentaci.
Synchronizace změn firem (systém)
Chcete-li mít kopii firem ve svém systému, stahujte jen změny. Postup tvoří dvě volání:
POST /api/v1/system/company/list_changedvrátí dávku změněných firem seřazenou podle verze (RowVersion). První dávku vyžádejte bezLastRv; do dalšího požadavku pošleteRowVersionz posledního záznamu předchozí dávky. Prázdný seznam znamená, že už žádné další změny nejsou. Velikost dávky (BatchSize) je 1 až 100, výchozí 20.POST /api/v1/system/company/read_batchvrátí detail firem podle seznamuIDS(nejvýše 100 ID).
Zrušená firma se vrací se Deleted = true; je-li nahrazena jinou, je v ReplacedById její ID. Poslední RowVersion si uložte, příště se od něj začíná.
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/system/company/list_changed" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"LastRv":null,"BatchSize":100}'
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/system/company/read_batch" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"IDS":[101,102,103]}'
using System.Net.Http.Json;
using System.Text.Json.Nodes;
string? lastRv = null; // z posledního běhu; při prvním běhu null
while (true)
{
var changedResponse = await http.PostAsJsonAsync("/api/v1/system/company/list_changed",
new { LastRv = lastRv, BatchSize = 100 });
changedResponse.EnsureSuccessStatusCode();
var changed = (await changedResponse.Content.ReadFromJsonAsync<JsonArray>())!;
if (changed.Count == 0) break; // hotovo
var ids = changed.Where(c => !(bool)c!["Deleted"]!).Select(c => (int)c!["CompanyId"]!).ToArray();
if (ids.Length > 0)
{
var detailResponse = await http.PostAsJsonAsync("/api/v1/system/company/read_batch", new { IDS = ids });
detailResponse.EnsureSuccessStatusCode();
var details = (await detailResponse.Content.ReadFromJsonAsync<JsonArray>())!;
// ... uložte details do svého systému
}
// zrušené firmy (Deleted, případně ReplacedById) zpracujte podle záznamů v changed
lastRv = (string)changed[^1]!["RowVersion"]!; // uložte si ho i trvale
}
let lastRv = null; // z posledního běhu; při prvním běhu null
for (;;) {
const changedResponse = await fetch(`${BASE}/api/v1/system/company/list_changed`, {
method: "POST",
headers,
body: JSON.stringify({ LastRv: lastRv, BatchSize: 100 }),
});
if (!changedResponse.ok) throw new Error(`list_changed selhal (${changedResponse.status})`);
const changed = await changedResponse.json();
if (changed.length === 0) break; // hotovo
const ids = changed.filter((c) => !c.Deleted).map((c) => c.CompanyId);
if (ids.length > 0) {
const detailResponse = await fetch(`${BASE}/api/v1/system/company/read_batch`, {
method: "POST",
headers,
body: JSON.stringify({ IDS: ids }),
});
const details = await detailResponse.json();
// ... uložte details do svého systému
}
// zrušené firmy (Deleted, případně ReplacedById) zpracujte podle záznamů v changed
lastRv = changed[changed.length - 1].RowVersion; // uložte si ho i trvale
}
interface ChangedCompany {
CompanyId: number;
RowVersion: string;
Invalidated: boolean;
ReplacedById: number | null;
Deleted: boolean;
}
let lastRv: string | null = null; // z posledního běhu; při prvním běhu null
for (;;) {
const changedResponse: Response = await fetch(`${BASE}/api/v1/system/company/list_changed`, {
method: "POST",
headers,
body: JSON.stringify({ LastRv: lastRv, BatchSize: 100 }),
});
if (!changedResponse.ok) throw new Error(`list_changed selhal (${changedResponse.status})`);
const changed: ChangedCompany[] = await changedResponse.json();
if (changed.length === 0) break; // hotovo
const ids = changed.filter((c) => !c.Deleted).map((c) => c.CompanyId);
if (ids.length > 0) {
const detailResponse = await fetch(`${BASE}/api/v1/system/company/read_batch`, {
method: "POST",
headers,
body: JSON.stringify({ IDS: ids }),
});
const details: Record<string, unknown>[] = await detailResponse.json();
// ... uložte details do svého systému
}
// zrušené firmy (Deleted, případně ReplacedById) zpracujte podle záznamů v changed
lastRv = changed[changed.length - 1].RowVersion; // uložte si ho i trvale
}
Zakládání a změna firem (uživatel)
1. Nejdřív dohledejte údaje
Při zakládání firmy API nic nedohledává; všechny hodnoty musíte předat již zjištěné. Před založením proto zavolejte:
POST /api/v1/address/list– vyhledání adresy; ze záznamu vezměteAddressCode.POST /api/v1/address/geography/resolvesAddressCodea typem místa vrátíAddressId,CityIdaCityStreetId(neboAddressFound = false).POST /api/v1/company/headquarters/lista…/premises/listsCompanyRegistrationNumbervrátí sídla a provozovny podle IČ;GET /api/v1/company/premises/max-numberčíslo poslední provozovny.
2. Založení podle typu firmy
| Metoda | Typ firmy |
|---|---|
POST /api/v1/company/create/company | podnikající, s IČ |
POST /api/v1/company/create/company-noid | podnikající, bez IČ |
POST /api/v1/company/create/foreign | podnikající, zahraniční subjekt |
POST /api/v1/company/create/municipality | obec |
POST /api/v1/company/create/citizen | nepodnikající, občan |
POST /api/v1/company/create/unknown | bez rozlišení typu (vyžaduje zvláštní oprávnění) |
Odpověď (201) obsahuje celý záznam založené firmy včetně jejího CompanyId. Sídlo se zakládá s prázdným nebo nulovým číslem provozovny; u provozovny zadejte její číslo a identifikátor organizace sídla (ParentOrganization).
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/company/create/company" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{
"CompanyRegistrationNumber": "12345678",
"OrganizationName": "Novák a syn s.r.o.",
"VatNumber": "CZ12345678",
"WinyxAddressId": 1001,
"CityId": 55,
"CityStreetId": 3021,
"PostalCode": "11000",
"HouseNumber": "12"
}'
using System.Net.Http.Json;
using System.Text.Json.Nodes;
var response = await http.PostAsJsonAsync("/api/v1/company/create/company", new
{
CompanyRegistrationNumber = "12345678",
OrganizationName = "Novák a syn s.r.o.",
VatNumber = "CZ12345678",
WinyxAddressId = 1001, // AddressId z address/geography/resolve
CityId = 55,
CityStreetId = 3021,
PostalCode = "11000",
HouseNumber = "12",
});
if (response.IsSuccessStatusCode)
{
var company = (await response.Content.ReadFromJsonAsync<JsonObject>())!;
Console.WriteLine($"Založena firma {company["CompanyId"]}");
}
// 409 s ConfirmationRequired viz kapitola Chyby a potvrzení
const response = await fetch(`${BASE}/api/v1/company/create/company`, {
method: "POST",
headers,
body: JSON.stringify({
CompanyRegistrationNumber: "12345678",
OrganizationName: "Novák a syn s.r.o.",
VatNumber: "CZ12345678",
WinyxAddressId: 1001, // AddressId z address/geography/resolve
CityId: 55,
CityStreetId: 3021,
PostalCode: "11000",
HouseNumber: "12",
}),
});
if (response.ok) {
const company = await response.json();
console.log(`Založena firma ${company.CompanyId}`);
}
// 409 s ConfirmationRequired viz kapitola Chyby a potvrzení
interface NewCompany {
CompanyRegistrationNumber: string;
OrganizationName: string;
VatNumber?: string;
WinyxAddressId?: number;
CityId?: number;
CityStreetId?: number;
PostalCode?: string;
HouseNumber?: string;
}
const newCompany: NewCompany = {
CompanyRegistrationNumber: "12345678",
OrganizationName: "Novák a syn s.r.o.",
VatNumber: "CZ12345678",
WinyxAddressId: 1001, // AddressId z address/geography/resolve
CityId: 55,
CityStreetId: 3021,
PostalCode: "11000",
HouseNumber: "12",
};
const response = await fetch(`${BASE}/api/v1/company/create/company`, {
method: "POST",
headers,
body: JSON.stringify(newCompany),
});
if (response.ok) {
const company: { CompanyId: number } = await response.json();
console.log(`Založena firma ${company.CompanyId}`);
}
// 409 s ConfirmationRequired viz kapitola Chyby a potvrzení
3. Změna a odstranění
- Změna je vždy úplná (
PUT), ne částečná. Firmu načtěte (GET /api/v1/company?CompanyId=…), upravte hodnoty a odešlete celý záznam naPUT /api/v1/company?CompanyId=…. Pole, která neodešlete, se vymažou. - Odstranění je nahrazení jinou firmou:
DELETE /api/v1/company?CompanyId=…&ReplacementCompanyId=…. GET /api/v1/company/exists?CompanyId=…ověří, zda firma existuje.
# načtení, úprava (v souboru firma.json) a odeslání celého záznamu
curl "https://{CompanyName}.api.ros.bclogia.cz/api/v1/company?CompanyId=101" \
-H "Authorization: Bearer ACCESS_TOKEN" -o firma.json
curl -X PUT "https://{CompanyName}.api.ros.bclogia.cz/api/v1/company?CompanyId=101" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
--data-binary @firma.json
using System.Net.Http.Json;
using System.Text.Json.Nodes;
var company = (await http.GetFromJsonAsync<JsonObject>("/api/v1/company?CompanyId=101"))!;
company["Note"] = "Nová poznámka"; // upravte, co potřebujete
var response = await http.PutAsJsonAsync("/api/v1/company?CompanyId=101", company); // celý záznam
response.EnsureSuccessStatusCode();
const getResponse = await fetch(`${BASE}/api/v1/company?CompanyId=101`, { headers });
const company = await getResponse.json();
company.Note = "Nová poznámka"; // upravte, co potřebujete
const response = await fetch(`${BASE}/api/v1/company?CompanyId=101`, {
method: "PUT",
headers,
body: JSON.stringify(company), // celý záznam
});
if (!response.ok) throw new Error(`Změna selhala (${response.status})`);
type Company = { CompanyId: number } & Record<string, unknown>;
const getResponse = await fetch(`${BASE}/api/v1/company?CompanyId=101`, { headers });
const company: Company = await getResponse.json();
company.Note = "Nová poznámka"; // upravte, co potřebujete
const response = await fetch(`${BASE}/api/v1/company?CompanyId=101`, {
method: "PUT",
headers,
body: JSON.stringify(company), // celý záznam
});
if (!response.ok) throw new Error(`Změna selhala (${response.status})`);
Chyby a potvrzení
Chyby mají vždy stejný tvar (application/problem+json). Pole code slouží ke zpracování ve vaší aplikaci, title a detail můžete zobrazit uživateli, traceId uveďte při hlášení problému. U neplatných dat jsou v issues jednotlivé chyby i s polem, kterého se týkají (field).
| Stav | Význam | Co dělat |
|---|---|---|
400 | neplatný požadavek nebo data | opravte data podle issues; potvrdit to nelze |
401 | chybí nebo je neplatný token | přihlaste se znovu nebo obnovte token |
403 | chybí oprávnění | požádejte správce o oprávnění |
404 | záznam neexistuje | zkontrolujte identifikátor |
409 | záznam už existuje, nelze odstranit, nebo se vyžaduje potvrzení | viz níže |
500 | chyba na straně serveru | zkuste to znovu; při opakování nahlaste traceId |
Potvrzení upozornění
Některé operace (založení nebo změna firmy) najdou upozornění, které může uživatel potvrdit – například jinou firmu se stejným IČ nebo DIČ. Operace se pak neprovede a API vrátí 409 s kódem ConfirmationRequired:
{
"status": 409,
"code": "ConfirmationRequired",
"warnings": [ { "code": "DuplicateRegistrationNumber", "text": "Firma se stejným IČ už existuje." } ],
"confirmationToken": "…",
"confirmationTokenExpiresAt": "2026-10-07T12:30:00Z"
}
- Zobrazte
warningsuživateli. - Pokud souhlasí, pošlete úplně stejný požadavek znovu a token přidejte do hlavičky
X-Confirmation-Token. - Token platí omezenou dobu (standardně 10 minut), jen pro tato data a jen pro vás. Pokud se data změní nebo se najde další upozornění, API vrátí znovu
409s novým tokenem – postup opakujte.
# stejný požadavek jako původně, navíc hlavička s tokenem
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/company/create/company" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-H "X-Confirmation-Token: CONFIRMATION_TOKEN" \
-d @novafirma.json
using System.Net;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
async Task<HttpResponseMessage> CreateAsync(JsonObject body, Func<JsonArray, bool> userConfirms)
{
string? token = null;
while (true)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/company/create/company")
{
Content = JsonContent.Create(body),
};
if (token is not null) request.Headers.Add("X-Confirmation-Token", token);
var response = await http.SendAsync(request);
if (response.StatusCode != HttpStatusCode.Conflict) return response;
var error = (await response.Content.ReadFromJsonAsync<JsonObject>())!;
if ((string?)error["code"] != "ConfirmationRequired") return response; // jiný 409
if (!userConfirms(error["warnings"]!.AsArray())) return response; // uživatel nesouhlasí
token = (string)error["confirmationToken"]!; // opakujte se stejnými daty
}
}
async function createCompany(body, userConfirms) {
let token = null;
for (;;) {
const response = await fetch(`${BASE}/api/v1/company/create/company`, {
method: "POST",
headers: token ? { ...headers, "X-Confirmation-Token": token } : headers,
body: JSON.stringify(body),
});
if (response.status !== 409) return response;
const error = await response.json();
if (error.code !== "ConfirmationRequired") return response; // jiný 409
if (!(await userConfirms(error.warnings))) return response; // uživatel nesouhlasí
token = error.confirmationToken; // opakujte se stejnými daty
}
}
interface ApiWarning {
code: string;
text: string;
}
interface ApiError {
code?: string;
warnings?: ApiWarning[];
confirmationToken?: string;
}
async function createCompany(
body: object,
userConfirms: (warnings: ApiWarning[]) => Promise<boolean>,
): Promise<Response> {
let token: string | null = null;
for (;;) {
const response: Response = await fetch(`${BASE}/api/v1/company/create/company`, {
method: "POST",
headers: token ? { ...headers, "X-Confirmation-Token": token } : headers,
body: JSON.stringify(body),
});
if (response.status !== 409) return response;
const error: ApiError = await response.json();
if (error.code !== "ConfirmationRequired") return response; // jiný 409
if (!(await userConfirms(error.warnings ?? []))) return response; // uživatel nesouhlasí
token = error.confirmationToken ?? null; // opakujte se stejnými daty
}
}
Import kalkulací (uživatel)
POST /api/v1/calculation/import naimportuje kalkulaci jednoho provozu: hlavičku a tři bloky řádků (WasteRows – odpady, SecondaryRows – druhotné suroviny, ServiceRows – ostatní služby). Počet řádků není omezen.
- Povinné je IČ zákazníka (8 číslic) a období od–do. U řádků odpadů a druhotných surovin je povinné
WasteIDa odpad s tímto ID musí existovat. - Kalkulace se identifikuje IČ, obdobím, divizí a provozem. Pokud už taková existuje, nahradí se celá (
200,replaced = true); jinak se založí nová (201). Při chybě zůstane původní kalkulace beze změny. - Ukládají se jen zadané hodnoty; součty, náklady, tržby ani marže se nepřepočítávají.
- Číslo řádku (
RowNo) je nepovinné; bez něj se řádky číslují podle pořadí.
curl -X POST "https://{CompanyName}.api.ros.bclogia.cz/api/v1/calculation/import" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{
"CompanyRegistrationNumber": "12345678",
"PeriodFrom": "2026-01-01",
"PeriodTo": "2026-12-31",
"Abc": "B",
"Division": "Čechy",
"Plant": "Pardubice",
"WasteRows": [
{ "WasteID": 1503, "ActivityCode": 110, "Unit": "t", "QtyPerPickup": 0.1, "PickupsPerPeriod": 12, "DisposalPerUnit": 3295, "RevPerUnit": 13650 }
],
"SecondaryRows": [],
"ServiceRows": [
{ "Item": "Školení", "Unit": "ks", "QtyPerPeriod": 2, "CostPerUnit": 1200, "RevPerUnit": 2000 }
]
}'
using System.Net.Http.Json;
using System.Text.Json.Nodes;
var response = await http.PostAsJsonAsync("/api/v1/calculation/import", new
{
CompanyRegistrationNumber = "12345678",
PeriodFrom = "2026-01-01",
PeriodTo = "2026-12-31",
Abc = "B",
Division = "Čechy",
Plant = "Pardubice",
WasteRows = new[]
{
new { WasteID = 1503, ActivityCode = 110, Unit = "t", QtyPerPickup = 0.1m, PickupsPerPeriod = 12m, DisposalPerUnit = 3295m, RevPerUnit = 13650m },
},
SecondaryRows = Array.Empty<object>(),
ServiceRows = new[]
{
new { Item = "Školení", Unit = "ks", QtyPerPeriod = 2m, CostPerUnit = 1200m, RevPerUnit = 2000m },
},
});
response.EnsureSuccessStatusCode();
var result = (await response.Content.ReadFromJsonAsync<JsonObject>())!;
Console.WriteLine($"Kalkulace {result["CalculationId"]}, nahrazena: {result["Replaced"]}");
const response = await fetch(`${BASE}/api/v1/calculation/import`, {
method: "POST",
headers,
body: JSON.stringify({
CompanyRegistrationNumber: "12345678",
PeriodFrom: "2026-01-01",
PeriodTo: "2026-12-31",
Abc: "B",
Division: "Čechy",
Plant: "Pardubice",
WasteRows: [
{ WasteID: 1503, ActivityCode: 110, Unit: "t", QtyPerPickup: 0.1, PickupsPerPeriod: 12, DisposalPerUnit: 3295, RevPerUnit: 13650 },
],
SecondaryRows: [],
ServiceRows: [
{ Item: "Školení", Unit: "ks", QtyPerPeriod: 2, CostPerUnit: 1200, RevPerUnit: 2000 },
],
}),
});
if (!response.ok) throw new Error(`Import selhal (${response.status})`);
const result = await response.json();
console.log(`Kalkulace ${result.CalculationId}, nahrazena: ${result.Replaced}`);
interface WasteRow {
WasteID: number;
ActivityCode?: number;
Unit?: string;
QtyPerPickup?: number;
PickupsPerPeriod?: number;
DisposalPerUnit?: number;
RevPerUnit?: number;
}
interface ServiceRow {
Item?: string;
Unit?: string;
QtyPerPeriod?: number;
CostPerUnit?: number;
RevPerUnit?: number;
}
interface CalculationImport {
CompanyRegistrationNumber: string;
PeriodFrom: string; // yyyy-mm-dd
PeriodTo: string;
Abc?: "A" | "B" | "C";
Division?: string;
Plant?: string;
WasteRows: WasteRow[];
SecondaryRows: object[];
ServiceRows: ServiceRow[];
}
const calculation: CalculationImport = {
CompanyRegistrationNumber: "12345678",
PeriodFrom: "2026-01-01",
PeriodTo: "2026-12-31",
Abc: "B",
Division: "Čechy",
Plant: "Pardubice",
WasteRows: [
{ WasteID: 1503, ActivityCode: 110, Unit: "t", QtyPerPickup: 0.1, PickupsPerPeriod: 12, DisposalPerUnit: 3295, RevPerUnit: 13650 },
],
SecondaryRows: [],
ServiceRows: [
{ Item: "Školení", Unit: "ks", QtyPerPeriod: 2, CostPerUnit: 1200, RevPerUnit: 2000 },
],
};
const response = await fetch(`${BASE}/api/v1/calculation/import`, {
method: "POST",
headers,
body: JSON.stringify(calculation),
});
if (!response.ok) throw new Error(`Import selhal (${response.status})`);
const result: { CalculationId: number; Replaced: boolean } = await response.json();
console.log(`Kalkulace ${result.CalculationId}, nahrazena: ${result.Replaced}`);
Popis všech polí najdete v dokumentaci rozhraní user.