ReČeK API pro nakladatele

ReČeK · Registr čísel knih · API pro nakladatele

Založení titulu, produktu a přidělení ISBN přes API

Návod pro externí systém nakladatele (ERP, redakční systém). Popisuje, jak se přihlásit, jak načíst číselníky, najít nebo založit autora, nahrát obálku, založit titul a produkt a nakonec produktu přidělit ISBN nebo ISMN z rozsahu nakladatele.

Prostředí
FOLIO Eureka (Kong API gateway + Keycloak)
Role uživatele
Garant nakladatele (PublisherGuarantor)
Ověřeno proti
mod-br-agency 1.3.171, lokální prostředí eureka-local, 23. 9. 2026
ověřeno volání proběhlo proti běžícímu systému, ukázky jsou skutečné odpovědi z kódu popsáno podle zdrojového kódu, živě neověřeno

Úvod a konvence

Všechna volání jdou přes API gateway na jednu adresu. V příkladech se používají tři proměnné prostředí, které si nastavíte jednou:

export BASE_URL="{{BASE_URL}}"     # adresa API gateway, např. https://api.example.cz
export TENANT="{{TENANT}}"         # identifikátor tenanta, lokálně "tritius"
export TOKEN="…"                   # access token z přihlášení (kapitola 1)

Na lokálním testovacím prostředí platí BASE_URL=http://localhost:8000 a TENANT=tritius. Hodnoty pro testovací a produkční prostředí vám sdělí agentura.

  • Cesty jsou bez prefixu modulu – volá se přímo $BASE_URL/titles, $BASE_URL/products atd.
  • Tělo požadavků i odpovědí je JSON v UTF-8 (Content-Type: application/json), kromě nahrání obálky (multipart).
  • Identifikátory jsou UUID. Data vydání se zadávají jako text RRRR, MM.RRRR nebo DD.MM.RRRR.
  • ID z ukázek (UUID) pocházejí z testovacího prostředí. ID číselníkových položek se mezi prostředími mohou lišit – vždy je načtěte z API (kapitola 3), nekódujte je napevno.
  • Seznamové endpointy vrací {"items":[…],"totalRecords":N} a berou parametry query (CQL), offset a limit.

Referenční dokumentace (Swagger)

Úplná schémata všech endpointů a objektů najdete ve veřejném Swagger UI: recek-swagger.tritius.cz. Tento dokument je průvodce postupem – schémata neopakuje, ale doplňuje, co Swagger neříká: pořadí volání, skutečně povinná pole a reálné chybové odpovědi.

SpecifikaceObsahSoubor
agency-1.3.171tituly, produkty, čísla, osoby, číselníky, obálky, prefixyagency-1.3.171.json
login-keycloak-3.0.0přihlášení, obnova tokenu, odhlášenílogin-keycloak-3.0.0.json
users-1.2.95přihlášený uživatel, výběr organizaceusers-1.2.95.json
publishers-1.4.97evidence nakladatelů, potvrzení údajůpublishers-1.4.97.json

U každého endpointu v tomto dokumentu je odkaz Swagger · specifikace · operace ↗. Swagger UI se otevírá s jinou výchozí specifikací: v horní liště vyberte uvedenou specifikaci (Select a definition) a operaci najděte podle jejího názvu.

Povinná pole ve Swaggeru jsou neúplná

Swagger označuje jako povinné jen Title.owner, Product.title, ProductNumberAssignRequest.productId + publisherId, NaturalPerson.legalForm a TitleSupportingResource.link + type + mode. Skutečná validace se liší:

  • Titul: navíc právě jeden hlavní název (names[].type.value = "01") s vyplněným name; u autorů sequenceNumber, jméno a buď person, nebo unnamedPerson; u nakladatelů role.
  • Produkt: navíc title.version, title.owner.id, primaryContentType, form, composition, publishingStatus; u rozsahu unit, u ceny currency.
  • Obálka: link označený jako povinný při nahraném souboru posílat nemusíte – server ho odvodí ze storageKey.
  • Číselníkové položky: Swagger je popisuje jako celý objekt; ve skutečnosti stačí, ale musí být, id + value + version.

Podrobně v tabulkách polí u titulu, produktu a čísla produktu.

Přehled workflow

Pořadí kroků odpovídá tomu, co dělá webové rozhraní nakladatelského portálu. Kroky s přerušovaným kroužkem jsou potřeba jen někdy.

  1. Přihlášení
    POST /authn/login-with-expiry
    Access token (platí 1 h) a refresh token (24 h).
  2. Zjištění vlastního nakladatele
    GET /br-users/_self → GET /agency-publishers/by-source-id/{sourceId}
    Výsledné id je vlastník titulu (owner.id).
  3. Kontrola předpokladů pro ISBN
    GET /product-numbers/assign/feasibility/{titleId} · POST /publishers/{id}/confirmData · POST /publisher-prefixes/{id}/last-used-number
    Potvrzení údajů nakladatele (1× ročně) a prohlášení o posledním použitém čísle nového prefixu.
  4. Načtení číselníků
    GET /onix-codetable-items?query=onixCodetable.number==N
    Jazyk (74), role autora (17), typ názvu (15), forma produktu (150)… Stačí jednou a uložit do cache.
  5. Autor: najít, případně založit
    GET /persons/search-options · POST /natural-persons
    Autor se na titul napojí přes titleContributions[].person.id.
  6. Nahrání obálky
    POST /storage/upload
    JPEG do 5 MB, vrátí storageKey.
  7. Založení titulu
    POST /titles
    Název, jazyk, autoři, nakladatel, místo vydání, obálka.
  8. Založení produktu a přidělení čísla
    POST /products/create-with-number nebo POST /products → /product-numbers/assign/preview → /product-numbers/assign
    Vazba, brožura, e-kniha… Každý produkt dostane vlastní ISBN.

1. Přihlášení

Přihlašujete se jménem a heslem uživatele, který má v organizaci nakladatele roli Garant nakladatele (PublisherGuarantor). Doporučujeme pro integraci založit samostatný účet (např. erp@nakladatelstvi.cz), aby šlo změny v systému odlišit od práce lidí. Roli přiděluje správce nakladatele (PublisherAdmin) v modulu Uživatelé nebo agentura.

Získání tokenu ověřeno

POST/authn/login-with-expirySwagger · login-keycloak-3.0.0 · loginWithExpiry ↗Doporučený způsob. Tokeny přijdou jako cookies folioAccessToken a folioRefreshToken.
curl -s -c cookies.txt -X POST "$BASE_URL/authn/login-with-expiry" \
  -H "x-okapi-tenant: $TENANT" \
  -H "Content-Type: application/json" \
  -d '{"username":"erp@nakladatelstvi.cz","password":"***"}'

Odpověď 201 Created:

Set-Cookie: folioAccessToken=eyJhbGciOiJSU…; Path=/; Max-Age=3600; Secure; HttpOnly; SameSite=None
Set-Cookie: folioRefreshToken=eyJhbGciOiJIU…; Path=/authn; Max-Age=86400; Secure; HttpOnly; SameSite=None

{"accessTokenExpiration":"2026-09-23T13:55:36Z","refreshTokenExpiration":"2026-09-24T12:55:36Z"}

Hodnota cookie folioAccessToken je access token (JWT). Z cookie souboru ho získáte např. takto:

export TOKEN=$(awk '$6=="folioAccessToken"{print $7}' cookies.txt)
POST/authn/loginSwagger · login-keycloak-3.0.0 · login ↗Starší varianta, pro serverové integrace pohodlnější: token je přímo v těle odpovědi. Access token je stejný jako u varianty výše.
curl -s -X POST "$BASE_URL/authn/login" \
  -H "x-okapi-tenant: $TENANT" -H "Content-Type: application/json" \
  -d '{"username":"erp@nakladatelstvi.cz","password":"***"}'
HTTP/1.1 201 Created
x-okapi-token: eyJhbGciOiJSU…
Set-Cookie: folioAccessToken=…; Set-Cookie: folioRefreshToken=…

{"okapiToken":"eyJhbGciOiJSU…","refreshToken":"eyJhbGciOiJIU…"}

Chybové odpovědi přihlášení

SituaceHTTPTělo (zkráceno)
Špatné jméno nebo heslo400{"errors":[{"message":"Failed to obtain a token","type":"ServiceException","code":"service_error","parameters":[{"key":"cause","value":"… invalid_grant … Invalid user credentials"}]}]}
Chybí hlavička x-okapi-tenant404{"message":"no Route matched with those values"}

Hlavičky dalších volání ověřeno

Každé další volání musí nést tenanta a access token. Token lze předat kterýmkoli z těchto tří způsobů (všechny ověřeny):

HlavičkaHodnotaPoznámka
x-okapi-tenant$TENANTVždy povinná. Bez ní gateway odpoví 404 „no Route matched“.
x-okapi-token$TOKENDoporučeno pro serverové integrace.
AuthorizationBearer $TOKENAlternativa k x-okapi-token.
CookiefolioAccessToken=$TOKENAlternativa (např. curl -b cookies.txt).
Content-Typeapplication/jsonU volání s JSON tělem.

Příklady v dalších kapitolách proto začínají takto:

H=(-H "x-okapi-tenant: $TENANT" -H "x-okapi-token: $TOKEN" -H "Content-Type: application/json")
SituaceHTTPTělo
Chybí nebo neplatný / prošlý token401{"errors":[{"type":"UnauthorizedException","code":"authorization_error","message":"Unauthorized"}],"total_records":1}
Endpoint, na který role Garant nemá oprávnění403{"errors":[{"type":"ForbiddenException","code":"authorization_error","message":"Access Denied"}],"total_records":1}

Obnova tokenu a odhlášení ověřeno

Access token platí 1 hodinu, refresh token 24 hodin. Před vypršením access tokenu (čas je v accessTokenExpiration) zavolejte refresh s cookie folioRefreshToken. Pokud vyprší i refresh token, přihlaste se znovu.

curl -s -b cookies.txt -c cookies.txt -X POST "$BASE_URL/authn/refresh" -H "x-okapi-tenant: $TENANT"
# nebo s refresh tokenem z /authn/login:
curl -s -c cookies.txt -X POST "$BASE_URL/authn/refresh" -H "x-okapi-tenant: $TENANT" \
  -b "folioRefreshToken=$REFRESH_TOKEN"
HTTP/1.1 201 Created
Set-Cookie: folioAccessToken=…; Max-Age=3600 …
Set-Cookie: folioRefreshToken=…; Path=/authn …

{"accessTokenExpiration":"2026-09-23T13:55:53Z","refreshTokenExpiration":"2026-09-24T12:55:47Z"}

Bez cookie vrací refresh 400 "Required cookie 'folioRefreshToken' … is not present". Odhlášení (zneplatnění refresh tokenu): POST /authn/logout se stejnou cookie → 204.

2. Nakladatel a prefix

Garant jedná vždy za svou aktivní organizaci (nakladatele). Server podle ní kontroluje, že zakládá a upravuje jen tituly a produkty vlastního nakladatele.

Aktivní organizace ověřeno

curl -s "$BASE_URL/br-users/_self" "${H[@]}"
Odpověď 200 (pole permissions vynecháno)
{
  "user": {
    "username": "api-test-garant@example.cz",
    "id": "d0c44300-0000-4000-8000-000000000443",
    "shouldChangePassword": false,
    "personal": { "firstName": "API", "lastName": "Testovací garant", "email": "api-test-garant@example.cz" }
  },
  "userOrganizations": [
    { "id": "5b87074c-7496-4f9f-992a-8b017fc85c4d", "name": "Concordia", "source": "PUBLISHERS",
      "sourceId": "e545de53-1e28-4e6c-b419-7ecf534b9604", "archived": false, "adminResolved": true, "version": 0 }
  ],
  "activeOrganization": {
    "id": "5b87074c-7496-4f9f-992a-8b017fc85c4d", "name": "Concordia", "source": "PUBLISHERS",
    "sourceId": "e545de53-1e28-4e6c-b419-7ecf534b9604", "archived": false, "adminResolved": true, "version": 0
  },
  "activeRoles": [ "PublisherGuarantor" ],
  "tenant": "tritius"
}

Důležité je activeOrganization.sourceId – identifikátor nakladatele v evidenci nakladatelů. Musí platit activeRoles obsahuje PublisherGuarantor.

Je-li účet členem více organizací, vyberte aktivní organizaci (volba se uloží k účtu):

POST/br-users/select-organizationSwagger · users-1.2.95 · postBrUsersSelectOrganization ↗ověřeno
curl -s -X POST "$BASE_URL/br-users/select-organization" "${H[@]}" \
  -d '{"organizationId":"5b87074c-7496-4f9f-992a-8b017fc85c4d"}'     # userOrganizations[].id

Odpověď 200 má stejný tvar jako /br-users/_self.

ID nakladatele pro tituly ověřeno

Tituly a produkty neodkazují na sourceId, ale na záznam nakladatele v modulu agentury. Jeho id získáte takto:

GET/agency-publishers/by-source-id/{sourceId}Swagger · agency-1.3.171 · getPublisherBySourceId ↗
curl -s "$BASE_URL/agency-publishers/by-source-id/e545de53-1e28-4e6c-b419-7ecf534b9604" "${H[@]}"
{
  "id": "482788d9-8db9-40a4-b6b8-3b33c9560bb4",        ← použijte jako owner.id a titlePublishers[].publisher.id
  "sourceId": "e545de53-1e28-4e6c-b419-7ecf534b9604",
  "name": "Concordia",
  "status": "ACTIVE",
  "dataConfirmationTime": "2007-08-23T22:00:00Z",      ← starší než 1 rok = nelze přidělit ISBN
  "publisherType": "COMMON",
  "registrations": [ { "registrationAgency": "ISBN", "registrationIntent": "CONTINUOUS", "registrationTime": "1990-05-15T22:00:00Z" } ],
  "municipality": "Praha",
  …
}
Tip

Toto id se nemění – stačí ho zjistit jednou a uložit do konfigurace integrace.

Prefixy nakladatele ověřeno

GET/publisher-prefixes?query=publisher.id=={publisherId}Swagger · agency-1.3.171 · getPublisherPrefixCollection ↗
curl -s -G "$BASE_URL/publisher-prefixes" "${H[@]}" \
  --data-urlencode 'query=publisher.id==482788d9-8db9-40a4-b6b8-3b33c9560bb4'
Odpověď 200 (zkráceno)
{
  "totalRecords": 3,
  "items": [
    { "id": "f3be7602-…", "value": "900124", "status": "CLOSED", "declarationStatus": "NOT_REQUIRED", … },
    { "id": "66328734-…", "value": "901389", "status": "CLOSED", "declarationStatus": "NOT_REQUIRED", … },
    { "id": "0c271993-b753-43ef-a1fc-7b43480a1d6a",
      "value": "85997",
      "status": "ACTIVE",
      "declarationStatus": "PENDING",
      "publisherPrefixRange": {
        "magnitude": 2, "status": "ACTIVE",
        "agencyPrefix": { "value": "80", "internationalPrefix": { "agency": "ISBN", "value": "978" } }
      } }
  ]
}

Plný prefix je 978-80-85997 (mezinárodní prefix – prefix agentury – prefix nakladatele). Čísla se přidělují jen z prefixů ve stavu ACTIVE. magnitude je počet číslic pořadového čísla (1 = blok 10 čísel, 2 = 100, 3 = 1000).

Předpoklady pro přidělení ISBN/ISMN

Založit titul a produkt jde vždy. Aby šlo produktu přidělit číslo, musí nakladatel splnit tyto podmínky. Nejrychleji je ověříte voláním feasibility – vrátí konkrétní důvod.

PodmínkaDůvod ve feasibilityJak ji splnit
Nakladatel není ukončenPUBLISHER_CLOSEDKontaktujte agenturu.
Údaje nakladatele potvrzeny v posledních 12 měsícíchDATA_NOT_CONFIRMEDPotvrzení údajů – viz níže.
U nového prefixu je podáno prohlášení o posledním použitém čísleDECLARATION_PENDINGProhlášení – viz níže.
Nakladatel je registrován u agentury (ISBN, pro noty ISMN)NO_AGENCY_PREFIXRegistrace u agentury.
V aktivních prefixech zbývají volná číslaNO_AVAILABLE_NUMBERSPožádejte o nový prefix (případně needsNewPrefix, viz kap. 8).

Potvrzení údajů nakladatele z kódu

POST/publishers/{sourceId}/confirmDataSwagger · publishers-1.4.97 · confirmPublisherData ↗Bez těla. {sourceId} = activeOrganization.sourceId. Nastaví datum potvrzení na „teď“; údaje by měl nejprve zkontrolovat člověk (v portálu nebo přes GET /publishers/{sourceId}).
curl -s -X POST "$BASE_URL/publishers/e545de53-1e28-4e6c-b419-7ecf534b9604/confirmData" "${H[@]}"
# → 200 OK
Neověřeno živě

Modul evidence nakladatelů nebyl v testovacím prostředí přes gateway dostupný (chyba prostředí). Popis vychází ze specifikace modulu (publishers.yml) a z chování portálu. Dokud údaje potvrzeny nejsou, feasibility vrací DATA_NOT_CONFIRMED a assign 422 "The publisher data confirmation is missing or older than one year" (typ -16) – to ověřeno je.

Prohlášení o posledním použitém čísle ověřeno

Prefix ve stavu declarationStatus: "PENDING" (typicky převzatý prefix, ze kterého nakladatel už čísla používal mimo registr) je zablokovaný, dokud nakladatel neprohlásí, jaké číslo použil jako poslední. Všechna čísla až po toto číslo se tím zablokují, přidělování pokračuje dalším.

POST/publisher-prefixes/{publisherPrefixId}/last-used-numberSwagger · agency-1.3.171 · declareLastUsedNumber ↗
curl -s -X POST "$BASE_URL/publisher-prefixes/0c271993-b753-43ef-a1fc-7b43480a1d6a/last-used-number" "${H[@]}" \
  -d '{"noNumberUsed": false, "lastUsedNumber": "978-80-85997-45-3"}'
# pokud z prefixu dosud žádné číslo použito nebylo:
#   -d '{"noNumberUsed": true}'
PoleTypPovinnéPoznámka
noNumberUsedbooleananotrue = z prefixu nebylo použito žádné číslo; pak lastUsedNumber musí být prázdné (jinak 422 -53).
lastUsedNumberstringpři noNumberUsed=falseCelé ISBN/ISMN s pomlčkami nebo bez; musí mít platnou kontrolní číslici (422 -53) a patřit do prefixu (422 -54).
Odpověď 200 (zkráceno)
{
  "publisherPrefix": {
    "id": "0c271993-b753-43ef-a1fc-7b43480a1d6a", "value": "85997", "status": "ACTIVE",
    "declarationStatus": "DECLARED", "lastUsedNumber": "9788085997453", "noNumberUsed": false, …
  },
  "blockedCount": 5,
  "alreadyBlockedCount": 0,
  "createdCount": 0,
  "skippedAssignedCount": 41
}

Chyba 422 {"errors":[{"message":"lastUsedNumber must be empty when noNumberUsed is true","type":"-53","code":"VALIDATION_ERROR"}]}. Prohlásit lze jen vlastní prefix ve stavu PENDING, jinak 403 (-56).

3. Číselníky ONIX ověřeno

Většina kódovaných polí (typ názvu, jazyk, role autora, forma produktu, měna…) odkazuje na položky číselníků podle standardu ONIX for Books. Číselníky smí číst každý přihlášený uživatel.

Jak číselníky načíst

GET/onix-codetablesSwagger · agency-1.3.171 · getOnixCodetableCollection ↗Seznam číselníků, každý včetně všech svých položek (onixCodetableItems). Filtr: query=number==74 nebo query=name=="Jazyky".
GET/onix-codetable-itemsSwagger · agency-1.3.171 · getOnixCodetableItemCollection ↗Položky jednoho číselníku – takto je načítá i portál.
# všechny jazyky (číselník 74), seřazené jako v portálu
curl -s -G "$BASE_URL/onix-codetable-items" "${H[@]}" \
  --data-urlencode 'query=onixCodetable.number==74 sortBy sortOrder value' \
  --data-urlencode 'limit=1000'

# jedna konkrétní položka: čeština
curl -s -G "$BASE_URL/onix-codetable-items" "${H[@]}" \
  --data-urlencode 'query=onixCodetable.number==74 and value==cze'
{
  "items": [
    { "id": "99828aa9-bb9c-42e6-9dc6-dc7ca4583a21",
      "onixCodetable": { "id": "9e3c308d-4c53-4d6c-9392-f8bb0e32529b", "number": 74, "name": "Jazyky", "version": 0 },
      "value": "cze",
      "description": "čeština",
      "version": 0 }
  ],
  "totalRecords": 1
}

Číselník jazyků má 569 položek (kódy ISO 639-2/B, např. cze, slo, eng, ger). Výchozí limit je 1000.

Co se posílá do pole s číselníkem

Posílejte vždy id, value i version

Kódované pole je objekt položky číselníku. Server ho páruje podle id, validace ale čte value a databázová vrstva vyžaduje version. Nejjednodušší je vložit objekt položky tak, jak ho vrátilo API.

"language": { "id": "99828aa9-bb9c-42e6-9dc6-dc7ca4583a21", "value": "cze", "version": 0 }
Co pošleteVýsledek (ověřeno)
{"id","value","version"}201 – správně
jen {"id"} u typu názvu422 -58 „…exactly one main title … none was provided“ – zavádějící, validace nevidí value
{"id","value"} bez version400 s textovou (ne-JSON) zprávou Detached entity … has an uninitialized version value 'null'
jen {"value"} bez id500 TransientPropertyValueException … OnixCodetableItem

Číselníky, které budete potřebovat

ČísloNázevPole v APITypické hodnoty
15Typ názvutitles.names[].type01 hlavní název (právě jeden povinný), 03 název v původním jazyce
22Jazyk vzhledem k typu obsahutitles.languages[].role01 jazyk textu, 02 původní jazyk překladu
74Jazykytitles.languages[].language, textContents[].languagecze čeština, slo, eng, ger
17Typ autorské roletitleContributions[].roles[].roleA01 autor, A12 ilustrátor, B06 překladatel
19Typ nepojmenované osobytitleContributions[].unnamedPerson02 anonymní, 04 kolektiv
45Nakladatelské roletitlePublishers[].role01 nakladatel, 02 spoluvydavatel
28Cílová skupinaaudiences[].type01 obecné/dospělí, 02 děti
153Typ textutextContents[].type02 krátká anotace, 03 popis
158Typ obsahu podle zdrojesupportingResources[].type01 přední strana obálky, 02 zadní strana
159MediumsupportingResources[].mode03 obrázek
27Rejstřík předmětových klasifikacísubjects[].schemeThema, MDT, klíčová slova
81Typ obsahu produktuproducts.primaryContentType10 text, 11 notový zápis (→ ISMN)
150Formát produktuproducts.formBB vázaná, BC brožovaná, EA e-kniha, AC audio CD
175Detaily formátuproducts.formDetails[].valueE101 EPUB, E107 PDF
2Složení produktuproducts.composition00 jednosložkový
64Stav publikováníproducts.publishingStatus02 připravuje se, 04 aktivní; 01 zrušeno a 03 odloženo = nelze přidělit číslo
23Rozsah vzhledem k typu obsahuproducts.extents[].type06 celkový počet stran
24Jednotky rozsahuproducts.extents[].unit03 strany
58Typy cenproducts.price.type02 maloobchodní cena vč. daně
96Měnyproducts.price.currencyCZK, EUR
51Souvislosti mezi produktyproducts.productsRelations[].relationTypevazby mezi produkty

Číselníky se mění zřídka – načtěte je při startu integrace a mapujte si svůj kód (např. „cze“) na objekt položky.

4. Autor

Autoři (a další tvůrci – ilustrátoři, překladatelé) jsou osoby ze společné autoritní databáze. Nejprve zkuste existující osobu najít; novou zakládejte, jen když ji nenajdete. Nově založená osoba má stav PROPOSAL a agentura ji propojí s národní autoritou.

Vyhledání existující osoby ověřeno

GET/persons/search-options?nameFilter=…&legalForm=NATURAL&limit=20Swagger · agency-1.3.171 · getPersonSearchOptions ↗Tento endpoint používá portál při psaní jména. nameFilter hledá ve jméně i příjmení bez ohledu na diakritiku (celé jméno „Karel Čapek“ i část „Čapek“). legalForm: NATURAL = fyzická osoba, LEGAL = korporace. Volitelně sourceIdFilter = ID národní autority.
curl -s -G "$BASE_URL/persons/search-options" "${H[@]}" \
  --data-urlencode 'nameFilter=Karel Čapek' --data-urlencode 'legalForm=NATURAL' --data-urlencode 'limit=5'
[
  { "id": "…", "legalForm": "NATURAL", "firstName": "Karel Matěj", "lastName": "Čapek Chod", "birthDate": "1860", … },
  { "id": "…", "legalForm": "NATURAL", "firstName": "Karel", "lastName": "Čapek", "birthDate": "1890",
    "deathDate": "1938", "biographicalNote": "…", "sourceId": "jk01021023" },
  …
]

Odpověď je pole (bez obálky items). Rozlišujte osoby podle birthDate, deathDate a biographicalNote. Pro stránkovaný seznam s identifikátory (ISNI, ORCID) lze použít GET /persons?nameFilter=…&identifierFilter=…&offset=0&limit=20 → {"items":[…],"totalRecords":N}.

Založení nové fyzické osoby ověřeno

curl -s -X POST "$BASE_URL/natural-persons" "${H[@]}" -d '{
  "legalForm": "NATURAL",
  "firstName": "Jana",
  "lastName": "Testovací-Autorka",
  "birthDate": "1980",
  "biographicalNote": "Česká autorka knih pro děti.",
  "disclosureConsent": true
}'

Odpověď 201:

{
  "id": "00a3b2ab-47bc-4f11-a106-e6c5988501bb",       ← použijte v titleContributions[].person.id
  "legalForm": "NATURAL",
  "status": "PROPOSAL",
  "firstName": "Jana",
  "lastName": "Testovací-Autorka",
  "birthDate": "1980",
  "biographicalNote": "Česká autorka knih pro děti.",
  "disclosureConsent": true,
  "proposer": { "id": "482788d9-8db9-40a4-b6b8-3b33c9560bb4", "name": "Concordia", … },
  "version": 0
}
PoleTypPovinnéPoznámka
legalFormstringano (server)Vždy "NATURAL". Bez něj 400 missing type id property 'legalForm'.
firstNamestringano (portál)Server to nekontroluje, ale osoba bez jména je nepoužitelná.
lastNamestringano (portál)Totéž.
birthDate / deathDatestringneRRRR, MM.RRRR nebo DD.MM.RRRR; ne v budoucnosti.
disclosureConsentbooleanano (portál)Souhlas se zveřejněním údajů; portál vyžaduje true, pokud není vyplněno datum úmrtí.
biographicalNotestringneKrátký životopis.
identifiers[]polene{"type":"ISNI"|"ORCID","identifier":"…"}; formát se kontroluje (422 -27), duplicita 422 -26.
naturalAlternativeNames[]polenePseudonymy, jiné podoby jména: {"firstName","lastName"}.

Korporaci (např. autorský kolektiv, studio) založíte přes POST /legal-persons s tělem {"legalForm":"LEGAL","name":"…"} → 201 ověřeno.

Vlastní dosud nepotvrzené osoby lze opravit (PUT /natural-persons/{id}) nebo smazat (DELETE /natural-persons/{id} → 204, pokud nejsou použité).

Napojení autora na titul

Autor se k titulu přidává v poli titleContributions přímo při založení nebo úpravě titulu (samostatný endpoint neexistuje). Jméno se kopíruje z osoby do příspěvku – musí být vyplněno i při odkazu na osobu.

"titleContributions": [
  { "sequenceNumber": 1,
    "person": { "id": "00a3b2ab-47bc-4f11-a106-e6c5988501bb", "legalForm": "NATURAL" },
    "firstName": "Jana",
    "lastName": "Testovací-Autorka",
    "roles": [ { "role": { "id": "244aa3d3-f195-45c1-8aaf-88769f830b6c", "value": "A01", "version": 0 } } ] }
]
PolePovinnéPravidlo / chyba
sequenceNumberanoPořadí 1, 2, 3… Bez něj 400 null value in column "sequence_number".
person.idbuď – neboPřesně jedno z person / unnamedPerson (č. 19); jinak 422 -31.
firstName / lastName / nameanoAspoň jedno z firstName, lastName, name (korporace) nebo unnamedPerson; jinak 422 -30.
roles[].roleano (portál)Aspoň jedna role z číselníku 17. Server příspěvek bez role přijme, portál ne.
biographicalNotenePoznámka k autorovi v rámci titulu.
HTTP 422
{"errors":[
  {"message":"Title contribution #1 must have at least one of: firstName, lastName, name, or unnamedPerson","type":"-30","code":"VALIDATION_ERROR"},
  {"message":"Title contribution #2 must have exactly one of person or unnamedPerson, not both or neither","type":"-31","code":"VALIDATION_ERROR"}]}

5. Obálka ověřeno

Obálka se nahraje jako soubor a pak se k titulu přidá jako doprovodný zdroj (supportingResources). Nahrát ji lze před založením titulu i kdykoli později.

Nahrání souboru

POST/storage/uploadSwagger · agency-1.3.171 · upload ↗multipart/form-data, pole file. Pouze image/jpeg, max. 5 MB. Nenastavujte ručně Content-Type: application/json.
curl -s -X POST "$BASE_URL/storage/upload" \
  -H "x-okapi-tenant: $TENANT" -H "x-okapi-token: $TOKEN" \
  -F "file=@obalka.jpg;type=image/jpeg"
{
  "storageKey": "3acf0c5b-850b-4b3d-9a2c-be7a86f1d61f.jpg",
  "publicUrl": "{{BASE_URL}}/storage/content/3acf0c5b-850b-4b3d-9a2c-be7a86f1d61f.jpg",
  "mimeType": "image/jpeg",
  "fileSize": 903
}
SituaceHTTPmessage
Jiný typ než JPEG (PNG, PDF…)400Only image/jpeg is supported
Chybí pole file400Required part 'file' is not present.
Prázdný soubor / větší než 5 MB400 / 413File is empty / File exceeds 5242880 bytes z kódu
Úložiště na serveru není nastaveno503Storage is not configured on this server… – ohlaste agentuře

Navázání na titul

Do titulu vložte položku supportingResources se storageKey z uploadu. Odkaz (link) doplní server sám.

"supportingResources": [
  { "type": { "id": "c95fe1c3-3d95-4fd0-8e51-5a82522cfd10", "value": "01", "version": 0 },   ← 158: přední strana obálky
    "mode": { "id": "289d847e-1cff-4a3b-a0cd-d702c24a94bb", "value": "03", "version": 0 },   ← 159: obrázek
    "storageKey": "3acf0c5b-850b-4b3d-9a2c-be7a86f1d61f.jpg",
    "mimeType": "image/jpeg",
    "fileSize": 903 }
]
PolePovinnéPoznámka
typeanoČíselník 158. Portál zobrazuje jako obálku položku s 01 a mode 03.
modeanoČíselník 159. Bez něj 400 null value in column "mode_id".
storageKeyano pro nahraný souborZ odpovědi /storage/upload.
linkano pro externí URLMísto storageKey lze uvést vlastní veřejnou http(s) adresu obrázku.
mimeType, fileSizeneDoporučeno převzít z uploadu.

Doplnění obálky k existujícímu titulu: načtěte titul (GET /titles/{id}), přidejte položku do supportingResources a pošlete celý titul zpět přes PUT /titles/{id} → 204 ověřeno. PUT nahrazuje celý titul, proto posílejte i stávající obálky – vynechaná položka se z titulu odebere (odebraný soubor server archivuje).

6. Titul ověřeno

Titul je dílo v konkrétním vydání (název, autoři, jazyk, vydání). Jednotlivé formy – vázaná kniha, brožura, e-kniha – jsou jeho produkty a každý dostane vlastní ISBN.

POST/titlesSwagger · agency-1.3.171 · postTitle ↗201 Created, v těle celý titul včetně přidělených id vnořených položek, hlavička Location: /titles/{id}.
curl -s -X POST "$BASE_URL/titles" "${H[@]}" -d @titul.json
titul.json – kompletní ověřený příklad
{
  "owner": { "id": "482788d9-8db9-40a4-b6b8-3b33c9560bb4" },
  "ownerId": "ERP-2026-0443",
  "names": [
    { "type": { "id": "660e4af8-7688-4323-befa-1434928b0106", "value": "01", "version": 0 },
      "name": "Jak se API učilo číst",
      "subtitle": "Příběh pro malé i velké integrátory" }
  ],
  "editionNumber": 1,
  "languages": [
    { "role":     { "id": "f4e862e7-ad8a-4f2f-b631-47b2a35d3b1a", "value": "01",  "version": 0 },
      "language": { "id": "99828aa9-bb9c-42e6-9dc6-dc7ca4583a21", "value": "cze", "version": 0 } }
  ],
  "titleContributions": [
    { "sequenceNumber": 1,
      "person": { "id": "00a3b2ab-47bc-4f11-a106-e6c5988501bb", "legalForm": "NATURAL" },
      "firstName": "Jana",
      "lastName": "Testovací-Autorka",
      "roles": [ { "role": { "id": "244aa3d3-f195-45c1-8aaf-88769f830b6c", "value": "A01", "version": 0 } } ] }
  ],
  "titlePublishers": [
    { "publisher": { "id": "482788d9-8db9-40a4-b6b8-3b33c9560bb4" },
      "role": { "id": "e66966a3-90eb-4320-95b9-a0beb768600b", "value": "01", "version": 0 } }
  ],
  "publicationPlaces": [ { "city": "Praha" } ],
  "audiences": [ { "type": { "id": "1508d530-5511-4da5-9f6a-a918292562e7", "value": "02", "version": 0 } } ],
  "textContents": [
    { "type": { "id": "36ea10a4-ffbc-40a3-9efe-4cc9998689dc", "value": "02", "version": 0 },
      "text": "Krátká anotace titulu.",
      "language": { "id": "99828aa9-bb9c-42e6-9dc6-dc7ca4583a21", "value": "cze", "version": 0 } }
  ],
  "supportingResources": [
    { "type": { "id": "c95fe1c3-3d95-4fd0-8e51-5a82522cfd10", "value": "01", "version": 0 },
      "mode": { "id": "289d847e-1cff-4a3b-a0cd-d702c24a94bb", "value": "03", "version": 0 },
      "storageKey": "3acf0c5b-850b-4b3d-9a2c-be7a86f1d61f.jpg",
      "mimeType": "image/jpeg", "fileSize": 903 }
  ]
}
Odpověď 201 (zkráceno)
{
  "id": "9403e266-9a92-4da9-9f97-682390007411",
  "owner": { "id": "482788d9-8db9-40a4-b6b8-3b33c9560bb4", "name": "Concordia", … },
  "ownerId": "ERP-2026-0443",
  "names": [ { "id": "34ad8d4c-…", "type": { "id": "660e4af8-…", "value": "01", "description": "Hlavní název dokumentu (knihy)…", "version": 0 },
               "name": "Jak se API učilo číst", "subtitle": "Příběh pro malé i velké integrátory" } ],
  "editionNumber": 1,
  "languages": [ { "id": "8333383b-…", "role": { "value": "01", "description": "Jazyk textu", … },
                   "language": { "value": "cze", "description": "čeština", … } } ],
  "titleContributions": [ { "id": "da8fb972-…", "sequenceNumber": 1,
                            "person": { "id": "00a3b2ab-…", "legalForm": "NATURAL", "status": "PROPOSAL", … },
                            "firstName": "Jana", "lastName": "Testovací-Autorka",
                            "roles": [ { "id": "e16f45d1-…", "role": { "value": "A01", "description": "Autor", … } } ] } ],
  "supportingResources": [ { "id": "201bc40a-…", "type": { "value": "01", … }, "mode": { "value": "03", … },
                             "link": "{{BASE_URL}}/storage/content/3acf0c5b-850b-4b3d-9a2c-be7a86f1d61f.jpg",
                             "storageKey": "3acf0c5b-850b-4b3d-9a2c-be7a86f1d61f.jpg", "mimeType": "image/jpeg", "fileSize": 903 } ],
  "ownerUnresolved": false,
  "version": 0
}

Pole titulu

Sloupec Povinné: server = bez pole API vrátí chybu; portál = server pole nevynucuje, ale webový formulář ho vyžaduje a agentura s ním počítá – posílejte ho.

PoleTypPovinnéČíselníkPoznámka
owner.idUUIDserver–ID vlastního nakladatele (kap. 2). Chybí nebo je cizí → 403 -28.
ownerIdstringne–Vaše vlastní ID titulu (z ERP). Podle něj titul později dohledáte.
names[]poleserver–Právě jeden název s type.value = "01"; jinak 422 -58.
names[].typepoložkaserver15Viz formát položky.
names[].namestringserver–Text názvu. Bez něj 500 „Could not commit JPA transaction“.
names[].subtitle, partNumber, partName, prefix, titleWithoutPrefixstringne–Podnázev, číslo a název části, člen (např. „The“).
languages[]poleportál–Aspoň jeden jazyk. Server titul bez jazyka přijme.
languages[].rolepoložkaportál2201 jazyk textu. Server přijme i bez role.
languages[].languagepoložkaportál74cze čeština.
titleContributions[]polene17, 19Autoři – pravidla viz kapitola 4.
titlePublishers[]poleportál–Aspoň vlastní nakladatel s rolí 01. Portál ho předvyplní.
titlePublishers[].publisher.idUUIDserver*–*Pokud je uveden publisher, musí mít id.
titlePublishers[].rolepoložkaserver45Bez role 500 „Could not commit JPA transaction“.
publicationPlaces[].citystringportál–Aspoň jedno místo vydání (portál předvyplní obec sídla).
editionNumberintegerne–Pořadí vydání, ≥ 1.
editionStatementstringne–Údaj o vydání („2., upravené vydání“).
textContents[]polene153, 74Anotace; každá položka potřebuje type a text.
supportingResources[]polene158, 159Obálky – viz kapitola 5.
audiences[].typepoložkane28Cílová skupina.
subjects[]polene27, 93xxscheme povinné; u Thema a MDT i code, u klíčových slov text.
titleCollections[]polene–Edice/řada: {"collection":{"id":…},"partNumber":"…"}.
ageFrom, ageTointegerne–0–99, ageFrom ≤ ageTo.
doistringne–Tvar https://doi.org/10.xxxx/….
illustrationsNote, imprintstringne–Poznámka k ilustracím, imprint.
commentForAgencystringne–Vzkaz pro agenturu; při neprázdné hodnotě jde agentuře e-mail.
versionintegerpři PUT–Optimistický zámek, viz níže. Při POST se ignoruje.

Úprava titulu

GET/titles/{id}PUT/titles/{id}Swagger · agency-1.3.171 · putTitleById ↗PUT nahrazuje celý titul – vždy pošlete kompletní objekt z GET se změnami, včetně version. Odpověď 204 bez těla.
curl -s "$BASE_URL/titles/$TITLE_ID" "${H[@]}" > titul-aktualni.json
jq '.editionStatement = "1. vydání"' titul-aktualni.json > titul-upraveny.json
curl -s -X PUT "$BASE_URL/titles/$TITLE_ID" "${H[@]}" -d @titul-upraveny.json     # → 204

Pokud mezitím titul změnil někdo jiný (nebo posíláte starou version), vrátí server 409:

{"errors":[{"message":"Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [Title#9403e266-…]","type":"-1","code":"VALIDATION_ERROR"}]}

Řešení: titul znovu načtěte, změnu aplikujte a pošlete znovu. Po každém úspěšném PUT se version zvýší o 1. Titul s produkty nelze smazat (TITLE_HAS_PRODUCTS, -44).

7. Produkt ověřeno

Produkt je konkrétní forma titulu – vázaná kniha, brožura, e-kniha v EPUB, audiokniha. ISBN se přiděluje produktu. Produkt lze založit samostatně (POST /products) nebo rovnou s číslem (POST /products/create-with-number, kapitola 8B) – tělo produktu je v obou případech stejné.

curl -s -X POST "$BASE_URL/products" "${H[@]}" -d @produkt.json
produkt.json – ověřený příklad (vázaná kniha)
{
  "title": { "id": "9403e266-9a92-4da9-9f97-682390007411", "version": 1,
             "owner": { "id": "482788d9-8db9-40a4-b6b8-3b33c9560bb4" } },
  "ownerId": "ERP-2026-0443-V",
  "primaryContentType": { "id": "8b83a3f8-9b80-4014-9256-08b1c7599d74", "value": "10", "version": 0 },
  "form":               { "id": "09f49c35-a228-4c01-871a-528df0524481", "value": "BB", "version": 0 },
  "composition":        { "id": "af3594a3-589f-4080-8f31-e30d6797a349", "value": "00", "version": 0 },
  "publishingStatus":   { "id": "238186fd-1d62-4cd9-9dc9-db1539a49761", "value": "02", "version": 0 },
  "publishingDate": "11.2026",
  "unitMeasures": { "height": 240, "width": 170, "thickness": 12, "weight": 450 },
  "extents": [
    { "type": { "id": "9edc8d2b-3731-45bf-b214-2596e398efb1", "value": "06", "version": 0 },
      "value": 128,
      "unit": { "id": "8dd20a95-b47b-49ab-9417-97c7d51dffb0", "value": "03", "version": 0 } }
  ],
  "price": {
    "type":     { "id": "48bbe7b3-0ecf-4ccb-be27-b745f9bb8250", "value": "02", "version": 0 },
    "amount": 34900,
    "currency": { "id": "d2f3b181-e6da-470c-a45f-86a434030033", "value": "CZK", "version": 0 }
  }
}

Odpověď 201 obsahuje celý produkt, mimo jiné "id": "554599d0-71c2-4c42-9f24-b747cdea217d", "accessibility": "PRIVATE" a "version": 0.

Pole produktu

PoleTypPovinnéČíselníkPoznámka
title.idUUIDserver–Titul, ke kterému produkt patří.
title.versionintegerserver–Aktuální verze titulu (z GET /titles/{id}). Bez ní 400 Detached entity … Title.version.
title.owner.idUUIDserver–ID vlastního nakladatele; bez něj 403 -25. Nejjednodušší je poslat celý titul z GET.
primaryContentTypepoložkaserver8110 text → ISBN; 11 notový zápis → ISMN. Všechny produkty titulu musí mít stejný (jinak 422 -52).
formpoložkaserver150Forma produktu.
compositionpoložkaserver2Obvykle 00 jednosložkový.
publishingStatuspoložkaserver64Pro přidělení čísla nesmí být 01 (zrušeno) ani 03 (odloženo).
publishingDatestringpro zveřejnění–RRRR, MM.RRRR nebo DD.MM.RRRR. Bez data nelze produkt zveřejnit (422 -36).
ownerIdstringne–Vaše ID produktu (z ERP).
formDetails[].valuepoložkane175U e-knih formát souboru, např. E101 EPUB.
extents[]polene23, 24Každá položka potřebuje type, číselné value a unit (bez unit 500 unit is marked non-null).
priceobjektne58, 96Pokud je uveden, potřebuje type, amount a currency. amount je celé číslo v haléřích (349,00 Kč = 34900).
unitMeasures, cartonMeasures, palletMeasuresobjektne–height, width, thickness v mm, weight v g.
copyrightFrom, copyrightTostringne–Roky copyrightu.
productsRelations[]polene51{"relationType":…,"relatedProductId":"…"}.
accessibilitystringne–Výchozí PRIVATE. Zveřejnění: PATCH /products/{id}/accessibility s {"accessibility":"PUBLIC"} z kódu.
versionintegerpři PUT–Optimistický zámek, stejně jako u titulu.
Chybějící povinné pole vrací 500

Když chybí primaryContentType, form, composition nebo publishingStatus, server v této verzi neodpoví 422, ale 500 se zprávou "primaryContentType is marked non-null but is null" (resp. form …). Kontrolujte tato pole před odesláním.

Formu produktu vybírejte podle typu obsahu: pro text (10) formy B* (tištěné), E* (elektronické) a D*. Úprava produktu: GET /products/{id} → změna → PUT /products/{id} (204), 409 při staré verzi.

8. Číslo produktu (ISBN/ISMN)

Číslo se přiděluje automaticky z aktivního prefixu nakladatele – další volné číslo v pořadí. Jsou dvě cesty:

A: krok za krokem

Produkt už existuje. feasibility → preview (rezervuje číslo) → assign. Hodí se, když chcete číslo před potvrzením ukázat uživateli.

B: jedním voláním

POST /products/create-with-number založí produkt a rovnou mu přidělí číslo. Pro integrace nejjednodušší.

A: přidělení z rozsahu krok za krokem ověřeno

1. Lze číslo přidělit?

GET/product-numbers/assign/feasibility/{titleId}?primaryContentType=10Swagger · agency-1.3.171 · checkAssignFeasibility ↗Bez vedlejších účinků, lze volat i před založením produktu. primaryContentType = value z číselníku 81 (určuje ISBN vs. ISMN).
curl -s "$BASE_URL/product-numbers/assign/feasibility/$TITLE_ID?primaryContentType=10" "${H[@]}"

Skutečné odpovědi v pořadí, jak nakladatel postupně plnil podmínky:

{"canAssign":false,"agency":"ISBN","needsNewPrefix":false,"reason":"DATA_NOT_CONFIRMED","prefixes":[]}
{"canAssign":false,"agency":"ISBN","needsNewPrefix":false,"reason":"DECLARATION_PENDING","prefixes":[]}
{"canAssign":true,"agency":"ISBN","needsNewPrefix":false,
 "prefixes":[{"publisherPrefixId":"0c271993-b753-43ef-a1fc-7b43480a1d6a","label":"978-80-85997"}]}
PoleVýznam
canAssigntrue = číslo lze přidělit.
agencyISBN nebo ISMN.
prefixes[]Použitelné prefixy; label lze poslat jako publisherPrefix.
needsNewPrefixtrue = nakladatel nemá aktivní prefix; při přidělení je nutné poslat magnitude (1 = 10 čísel, 2 = 100, 3 = 1000) a vznikne nový prefix.
reasonPUBLISHER_CLOSED, DATA_NOT_CONFIRMED, DECLARATION_PENDING, NO_AGENCY_PREFIX, NO_AVAILABLE_NUMBERS – viz předpoklady.

2. Náhled a rezervace čísla

POST/product-numbers/assign/previewSwagger · agency-1.3.171 · previewProductNumberAssignment ↗Vybere další volné číslo a rezervuje ho (selected: true). Nepotvrzená rezervace se uvolní automaticky po 24 hodinách.
curl -s -X POST "$BASE_URL/product-numbers/assign/preview" "${H[@]}" -d '{
  "productId":   "554599d0-71c2-4c42-9f24-b747cdea217d",
  "publisherId": "482788d9-8db9-40a4-b6b8-3b33c9560bb4",
  "publisherPrefix": "978-80-85997"
}'
Odpověď 200 (zkráceno)
{
  "publisherName": "Concordia",
  "publisherId": "482788d9-8db9-40a4-b6b8-3b33c9560bb4",
  "title": "Jana Testovací-Autorka: Jak se API učilo číst",
  "product": "Text, Vázaná kniha",
  "productId": "554599d0-71c2-4c42-9f24-b747cdea217d",
  "agency": "ISBN",
  "internationalPrefix": { "value": "978", … },
  "agencyPrefix": { "id": "ec96fdc3-826f-4eab-8e81-f89a872ceb42", "value": "80" },
  "publisherPrefix": { "id": "0c271993-b753-43ef-a1fc-7b43480a1d6a", "value": "85997", "status": "ACTIVE" },
  "createNewPublisherPrefix": false,
  "publisherPrefixValue": "85997",
  "assignedValue": "ISBN 978-80-85997-46-0",
  "productNumber": {
    "id": "84fc435b-3e54-48e4-b982-c7fd63423bab",
    "value": "9788085997460",
    "formattedValue": "978-80-85997-46-0",
    "fullFormattedValue": "ISBN 978-80-85997-46-0",
    "selected": true,
    "assigned": false
  },
  "ready": true
}

3. Přidělení

POST/product-numbers/assignSwagger · agency-1.3.171 · assignProductNumber ↗Stejné tělo jako preview, navíc productNumberId z preview – přidělí se právě rezervované číslo.
curl -s -X POST "$BASE_URL/product-numbers/assign" "${H[@]}" -d '{
  "productId":   "554599d0-71c2-4c42-9f24-b747cdea217d",
  "publisherId": "482788d9-8db9-40a4-b6b8-3b33c9560bb4",
  "publisherPrefix": "978-80-85997",
  "productNumberId": "84fc435b-3e54-48e4-b982-c7fd63423bab"
}'
{
  "id": "84fc435b-3e54-48e4-b982-c7fd63423bab",
  "value": "9788085997460",
  "formattedValue": "978-80-85997-46-0",
  "fullFormattedValue": "ISBN 978-80-85997-46-0",
  "assigned": true,
  "selected": false,
  "blocked": false,
  "publisher": { "id": "482788d9-…", "name": "Concordia", … },
  "product":   { "id": "554599d0-…", … },
  "version": 2
}
Pole požadavkuTypPovinnéPoznámka
productIdUUIDanoProdukt vlastního nakladatele (jinak 403 -25).
publisherIdUUIDanoID vlastního nakladatele.
publisherPrefixstringnePrefix, ze kterého brát číslo: 978-80-85997, 80-85997 nebo 85997. Bez něj server vybere sám (nejdřív prefix s nejméně volnými čísly).
publisherPrefixIdUUIDneTotéž jako interní ID (z feasibility.prefixes[]); má přednost před publisherPrefix.
productNumberIdUUIDdoporučenoID rezervovaného čísla z preview.
magnitudeintegerjen při needsNewPrefix1 / 2 / 3 = nový prefix pro 10 / 100 / 1000 čísel.

Druhé přidělení témuž produktu vrací 422 {"errors":[{"message":"Product already has an assigned ISBN/ISMN number","type":"-29","code":"VALIDATION_ERROR"}]}. Stav produktu kdykoli zjistíte přes GET /product-numbers/assign/{productId}/by-product-id – pro produkt s číslem vrací "assignBlockedReason":"PRODUCT_ALREADY_HAS_ASSIGNED_NUMBER", "assignedValue":"ISBN 978-80-85997-46-0". ověřeno

Rezervaci nelze jako garant zrušit

Endpoint POST /product-numbers/{id}/deselect vrací roli Garant 403. Pokud rezervované číslo nepoužijete, uvolní se samo do 24 hodin. Proto volejte preview až ve chvíli, kdy chcete číslo opravdu přidělit, nebo použijte cestu B.

B: produkt s číslem jedním voláním ověřeno

POST/products/create-with-numberSwagger · agency-1.3.171 · createProductWithNumber ↗Produkt se založí vždy (pokud je platný). Číslo se přidělí, pokud to jde; jinak odpověď vysvětlí proč a produkt zůstane bez čísla.
curl -s -X POST "$BASE_URL/products/create-with-number" "${H[@]}" -d '{
  "product": { …stejné tělo jako POST /products… },
  "assignProductNumber": true,
  "publisherPrefix": "978-80-85997"
}'
HTTP 201
{
  "product": { "id": "68fab90d-9a17-4fe2-a5d4-027e30ee1353", "ownerId": "ERP-2026-0443-B", "form": { "value": "BC", … }, … },
  "productNumber": {
    "id": "a4b9fa59-34b4-40d3-be13-3f470967bd49",
    "value": "9788085997477",
    "fullFormattedValue": "ISBN 978-80-85997-47-7",
    "assigned": true, …
  },
  "needsPublisherPrefix": false,
  "reason": null
}
Pole požadavkuTypPovinnéPoznámka
productobjektanoProdukt – pole a pravidla viz kapitola 7.
assignProductNumberbooleananotrue = přidělit číslo. Výchozí false – pak se jen založí produkt.
publisherPrefix / publisherPrefixIdstring / UUIDneJako u assign. Bez obou server vybere aktivní prefix sám (ověřeno – e-kniha dostala 978-80-85997-48-4).
magnitudeintegerjen bez prefixuNemá-li nakladatel aktivní prefix: 1/2/3 = založit nový prefix pro 10/100/1000 čísel. Bez něj se číslo nepřidělí a vrátí se needsPublisherPrefix: true.

Když se číslo nepřidělí

Odpověď je stále 201 a produkt existuje. productNumber je null a reason říká proč:

reasonneedsPublisherPrefixCo udělat
nulltrueNakladatel nemá aktivní prefix nebo volná čísla – pošlete magnitude, nebo kontaktujte agenturu.
PUBLISHER_PREFIX_VALUE_NOT_FOUND ověřenotruepublisherPrefix nepatří nakladateli – opravte hodnotu.
INVALID_PUBLISHER_PREFIX_SELECTIONtrueZvolený prefix nelze použít (uzavřený, cizí).
DATA_NOT_CONFIRMEDtruePotvrďte údaje nakladatele.
DECLARATION_PENDINGtruePodejte prohlášení o posledním čísle.
PUBLISHER_CLOSEDtrueKontaktujte agenturu.
PRODUCT_STATUS_NOT_ASSIGNABLE ověřenofalseStav publikování je 01 nebo 03 – číslo nelze přidělit.

Po odstranění příčiny přidělte číslo již založenému produktu cestou A (preview → assign) – nevolejte create-with-number znovu, vznikl by duplicitní produkt.

9. Dohledání vlastních záznamů ověřeno

Seznamy vracejí jen záznamy vlastního nakladatele. Filtruje se jazykem CQL v parametru query.

# titul podle vašeho ERP identifikátoru
curl -s -G "$BASE_URL/titles" "${H[@]}" --data-urlencode 'query=ownerId==ERP-2026-0443'
# produkty titulu
curl -s -G "$BASE_URL/products" "${H[@]}" --data-urlencode "query=title.id==$TITLE_ID"
# čísla přidělená produktům titulu
curl -s "$BASE_URL/product-numbers/by-title/$TITLE_ID" "${H[@]}"
{"items":[{"id":"84fc435b-…","fullFormattedValue":"ISBN 978-80-85997-46-0","assigned":true,"product":{"id":"554599d0-…"}}],"totalRecords":1}

Titul jiného nakladatele vrací 403 "Title not owned by guarantor's publisher" (-28).

10. Chyby a časté problémy

Formát chyb

Chyby aplikace mají tvar {"errors":[{"message","type","code"}]}. type je stabilní číselný kód chyby (záporné číslo jako text), message je anglický popis pro vývojáře. Jedna odpověď může obsahovat více chyb.

{"errors":[{"message":"A title must have exactly one main title (title type code 01), but none was provided","type":"-58","code":"VALIDATION_ERROR"}]}

Chyby gateway a oprávnění (401, 403 „Access Denied“) mají tvar {"errors":[{"type","code","message"}],"total_records":1}. Některé technické chyby (400/500 z databázové vrstvy) vrací tělo {"timestamp","status","error","message","path"} nebo prostý text.

HTTP stavy

HTTPVýznamCo dělat
200 / 201 / 204OK / založeno / uloženo bez těla–
400Chybný formát požadavku, chybějící technicky povinné poleOpravit tělo; message jmenuje sloupec/pole.
401Chybí nebo vypršel tokenRefresh, případně nové přihlášení.
403Chybí oprávnění role, nebo záznam patří jinému nakladateliZkontrolovat owner.id a aktivní organizaci.
404Záznam neexistuje, nebo chybí x-okapi-tenant–
409Souběžná změna (stará version)Načíst znovu a zopakovat.
422Porušené obchodní pravidloPodle type v tabulce níže.
500Neošetřená chyba – v této verzi i některá chybějící povinná pole produktuZkontrolovat povinná pole; jinak nahlásit agentuře i s časem volání.
503Služba (např. úložiště obálek) není dostupnáZopakovat později, nahlásit.

Kódy chyb (type) relevantní pro nakladatele

typeNázevKdy nastane
-15PUBLISHER_CLOSEDNakladatel je ukončen – nelze přidělit číslo.
-16PUBLISHER_GUARANTOR_DATA_NOT_CONFIRMEDÚdaje nakladatele nepotvrzeny 12 měsíců. ověřeno
-24NO_AVAILABLE_PRODUCT_NUMBERV prefixu není volné číslo.
-25PRODUCT_NOT_OWNED_BY_GUARANTORProdukt / titul produktu patří jinému nakladateli nebo chybí title.owner.id. ověřeno
-26 / -27PERSON_IDENTIFIER_ALREADY_EXISTS / _INVALID_FORMATISNI/ORCID osoby už existuje / má chybný formát.
-28TITLE_NOT_OWNED_BY_GUARANTORTitul patří jinému nakladateli nebo chybí owner.id. ověřeno
-29PRODUCT_ALREADY_HAS_ASSIGNED_NUMBERProdukt už číslo má. ověřeno
-30TITLE_CONTRIBUTION_NAME_REQUIREDAutor v titulu bez jména. ověřeno
-31TITLE_CONTRIBUTION_PERSON_XOR_UNNAMEDAutor musí mít buď person, nebo unnamedPerson. ověřeno
-34ONE_OFF_PUBLISHER_CANNOT_CREATE_TITLEJednorázový nakladatel nemůže zakládat tituly sám.
-36PRODUCT_PUBLISHING_DATE_REQUIREDZveřejnění produktu bez data vydání.
-38INVALID_PUBLISHER_PREFIX_SELECTIONZvolený prefix nelze použít.
-44TITLE_HAS_PRODUCTSMazání titulu, který má produkty.
-51PRODUCT_PUBLISHING_STATUS_NOT_ASSIGNABLEStav publikování 01/03.
-52PRODUCT_PRIMARY_CONTENT_TYPE_MISMATCHJiný typ obsahu než u ostatních produktů titulu. ověřeno
-53 / -54LAST_USED_NUMBER_INVALID / _NOT_IN_PREFIXChybné prohlášení o posledním čísle. ověřeno
-55PREFIX_DECLARATION_PENDINGPrefix čeká na prohlášení.
-56DECLARATION_NOT_ALLOWEDProhlášení u prefixu, který ho nevyžaduje, nebo cizího.
-57PUBLISHER_PREFIX_VALUE_NOT_FOUNDHodnota publisherPrefix neodpovídá žádnému prefixu nakladatele. ověřeno
-58MAIN_TITLE_EXACTLY_ONETitul nemá právě jeden hlavní název (01). ověřeno
-1INTERNALObecná chyba, např. souběžná změna (409).

Časté chyby integrace

  • Číselníková položka jen s id → titul „nemá hlavní název“ (422 -58). Posílejte id, value a version.
  • Produkt bez title.version nebo title.owner.id → 400 / 403. Nejjistější je vložit do product.title celý titul z GET /titles/{id}.
  • PUT jen se změněnými poli → pole, která nepošlete, se smažou. PUT vždy s celým objektem.
  • Autor bez jména v titleContributions → 422 -30, i když odkazuje na osobu.
  • Obálka v PNG → 400. Převeďte na JPEG.
  • Opakované create-with-number po neúspěšném přidělení → duplicitní produkt. Přidělujte cestou A.
  • Prošlý token → 401. Obnovujte token před vypršením (1 h).

Pro jednorázové zasílání ONIX zpráv z ERP existuje v systému samostatná role Externí nakladatel s endpointy /titles/receive a /products/receive; ta neumí autory, obálky ani ISBN a tento dokument ji nepopisuje.

11. Stav ověření

Všechny kroky proběhly 23. 9. 2026 jako uživatel s jedinou rolí Garant nakladatele v lokálním prostředí eureka-local (mod-br-agency 1.3.171). Chování serveru je stejné jako na ostatních prostředích se stejnou verzí; adresy, ID nakladatele a ID číselníkových položek se liší.

KrokStavPoznámka
Přihlášení, refresh, logout, hlavičkyověřenoPřes gateway.
_self, select-organization, agency-publishers, publisher-prefixesověřenoPřes gateway.
Potvrzení údajů nakladatele (/publishers/{id}/confirmData)z kóduModul evidence nakladatelů nebyl v testovacím prostředí přes gateway dostupný.
Prohlášení o posledním čísleověřenoLogika ověřena přímým voláním modulu; přes gateway v testovacím prostředí chybělo oprávnění (starší registrace modulu).
ČíselníkyověřenoPřes gateway.
Vyhledání a založení autoraověřenoPřes gateway.
Nahrání obálky a navázání na titulověřenoVeřejné stažení obálky (publicUrl) přes gateway testovacího prostředí nefungovalo (404), přímo z modulu ano.
Titul (POST, GET, PUT, 409)ověřenoPřes gateway.
Produkt, feasibility, preview, assign, create-with-numberověřenoPřes gateway.
Limit 5 MB, -34, -36, -38, needsNewPrefix/magnitudez kóduTestovací nakladatel tyto situace neměl.