This is a development version of the documentation. Content may change without notice.
Voke Documentation
Partner API

Chyby a opakování požadavků (CZ)

Obálky chyb, chybové kódy pro jednotlivé rozhraní, strategie opakování, idempotence a observabilita pro ESM partnery.

Obálka chyby

Každá chybová REST odpověď z Voke má stejný JSON tvar:

{
  "statusCode": 400,
  "message": "Human-readable description",
  "error": "Bad Request",
  "code": "VALIDATION_ERROR",
  "details": ["field: message"]
}

Pole code je stabilní kontrakt — jde o řetězec z enumu ApiErrorCode, který se mezi minor verzemi nezmění. message a details jsou informativní a mohou se změnit. Logiku zpracování chyb stavte na code, nikoli na message.

Úplný seznam všech kódů a jejich mapování na HTTP stavy najdete v Reference / Error Codes.

REST chyby, na které partneři narazí

Toto jsou hodnoty ApiErrorCode, na které partner pravděpodobně narazí při volání REST API Voke (správa API klíčů, trading konfigurace, endpointy org-contextu).

CodeHTTPKdy nastává
UNAUTHORIZED401Chybějící/expirovaný API klíč, nebo (na partnerských endpointech) chybějící hlavička X-API-Key.
FORBIDDEN403Platné přihlašovací údaje, ale nedostatečné scopes pro daný zdroj.
BAD_REQUEST400Obecně nesprávně formovaný požadavek — neshoda content-type, rozbité JSON apod.
VALIDATION_ERROR400Tělo požadavku nebo query parametry neprošly validací schématu. details[] vypíše chybná pole.
INVALID_UUID400Path nebo query parametr očekával UUID, ale obdržel něco jiného.
EXTERNAL_PLANT_ID_INVALID_FORMAT400externalPlantId neodpovídá formátu partnerského identifikátoru.
PAYLOAD_TOO_LARGE413Tělo požadavku překračuje limit dané route (výchozí 5 MB na /api/v1/*).
NOT_FOUND404Obecná 404 pro non-VCP routes.
SITE_NOT_FOUND404siteId buď neexistuje, nebo není viditelné pro org vašeho API klíče. Cross-org přístup vrací stejnou 404 — nikdy ne 403 — aby neunikla existence sites z jiných org.
PLANT_NOT_FOUND404Totéž jako SITE_NOT_FOUND, ale pro starší rodinu routes /plants/* (ponechána vedle /vcp/sites/*, dokud konzumenti migrují).
CONFLICT409Porušení unique constraintu (např. duplicitní název API klíče).
API_KEY_NOT_FOUND404Revoke/lookup neexistujícího API klíče.
API_KEY_SCOPES_EMPTY400Mint požadavek neobsahuje žádné scopes.
API_KEY_SCOPE_INVALID400Jeden nebo více požadovaných scopes není rozpoznáno.
API_KEY_CIDR_NOT_IMPLEMENTED400allowedIps v mint požadavku používá tvar CIDR, který server zatím nepodporuje.
API_KEY_VCP_SCOPES_NOT_SUPPORTED400Nasazení zatím neprovisionuje per-key vhosty, takže scopes vcp:* nelze novým klíčům udělit.
TRADING_NOT_ENABLED400Trading nebyl pro organizaci povolen.
TRADING_CONFIG_INCOMPLETE400Trading je povolen, ale chybí povinná konfigurační pole (ESM URL, přihlašovací údaje).
ESM_NOT_CONFIGURED400Org nemá záznam TradingPartnerConfig.
PLANT_NOT_LINKED_TO_ESM400Plant nemá nastaveno external_plant_id.
TOO_MANY_REQUESTS429Překročen rate limit na IP. Může být přítomna hlavička Retry-After v sekundách (best-effort: závisí na throttler middleware na dané route). Počkejte alespoň tuto dobu a poté zopakujte jednou.
SERVICE_UNAVAILABLE503Downstream závislost (broker, databáze) je nedostupná. Opakujte s exponenciálním backoffem.
INTERNAL_SERVER_ERROR500Neočekávaná chyba serveru. Lze bezpečně opakovat s exponenciálním backoffem.

Kódy odmítnutí VCP příkazů

Když Voke odmítne příkaz, AMQP ack zpráva (event.command.ack) má status: "REJECTED" a rejectionCode z enumu RejectionCode.

rejectionCodeVýznam
CONSTRAINT_VIOLATIONHodnota příkazu porušuje omezení site (např. setpoint mimo povolený rozsah)
INVALID_COMMANDTyp příkazu není pro tuto site/device podporován
INVALID_PAYLOADPayload příkazu je strukturálně nesprávný
DEVICE_OFFLINECílové zařízení není dostupné
DEVICE_FAULTCílové zařízení je ve stavu poruchy a nemůže přijímat příkazy
UNSUPPORTED_FOR_TOPOLOGYPříkaz je obecně platný, ale není podporován pro fyzickou topologii tohoto plantu

Nikdy neopakujte odmítnutý příkaz se stejným messageId a stejným payloadem. Ack REJECTED je sémantické odmítnutí, nikoli přechodné selhání. Změňte payload (např. dostaňte setpoint do rozsahu) nebo eskalujte na operations, než budete opakovat.

Selhání AMQP autentizace

Selhání AMQP autentizace a autorizace nejsou HTTP chyby — nastávají na úrovni RabbitMQ protokolu při navazování spojení nebo otevírání kanálu. Voke používá rabbitmq_auth_backend_http k validaci přihlašovacích údajů na straně serveru.

SelháníViditelné jakoPříčina
Vyjednáno TLS 1.2Selhání TLS handshake (protocol_version / SSLHandshakeException) ještě před AMQP authBroker je pouze TLS 1.3. Klient, který vyjedná TLS 1.2, je odmítnut — např. Spring ssl.enabled: true bez ssl.algorithm: TLSv1.3, nebo bezparametrové useSslProtocol() v RabbitMQ Java klientu. Explicitně připněte TLS 1.3.
Špatné username (slug) nebo API klíčAMQP 403 Connection refusedPřihlašovací údaje nenalezeny nebo nejsou spojeny se scope vcp:connect
API klíč zneplatněn (revoked)AMQP 403 Connection refusedKlíč byl na straně Voke smazán; znovu jej vytvořte a aktualizujte konfiguraci konzumenta
Špatný vhostAMQP 403 Access refusedPoužijte per-key vhost partner-{keyId} z vašeho connection bundle; nikdy se nevracejte k / (výchozí vhost). Každý klíč vcp:* si automaticky provisionuje vlastní vhost, již vložený v connection URI bundle.
Neshoda názvu frontyAMQP 403 Access refusedKonzument deklaroval frontu mimo namespace vcp.{slug}.*
Chybějící publish scopeAMQP 403 Access refusedPublish route vyžaduje odpovídající scope vcp:write:*
Chybějící/neplatné HMACZpráva je přijata brokerem, poté zahozena Vokecommand.device, command.mode a schedule.* vyžadují platný podpis obálky

Tato selhání jsou na straně Voke logována v AmqpAuthController. Partneři vidí pouze odmítnutí na úrovni protokolu. Pokud spojení, které dříve fungovalo, najednou začne selhávat, nejpravděpodobnější příčinou je rotace nebo zneplatnění API klíče — vytvořte nový klíč se scope vcp:connect a aktualizujte svého konzumenta.

Doporučení k opakování

REST

Třída odpovědiStrategie
4xx (kromě 429)Neopakujte. Opravte payload požadavku, přihlašovací údaje nebo org context.
429 Too Many RequestsPočkejte na hodnotu Retry-After (sekundy) z hlavičky odpovědi, poté zopakujte jednou.
5xxOpakujte s exponenciálním backoffem: 1 s, 2 s, 4 s, 8 s, 16 s. Maximálně 5 pokusů. Při selhání všech to vzdejte a vyvolejte alert.

VCP (AMQP zprávy)

ScénářStrategie
Přijat ack status: "ACCEPTED" nebo "QUEUED"Než vyhodnotíte výsledek příkazu, počkejte na event.execution.
Přijat ack status: "PARTIAL"Prozkoumejte results[]; část batch položek byla přijata a část odmítnuta.
Přijat ack status: "REJECTED"Neopakujte. Změňte payload nebo eskalujte.
Žádný ack v rámci vašeho timeoutuZkontrolujte, zda messageId již nebylo zpracováno (nahlédněte do vlastního logu). Pokud ne, můžete zopakovat s novým messageId poté, co potvrdíte, že site je dostupná.
Reconnect konzumentaRabbitMQ znovu doručí všechny nepotvrzené (unacknowledged) zprávy. Očekávejte duplikáty. Deduplikujte podle messageId na straně konzumenta.
event.execution opožděn nebo chybíJde o timing problém fan-outu na straně Voke — příkaz spekulativně neposílejte znovu. Před eskalací počkejte alespoň 30 s na opožděné doručení event.execution.

Výpadky AMQP spojení

Připojte se znovu okamžitě (bez čekání při prvním pokusu), poté při dalších selháních aplikujte backoff. Fronty jsou durable a zprávy jsou persistent — během vašeho odpojení se nic neztratí. Nepotvrzené (unacked) zprávy se po opětovném připojení automaticky znovu doručí.

Idempotence

  • Vždy nastavte unikátní messageId v každé VCP obálce, kterou odesíláte. Použijte UUID v4 nebo identifikátor odolný proti kolizím. messageId je vaše hlavní vodítko pro korelaci acků a statusů.
  • Voke deduplikuje příchozí příkazy podle messageId po dobu 10 minut přes Redis (vcp:seen:{orgSlug}:{messageId}, atomické SET NX EX 600). První doručení proběhne; duplikáty uvnitř okna jsou potvrzeny na brokeru a tiše zahozeny — na replay se partnerovi nepublikuje zpět žádný ACK, takže na něj nečekejte. Pokud downstream zpracování vyhodí výjimku, Voke uvolní claim, takže legitimní opakování se stejným messageId proběhne. Mimo 10minutové okno bude stejné messageId zpracováno znovu — navrhujte příkazy tak, aby byly sémanticky idempotentní (scheduleId, correlationId, stabilní reference příkazů), místo spoléhání pouze na replay guard. Úplný kontrakt replay guardu viz VCP message integrity.
  • Partneři musí deduplikovat příchozí události podle messageId lokálně. Po reconnectu konzumenta může RabbitMQ znovu doručit poslední nepotvrzený batch. Pro většinu případů postačí jednoduchá in-memory LRU množina posledních N hodnot messageId.

Observabilita

Partneři typicky logují každý příchozí ack a status event klíčovaný podle correlationId, aby šel kompletní round-trip příkazu rekonstruovat jen z partnerských logů:

[correlationId: req-abc123] SEND  command.site-setpoint  messageId: msg-001
[correlationId: req-abc123] ACK   QUEUED                 messageId: msg-001-ack
[correlationId: req-abc123] STATUS EXECUTING             messageId: msg-001-status-1
[correlationId: req-abc123] STATUS COMPLETED             messageId: msg-001-status-2

Na straně Voke VcpCommandLog uchovává stejnou timeline — každý příkaz, ack a status event je řádek klíčovaný podle messageId a correlationId. Při zakládání incidentu uveďte messageId a Voke ops během sekund najde odpovídající řádky VcpCommandLog.

Související odkazy

On this page