service
Envia a API key (x-service-api-key) mais o JWS. A plataforma verifica com nonce: reenviar o mesmo jti é recusado como replay. Por isso cada tentativa carrega um jti novo — o Agent cuida disso no backoff.
O catálogo de operações do Agent espelha as rotas da API uma a uma, e nada mais. Cada operação corresponde a uma rota do contrato: o Agent recebe o pedido em JSON simples, monta o corpo, assina e envia (ou apenas assina). Ele não valida posse de item, não confere preço, não decide nada de negócio — a autoridade é sempre o seu servidor. O nome da operação é o que você passa em operation (interface A) ou action (interface C).
| Operação | O que faz | Método e rota | Auth |
|---|---|---|---|
ping | verifica se o Agent está vivo | local (não fala com a plataforma) | — |
characterLinks.create | vincular personagem | POST /api/character-links | service |
market.eligibility | consultar elegibilidade do vendedor | GET /api/market/eligibility/{characterRef} | service |
market.adjustStock | ajustar estoque do anúncio | PUT /api/market/listings (ADJUST_STOCK) | service |
market.withdraw | retirar anúncio | PUT /api/market/listings (WITHDRAW) | service |
transactions.get | consultar transação | GET /api/transactions/{id} | service |
transactions.confirmDelivery | confirmar entrega | POST /api/transactions/{id}/delivery | service |
transactions.reportDeliveryFailure | reportar falha de entrega | POST /api/transactions/{id}/delivery-failure | service |
market.createListing | registrar anúncio (assina e envia) | POST /api/market/listings | signature |
market.signListing | registrar anúncio (assina e devolve, não envia) | POST /api/market/listings | signature |
transactions.create | criar transação (assina e envia) | POST /api/transactions | signature |
transactions.signBuyOffer | criar transação (assina e devolve, não envia) | POST /api/transactions | signature |
ping — confirma que o Agent está vivo. É local: não fala com a plataforma. Útil para o seu servidor checar que o sidecar está no ar.characterLinks.create — inicia o vínculo de um personagem a uma conta, a partir do jogo. Recebe characterRef e name.market.eligibility — diz se um personagem pode anunciar e o que falta quando não pode. É uma dica, nunca um portão: use para dar um aviso amigável ao vendedor, não para autorizar o anúncio. Recebe characterRef.market.adjustStock — informa à plataforma a quantidade que o seu servidor observa no anúncio. Recebe itemRef e observedQuantity.market.withdraw — retira o anúncio do mercado. Recebe itemRef.transactions.get — devolve o estado atual da transação. Consulte antes de transferir o item. Recebe transactionId.transactions.confirmDelivery — confirma que o item foi transferido. É a fonte da verdade da entrega. Recebe transactionId.transactions.reportDeliveryFailure — reporta uma falha de entrega. OUT_OF_STOCK estorna; TEMPORARY reenfileira. Recebe transactionId, reason, observedQuantity e, opcionalmente, detail.market.createListing / market.signListing — as duas registram um anúncio (POST /api/market/listings); a diferença é quem faz o HTTP (ver abaixo). Recebem itemRef, gameItemId, itemName, attributes, quantityListed, unitAmountCents e sellerCharacterRef.transactions.create / transactions.signBuyOffer — as duas criam uma transação a partir de uma oferta de compra (POST /api/transactions). Recebem apenas itemRef, quantity e buyerCharacterRef — preço e item não viajam, porque já são registro da Lootfy desde o anúncio.service × signature: a diferença que muda o retryCada operação declara como autentica, e isso não é cosmético — decide se um retry pode reusar o jti.
service
Envia a API key (x-service-api-key) mais o JWS. A plataforma verifica com nonce: reenviar o mesmo jti é recusado como replay. Por isso cada tentativa carrega um jti novo — o Agent cuida disso no backoff.
signature
Envia só o JWS, sem API key. É o modo das rotas que o client do jogo transporta (criar transação, POST /api/transactions, e registrar anúncio, POST /api/market/listings). Aqui o jti é chave de idempotência de domínio: o retry legítimo reusa o mesmo jti, porque um jti novo criaria uma segunda transação ou um segundo anúncio.
sign*: assinar sem enviarNas rotas transportadas pelo client (criar transação e registrar anúncio), quem faz o POST à Lootfy não é o seu servidor — é o client do jogo, pelo canal do jogo. O papel do servidor ali é só assinar, e as operações market.signListing e transactions.signBuyOffer fazem exatamente isso: assinam o corpo e devolvem o envelope, sem enviar nada.
O envelope devolvido tem quatro campos:
{ "body": "{\"itemRef\":\"...\",\"quantity\":1,\"buyerCharacterRef\":\"...\"}", "signature": "<protected>..<sig>", "jti": "b1c2...-...", "expiresAt": 1757000000}As variantes que enviam (market.createListing e transactions.create) existem para quem não tem o client transportando: nesse caso o próprio servidor assina e faz o HTTP. A plataforma não distingue quem fez a requisição — ela confere apenas a assinatura. Escolher entre assinar-e-enviar ou só-assinar é escolher o caminho de integração; a segurança é a mesma.
Pela interface A, um exemplo de operação service (confirmar entrega) e um de operação signature que só assina (a oferta de compra):
curl -s http://127.0.0.1:8899/call \ -H 'content-type: application/json' \ -d '{ "operation": "transactions.confirmDelivery", "input": { "transactionId": "txn-123" }, "id": "deliver-txn-123" }'Resposta em sucesso — o resultado da plataforma vem em data, e o id é ecoado:
{ "ok": true, "id": "deliver-txn-123", "data": null }curl -s http://127.0.0.1:8899/call \ -H 'content-type: application/json' \ -d '{ "operation": "transactions.signBuyOffer", "input": { "itemRef": "listing-42", "quantity": 1, "buyerCharacterRef": "char-7" } }'Resposta — o envelope assinado, para o client do jogo transportar até a Lootfy:
{ "ok": true, "data": { "body": "{\"itemRef\":\"listing-42\",\"quantity\":1,\"buyerCharacterRef\":\"char-7\"}", "signature": "<protected>..<sig>", "jti": "b1c2d3e4-...", "expiresAt": 1757000000 }}