Pular para o conteúdo

API pública do Spiro Land

Leia e mexa no seu servidor de The Isle a partir do seu site: kill feed, ranking, jogadores, garagem e loja. Cada chave pertence a um servidor, e o dono decide o que ela pode.

URL basehttps://spiro.land/api/v1

Os 112 endpoints da v1 estão no ar.

Comece em três passos

  1. 01Crie a chave

    O dono do servidor cria a chave em Onde fica:Integrações, API, no painel no site. A API faz parte do plano Elder, e o texto da chave aparece uma vez só.

  2. 02Confira a chave

    GET /v1/me responde quem é a chave, de qual servidor, e se a API está aberta para ela. É a chamada certa para testar uma chave nova.

    curl
    curl https://spiro.land/api/v1/me \
      -H "Authorization: Bearer $SPIRO_LAND_KEY"
  3. 03Veja se está pronta

    JSON
    {
      "key": {
        "id": "key_8fK2mQ1xZp0a",
        "name": "Community site",
        "type": "secret",
        "prefix": "sl_sec_Ab3x…9QzT",
        "scopes": ["killfeed:read", "leaderboard:read"],
        "expiresAt": null
      },
      "server": { "name": "My Evrima Server", "panelOnline": true, "panelVersion": "0.1.52" },
      "api": { "status": "ready" },
      "limits": { "perMinute": 120 }
    }

    O campo api.status diz o que falta, em vez de um erro seco:

    ready
    Tudo pronto. Os endpoints do plano respondem.
    panelNotReady
    O painel do servidor ainda não roda ordens da API. O dono atualiza o painel, ou espera a renovação da licença, que leva até 24 h.
    notInPlan
    O plano do servidor não inclui a API.
    suspended
    O Spiro Land suspendeu a API deste servidor. O dono vê o motivo em Onde fica:Integrações, API.

Passo a passo: Ligar o site da comunidade com a API

Chaves e autenticação

Toda chamada leva a chave no cabeçalho Authorization: Bearer <key>. Nunca na URL: uma chave na query é recusada com 400 keyInUrl, porque a URL fica gravada nos registros do proxy e no histórico do navegador.

Chave secreta

sl_sec_
Onde fica
No servidor do seu site, numa variável de ambiente.
O que alcança
Toda permissão que o dono marcar e o plano cobrir, inclusive as que agem no jogo.
Restrição
Até 10 IPs ou faixas CIDR, opcional.
No navegador
Recusada com 403 secretKeyInBrowser.
Teto
120 pedidos por minuto.

Chave pública

sl_pub_
Onde fica
Pode ir no HTML ou no JavaScript da página.
O que alcança
Só dado que já é público no jogo, e nunca escreve:server:readplayers.online:readkillfeed:readleaderboard:readshop:readskins:readperks:readkick:readvoice:read
Restrição
De 1 a 10 domínios, obrigatório.
No navegador
Permitida nos domínios da chave. Fora deles, 403 originNotAllowed.
Teto
60 por minuto por visitante, e 3.000 por minuto na chave.

A trava de domínio impede que outro site use a sua chave pública no navegador de um visitante. Ela não impede um script de servidor que forje o cabeçalho Origin, e é por isso que a chave pública só alcança dado que já é público no jogo.

Limites

Toda resposta diz quanto sobra no minuto:

RateLimit-Limit
o teto da chave por minuto
RateLimit-Remaining
quantos pedidos ainda cabem
RateLimit-Reset
em quantos segundos a conta recomeça

Passou do teto, a resposta é 429 rateLimited com Retry-After em segundos. Espere esse tempo antes de tentar de novo.

Um IP que erra a chave 30 vezes num minuto recebe 429 até o minuto virar, antes mesmo de a chave ser conferida.

O Spiro Land pode dar a uma chave um teto próprio. limits.perMinute em /v1/me mostra o que vale para a sua.

Leituras e kill feed

As leituras respondem da memória da central, com o que o painel do servidor mandou por último. asOf diz quando essa versão chegou, e stale fica verdadeiro quando o painel está sem sinal. Com o painel sem sinal, o que ninguém sabe sai null: se o servidor está ligado e quem está online.

Depois de um tempo sem ninguém consultar, a primeira leitura pede o dado ao painel e espera até 4 segundos por ele. Se não chegar, a resposta é 409 dataNotReady com Retry-After.

O cursor do kill feed

O kill feed vem da morte mais nova para a mais velha. Guarde o cursor da resposta e mande de volta em after: a próxima resposta traz só as mortes depois dele.

after
Só as mortes depois deste id. Sem ele, as mais recentes.
limit
De 1 a 100 mortes por resposta. O padrão é 50.
cursorReset
O painel foi reinstalado e os ids recomeçaram. Limpe a lista e recomece desta resposta.
gap
Houve mais mortes do que esta resposta traz, ou do que a central guarda (100). As do meio não vêm mais.
HTML
<ul id="feed"></ul>
<script>
  const KEY = "sl_pub_...";
  let cursor = null;

  async function update() {
    const url = new URL("https://spiro.land/api/v1/killfeed");
    if (cursor) url.searchParams.set("after", cursor);
    const r = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } });
    if (!r.ok) return;
    const feed = await r.json();
    const list = document.querySelector("#feed");
    if (feed.cursorReset) list.replaceChildren();
    for (const k of feed.kills.reverse()) {
      const li = document.createElement("li");
      li.textContent = `${k.killer?.name ?? "The island"} > ${k.victim.name ?? "Unknown survivor"} (${k.victim.species})`;
      list.prepend(li);
    }
    cursor = feed.cursor;
  }

  update();
  setInterval(update, 10000);
</script>

Um widget de kill feed inteiro, sem dependência, com a chave pública. Ele consulta a cada 10 segundos.

Escritas e idempotência

O que mexe no jogo vira uma ordem para o painel do servidor. A resposta espera o desfecho por até 8 segundos: 200 quando ele chega, ou 202 com Location quando o painel demora. Nesse caso, consulte a ordem até ela terminar.

Mande Idempotency-Key (de 1 a 64 letras, números, _ ou -) para repetir um pedido sem medo: a mesma chave acha a mesma ordem, e o jogo faz uma vez só. Onde mexe em moeda ou item ela é obrigatória. A mesma chave com outro corpo responde 409 idempotencyConflict. A chave vale enquanto a central guarda a ordem: até 30 dias, e menos num servidor com mais de 1.000 ordens encerradas nesse tempo. Depois disso, o mesmo pedido cria uma ordem nova: repita com a mesma chave só dentro desse prazo.

curl
curl -X POST https://spiro.land/api/v1/players/76561198000000001/garage/park \
  -H "Authorization: Bearer $SPIRO_LAND_KEY" \
  -H "Idempotency-Key: park-76561198000000001-2026-09-25" \
  -H "Content-Type: application/json" \
  -d '{ "slot": "a" }'

Com o painel fora do ar nada é enfileirado, e a resposta é 409 panelOffline. Quando o painel recusa, a resposta é 409 refused, com o motivo em reason:

JSON
{
  "error": {
    "code": "refused",
    "message": "The server's panel refused this. The reason field says why.",
    "reason": "cooldown",
    "detail": "The player used the garage too recently.",
    "retryAfterMs": 90000,
    "order": "api_Zx81kQm2Lw0pE4tR9yB6nV3cH7sJ5aDf"
  }
}
garageClosed
O dono fechou a garagem.
cooldown
O jogador usou a garagem há pouco. retryAfterMs diz quanto falta.
insufficientFunds
O jogador não tem saldo para pagar. price e balance dizem quanto custa e quanto ele tem.
slotOccupied
Já há um dino nessa vaga.
garageFull
Todas as vagas que o jogador pode usar estão ocupadas.
slotNotAllowed
O jogador não tem direito a essa vaga pelos cargos dele.
slotChanged
O dino da vaga mudou desde a leitura. Leia a garagem de novo e mande a version nova.
speciesMismatch
O jogador está em outra espécie. Mande swap: true para trocar, sabendo que o dino atual some.
speciesNotPlayable
A troca de espécie ainda não chega a essa espécie. O jogador resgata entrando no jogo com ela, sem swap.
playerOffline
O jogador não está no jogo. Com target: true, é o jogador de destino que não está no jogo com um dino.
notLinked
O jogador ainda não tem carteira neste servidor. Abra a conta sem Discord pelo POST /v1/players/{steamId}/account, ou peça para ele vincular o Discord.
productNotFound
Não há produto com esse id na loja agora.
alreadyOwned
O jogador já tem essa pele para sempre.
itemNotFound
Esse item não está no inventário do jogador, ou já foi removido.
alreadyUnlimited
O item já é ilimitado.
noUsesLeft
O item não tem mais usos para tirar.
purchaseNotFound
Não há compra com esse id neste painel.
purchaseClosed
A compra já foi resolvida por outra pessoa.
refundFailed
O estorno não passou, e o jogador segue sem as moedas. Confira a carteira antes de tentar de novo.
inCombat
O jogador está em combate. Tente de novo em uns 20 segundos.
noDinosaur
O jogador está no jogo sem dino agora: morto, renascendo ou escolhendo a espécie. No slay, vale também para quem está fora do jogo.
invalidChoice
A escolha do grow não serve: falta mutação para um slot, ou uma é desconhecida, repetida ou fora do slot.
notEligible
O grow não serve para a espécie de agora. tiers traz as que servem.
notApplied
O jogo não aplicou (jogador offline, morto ou na seleção). O uso voltou para o item.
notUsable
Este item não se usa por este caminho (por exemplo, pedir as mutações de algo que não é grow), ou o painel do servidor é anterior ao uso da entrega manual e do dino do kit pelo site: atualize o painel.
linkConflict
O Discord já está vinculado a outra Steam neste servidor, ou a Steam a outro Discord. conflict diz qual dos dois. Nada mudou: desvincule antes, se o vínculo antigo estiver errado.
skinNotDrawn
A pele desse produto ainda está por desenhar. Ela não se vende; o carrinho de graça a entrega no painel 0.1.87 ou mais novo, e usar o item recusa sem gastar até o dono desenhar.
outOfStock
Um produto do carrinho esgotou. O estoque é um só para a loja do Discord, o /players e o seu site, no painel 0.1.89 ou mais novo. Vem com o productId; nada foi cobrado.
limitReached
O jogador já levou o máximo de um produto (o limite por jogador da loja). Vem com o productId; nada foi cobrado.
gameNotResponding
O jogo não deu uma resposta que se lesse: o mod não respondeu a tempo, o servidor ainda está subindo o mapa, ou a resposta chegou ilegível. Nada mudou; tente de novo em alguns segundos. As leituras do dino recusam assim no painel 0.1.91 ou mais novo; antes dele vinham como unknown, e o prime de quem estava fora do jogo como noDinosaur.
unknown
O painel recusou por outro motivo. detail traz a frase dele.
alreadyGrown
O dino já está no crescimento do grow ou acima dele, e usar não o faria crescer. growth traz o do item (0 a 1). Nada foi gasto.
closesPrime
O dino ainda não é prime, está abaixo de 75% e o grow, sem prime, o leva a 75% ou mais: o prime desse dino fecha. growth traz o do item (0 a 1). Explique ao jogador e mande acceptPrimeClose true se ele confirmar. Nada foi gasto.
giftsDisabled
A doação de itens está desligada neste servidor (Painel do jogador › Recursos). Nada foi doado.
notGiftable
Este item não se doa: kit inicial, grow grátis ou item que veio de outro servidor. Nada foi doado.
patternOutOfRange
O desenho pedido não existe nessa espécie, pela contagem medida no jogo de hoje. patterns traz quantos ela tem (os índices vão de 0 a patterns menos 1). Nada foi escrito no dino.
growthLimit
O dono ligou o limite de crescimento do teleporte de amigo, e quem vai (ou o amigo, com target) está no limite ou acima. growth traz o crescimento do dino e maxGrowth o limite, os dois de 0 a 1. Ninguém se moveu.
boughtSkin
O dino tem uma pele comprada na loja ou pelo inventário. Peça a confirmação do jogador e mande replaceBought true: a comprada não volta para esse dino. Nada foi pintado e o uso voltou.

Ordens

O que mexe no jogo vira uma ordem, que o painel do servidor executa. GET /v1/orders/{id} mostra onde ela está. Cada chave só enxerga as próprias ordens: a de outra chave responde 404 orderNotFound, igual a uma que não existe.

A central guarda, por servidor, as 1.000 ordens encerradas mais novas, e nenhuma encerrada há mais de 30 dias. Uma ordem na fila ou rodando nunca sai. A que já saiu responde 404 orderNotFound, igual a uma que não existe.

JSON
{
  "order": {
    "id": "api_Zx81kQm2Lw0pE4tR9yB6nV3cH7sJ5aDf",
    "status": "done",
    "createdAt": "2026-09-25T18:04:11.000Z",
    "result": { "ok": true, "message": "Tyrannosaurus stored in slot 2.", "at": "2026-09-25T18:04:13.000Z" }
  }
}
pending
Na fila, esperando o painel buscar.
delivered
O painel levou e está executando.
done
Feita. result traz a mensagem do painel.
failed
O painel tentou e não deu. result diz por quê.
replaced
Uma ordem mais nova tomou o lugar desta antes de o painel buscá-la.
revoked
Cancelada antes de rodar, por exemplo porque a chave foi revogada.

Verificar o jogador

O seu site descobre a conta Steam de quem está no navegador dele, confirmada pela própria Steam, sem o jogador entrar no spiro.land. Ele clica num botão do seu site, confirma na Steam (quase sempre já está logado lá) e volta para o seu site. O SteamID chega ao servidor do seu site pela exchange, e nunca pela URL.

Jogador        clica em "Verificar com a Steam" no seu site
   |
   ▼
Seu servidor   POST /v1/verifications { redirectUri, state }
   |           ← { verification: { id, url } }
   ▼
Navegador      vai para a url → spiro.land → Steam, e confirma lá
   |
   ▼
spiro.land     confere com a Steam e devolve o navegador para
   |           redirectUri?verification=…&state=…&code=…
   ▼
Seu servidor   confere o state da sessão e chama
   |           POST /v1/verifications/{id}/exchange { code }
   |           ← { player: { steamId, name, linked, discordId } }
   ▼
Seu site       guarda o SteamID na sessão do jogador
  1. 01Ligue a permissão e cadastre a volta

    Em Onde fica:Integrações, API, crie ou edite uma chave secreta, marque players:verify e escreva em Endereços de volta da verificação o endereço exato do seu site que recebe o jogador, como https://seusite.com/verificar/volta. A central só devolve o navegador a um endereço dessa lista, comparado por inteiro.

  2. 02Comece pelo servidor do seu site

    Quando o jogador clicar, o servidor do seu site sorteia um state, guarda na sessão dele e chama POST /v1/verifications com o redirectUri e o state. A resposta traz verification.url: redirecione o navegador para ela. A chave secreta nunca vai ao navegador.

    JSON
    {
      "verification": {
        "id": "ver_Ab3xQ9mK2LwP7tRz",
        "status": "pending",
        "url": "https://spiro.land/api/v1/verifications/ver_Ab3xQ9mK2LwP7tRz/steam",
        "expiresAt": "2026-10-05T03:15:00.000Z"
      }
    }
  3. 03Receba a volta

    O jogador volta ao seu redirectUri com verification, state e code, ou com error quando não deu certo. Compare o state com o da sessão e, se for diferente, descarte a volta: é isso que impede alguém de plantar no navegador de outra pessoa uma volta que não é dela.

    https://yoursite.com/verify/return
      ?verification=ver_Ab3xQ9mK2LwP7tRz
      &state=f3Kq9xLm2PwZ7tRb
      &code=Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY
  4. 04Troque o código pelo SteamID

    Do servidor do seu site, chame POST /v1/verifications/{id}/exchange com o code. O player.steamId da resposta é a conta que a Steam confirmou: guarde na sessão e use nos outros endpoints. O código vale uma vez só, por cinco minutos.

    JSON
    {
      "verification": {
        "id": "ver_Ab3xQ9mK2LwP7tRz",
        "status": "exchanged",
        "method": "steam",
        "verifiedAt": "2026-10-05T03:01:12.000Z"
      },
      "player": {
        "steamId": "76561198000000001",
        "name": "Rex",
        "linked": true,
        "discordId": "100000000000000001"
      }
    }

    linked diz se o jogador já vinculou a conta ao Discord do seu servidor. Sem vínculo, abra a conta dele com POST /v1/players/{steamId}/account: ele ganha a carteira de moedas sem passar pelo Discord nem pelo spiro.land, e se vincular o Discord depois, a mesma carteira continua, com o saldo. null quer dizer que o painel ainda não mandou a lista: a verificação vale do mesmo jeito.

Se o seu site já tem login Steam próprio, a verificação não é necessária: o SteamID que a Steam devolveu ao seu login já está provado. Depois do login, chame POST /v1/players/{steamId}/account com uma chave secreta com link:write, e o jogador ganha a conta sem passar pelo Discord nem pelo spiro.land.

JS
// Node 18+ (fetch and crypto are built in). SPIRO_LAND_KEY: a secret key with players:verify and link:write.
import { randomBytes } from "node:crypto";

const API = "https://spiro.land/api/v1";
const RETURN = "https://yoursite.com/verify/return"; // registered on the key
const headers = {
  Authorization: `Bearer ${process.env.SPIRO_LAND_KEY}`,
  "Content-Type": "application/json",
};

// 1. The player clicked "Verify with Steam": redirect the browser to the url this returns.
export async function startVerification(session) {
  session.verifyState = randomBytes(24).toString("base64url");
  const r = await fetch(`${API}/verifications`, {
    method: "POST",
    headers,
    body: JSON.stringify({ redirectUri: RETURN, state: session.verifyState }),
  });
  const body = await r.json();
  if (!r.ok) throw new Error(body.error.code);
  return body.verification.url;
}

// 2. The browser came back to RETURN. query: the parsed query string of that request.
export async function finishVerification(session, query) {
  const expected = session.verifyState;
  delete session.verifyState;
  if (!expected || query.state !== expected) throw new Error("state mismatch");
  if (query.error) throw new Error(query.error); // cancelled, refused, expired...

  const r = await fetch(`${API}/verifications/${encodeURIComponent(query.verification)}/exchange`, {
    method: "POST",
    headers,
    body: JSON.stringify({ code: query.code }),
  });
  const body = await r.json();
  if (!r.ok) throw new Error(body.error.code);
  session.steamId = body.player.steamId; // proven by Steam, never typed by the player

  // 3. No Discord link: open the account, so coins and time income work without Discord.
  if (body.player.linked === false) {
    await fetch(`${API}/players/${body.player.steamId}/account`, { method: "POST", headers });
  }
  return body.player;
}

Quando não dá certo, a volta traz error no lugar de code:

cancelled
O jogador desistiu na página da Steam.
refused
A Steam não confirmou a entrada, ou a volta não veio do mesmo navegador que começou. Comece de novo.
expired
Passou o prazo de 15 minutos para entrar na Steam. Comece de novo.
alreadyStarted
O endereço da verificação já tinha sido aberto (um recarregar ou o voltar do navegador). Crie outra.
alreadyUsed
Essa volta já foi usada. Crie outra verificação.
unavailable
A central não conseguiu falar com a Steam agora. Tente de novo em instantes.

Limites: a verificação vale 15 minutos até a volta da Steam; o código vale 5 minutos e uma troca; cinco códigos errados encerram a verificação; cada chave tem até 300 verificações abertas ao mesmo tempo (429 tooManyVerifications); e cada chamada conta no limite por minuto da chave.

Segurança

  • O SteamID sai da Steam e da troca, nunca de algo que o navegador manda. Não aceite SteamID digitado nem lido da URL.
  • A chave secreta e a troca ficam no servidor do seu site. Usada num navegador, a chave é recusada.
  • Na página da Steam o jogador vê spiro.land: é o Spiro Land que confere com a Steam em nome do seu servidor. Ele não cria conta nem entra no spiro.land, e a Steam nunca entrega a senha a ninguém.
  • Cada verificação é de uma chave: outra chave, mesmo do mesmo servidor, recebe 404 verificationNotFound.
  • A central guarda só a impressão do código, por minutos, e na memória. Um reinício dela encerra as verificações abertas, e o jogador clica de novo.

Mapa ao vivo

Mostre no seu site onde o jogador está e onde está o grupo dele. Há dois jeitos: embutir o mapa pronto do spiro.land num iframe (o mesmo da aba Mapa do painel do jogador, com camadas, busca e zoom) ou desenhar o seu, com a imagem, as zonas e as posições que a API entrega. Nos dois, só aparecem o jogador e quem está no grupo dele naquele momento: ninguém de fora, nem como ponto.

A posição, o calor e o mapa embutido dependem do mapa ao vivo ligado em Onde fica:Jogadores, Painel do jogador. A imagem e as zonas, não.

Embutir o mapa

  1. 01Dê a permissão e os sites

    Em Onde fica:Integrações, API, numa chave secreta, marque map:read e liste em Sites que podem embutir o mapa os endereços do seu site, como https://seusite.com. O mapa só abre dentro deles.

  2. 02Abra o embutido no seu servidor

    Quando o jogador abrir a página, o servidor do seu site chama POST /v1/players/{steamId}/map/embed com o SteamID de quem entrou no seu site (pela verificação ou pelo seu login Steam). Abra um por carga da página: ele vale enquanto alguém olha.

    JSON
    {
      "embed": {
        "id": "emb_Ab3xQ9mK2LwP7tRz",
        "url": "https://spiro.land/embed/map?id=emb_Ab3xQ9mK2LwP7tRz#Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY",
        "expiresAt": "2026-10-07T23:40:00.000Z"
      }
    }
  3. 03Ponha a url num iframe

    Use embed.url como o src de um iframe. O mapa se ajusta à largura que você der: estreito, a busca, as camadas e o grupo vão para baixo do mapa.

    HTML
    <iframe
      src="{embed.url}"
      title="Live map"
      style="width: 100%; height: 720px; border: 0"
    ></iframe>

O id vai na consulta e o segredo depois do #, que o navegador não manda a servidor nenhum; a página do mapa o tira do endereço ao abrir. O embutido vence depois de uma hora sem ninguém olhando, e em doze horas no máximo: vencido, o mapa pede para recarregar a página do seu site, que abre outro.

JS
// Node 18+, no dependencies. Run:
//   SPIRO_LAND_KEY=sl_sec_... STEAM_ID=76561198000000001 node spiro-map-embed.mjs
// The key needs the Map permission, with http://localhost:3000 in its map websites.
import { createServer } from "node:http";

const API = "https://spiro.land/api/v1";

createServer(async (req, res) => {
  if (req.url !== "/") return res.writeHead(404).end();

  // On your site, STEAM_ID is the player who signed in (verification or your own Steam login).
  const r = await fetch(`${API}/players/${process.env.STEAM_ID}/map/embed`, {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.SPIRO_LAND_KEY}` },
  });
  const body = await r.json();
  if (!r.ok) return res.writeHead(r.status, { "Content-Type": "text/plain" }).end(body.error.code);

  // A new embed for every page load: it lasts while someone looks at it.
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(`<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Live map</title>
<iframe src="${body.embed.url}" title="Live map" style="width:100%;height:90vh;border:0"></iframe>`);
}).listen(3000, () => console.log("Open http://localhost:3000"));

Baixar o exemplo pronto (spiro-map-embed.mjs) Rode com a sua chave secreta e um SteamID, e abra http://localhost:3000.

Desenhar o seu próprio mapa

GET /v1/map entrega tudo o que o seu mapa precisa, com a mesma chave secreta e a permissão map:read: os limites do Gateway em centímetros, as camadas da imagem com a versão de cada uma, os rótulos e as zonas.

JSON
{
  "map": "Gateway",
  "liveMap": true,
  "heat": true,
  "bounds": { "xMin": -505000, "xMax": 607000, "yMin": -607000, "yMax": 509000 },
  "image": { "width": 975, "height": 977, "contentType": "image/webp" },
  "layers": [
    { "id": "color", "path": "/v1/map/image?layer=color", "version": "3f9a1c0b7e2d", "bytes": 185456, "overlay": false },
    { "id": "gray", "path": "/v1/map/image?layer=gray", "version": "a81e44c09d15", "bytes": 138000, "overlay": false },
    { "id": "water", "path": "/v1/map/image?layer=water", "version": "5c02d9e7b3f8", "bytes": 22986, "overlay": true }
  ],
  "svg": { "viewBox": "-505 -607 1112 1116", "unitCm": 1000 },
  "labels": [
    { "kind": "region", "name": "Delta", "x": 177000, "y": 33000, "detail": 0 }
  ],
  "zones": [
    { "kind": "sanctuary", "name": "Delta side", "center": { "x": 228000, "y": -18000 },
      "path": "M217 -18A11 11 0 0 0 239 -18A11 11 0 0 0 217 -18Z" }
  ]
}

GET /v1/map/image?layer= devolve o WebP de 975 por 977 de uma camada: color, gray ou water (a água doce, transparente, para pôr por cima da cor). A imagem leva spiro.land num canto: mantenha a marca. Baixe pelo servidor do seu site uma vez por version e sirva do seu próprio endereço; a chave nunca vai ao navegador.

curl
curl https://spiro.land/api/v1/map/image?layer=color \
  -H "Authorization: Bearer $SPIRO_LAND_KEY" \
  -o gateway-color.webp

A resposta traz a versão no ETag. Mande o mesmo valor em If-None-Match e, sem mudança, a resposta é 304, sem corpo. Mais simples ainda: compare o version de GET /v1/map com o que você guardou.

GET /v1/players/{steamId}/map devolve o jogador e o grupo dele, com nome, espécie e há quantos milissegundos a posição foi lida. A primeira leitura acorda o painel do servidor e volta 409 dataNotReady: leia de novo a cada 5 segundos enquanto a página estiver aberta. Sem dino em campo, player vem null.

JSON
{
  "map": "Gateway",
  "status": "ok",
  "groupsRead": true,
  "player": { "name": "Rex", "species": "Carnotaurus", "x": 12000, "y": -48000, "seenAgoMs": 2400 },
  "group": [
    { "name": "Ana", "species": "Tyrannosaurus", "x": 20000, "y": -40000, "seenAgoMs": 2400 }
  ],
  "asOf": "2026-10-07T22:40:03.000Z",
  "stale": false
}

x e y estão em centímetros do jogo, as mesmas coordenadas do teleporte, com precisão de dez metros. A conta abaixo leva ao pixel da imagem da API, com a origem no canto de cima à esquerda.

JS
// bounds and image come from GET /v1/map. The origin is the top left corner.
function toPixel(x, y, bounds, image) {
  return {
    left: ((x - bounds.xMin) / (bounds.xMax - bounds.xMin)) * image.width,
    top: ((y - bounds.yMin) / (bounds.yMax - bounds.yMin)) * image.height,
  };
}

zones traz santuários, migração, patrulha e cavernas, e labels os nomes das regiões, águas e lugares. O path de cada zona é um desenho de SVG em unidades do mapa (dez metros): ponha num svg com o svg.viewBox por cima da imagem e ele cai no lugar. detail vai de 0 (aparece de longe) a 2 (só de perto).

HTML
<div style="position: relative; aspect-ratio: 975 / 977">
  <img src="/map.webp" alt="Gateway map" style="width: 100%; height: 100%">
  <svg viewBox="-505 -607 1112 1116" style="position: absolute; inset: 0; width: 100%; height: 100%">
    <path d="M217 -18A11 11 0 0 0 239 -18A11 11 0 0 0 217 -18Z" fill="rgb(52 211 153 / .25)" />
  </svg>
</div>

GET /v1/map/heat devolve onde a população do servidor anda: uma grade de 48 por 48, com um dígito de 0 a 8 por célula em levels[row * columns + column], a linha 0 em cima. Só contagem, sem nome nem ponto, e só no plano que tem o mapa de calor. Sem medição na última hora, vem "status": "cold".

JSON
{
  "status": "ok",
  "columns": 48,
  "rows": 48,
  "levels": "000000000001233210000…",
  "samples": 37,
  "measuredAt": "2026-10-07T22:39:00.000Z",
  "bounds": { "xMin": -505000, "xMax": 607000, "yMin": -607000, "yMax": 509000 }
}
JS
// Node 18+, no dependencies. Run:
//   SPIRO_LAND_KEY=sl_sec_... STEAM_ID=76561198000000001 node spiro-map-own.mjs
// The key needs the Map permission. Your server keeps the image: the key never reaches the browser.
import { createServer } from "node:http";

const API = "https://spiro.land/api/v1";
const api = (path) => fetch(`${API}/${path}`, { headers: { Authorization: `Bearer ${process.env.SPIRO_LAND_KEY}` } });
const esc = (text) => text.replace(/[&<>"]/g, (c) => "&#" + c.charCodeAt(0) + ";");

// The map and the image, downloaded again only when the version changes.
let map = null;
let image = null;
async function refresh() {
  map = await (await api("map")).json();
  const color = map.layers.find((layer) => layer.id === "color");
  if (image?.version !== color.version) {
    image = { version: color.version, bytes: Buffer.from(await (await api("map/image?layer=color")).arrayBuffer()) };
  }
}
setInterval(refresh, 6 * 60 * 60 * 1000);

createServer(async (req, res) => {
  if (map === null) await refresh();
  if (req.url === "/map.webp") {
    return res.writeHead(200, { "Content-Type": "image/webp", "Cache-Control": "public, max-age=86400" }).end(image.bytes);
  }
  if (req.url === "/position") {
    // On your site, STEAM_ID is the player who signed in (verification or your own Steam login).
    const r = await api(`players/${process.env.STEAM_ID}/map`);
    return res.writeHead(r.status, { "Content-Type": "application/json" }).end(await r.text());
  }
  if (req.url !== "/") return res.writeHead(404).end();

  const sanctuaries = map.zones
    .filter((zone) => zone.kind === "sanctuary")
    .map((zone) => `<path d="${zone.path}"><title>${esc(zone.name)}</title></path>`)
    .join("");
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(`<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Live map</title>
<div style="position:relative;max-width:900px;aspect-ratio:${map.image.width}/${map.image.height}">
  <img src="/map.webp" alt="Gateway map" style="display:block;width:100%;height:100%">
  <svg viewBox="${map.svg.viewBox}" style="position:absolute;inset:0;width:100%;height:100%"
    fill="rgb(52 211 153 / .25)" stroke="rgb(52 211 153)" stroke-width="1">${sanctuaries}</svg>
  <div id="me" hidden style="position:absolute;width:12px;height:12px;margin:-6px;border-radius:50%;background:#f43f5e;outline:2px solid #fff"></div>
</div>
<script>
  const b = ${JSON.stringify(map.bounds)};
  async function tick() {
    const body = await (await fetch("/position")).json();
    const me = document.getElementById("me");
    me.hidden = !body.player;
    if (!body.player) return;
    me.style.left = ((body.player.x - b.xMin) / (b.xMax - b.xMin)) * 100 + "%";
    me.style.top = ((body.player.y - b.yMin) / (b.yMax - b.yMin)) * 100 + "%";
  }
  tick();
  setInterval(tick, 5000);
</script>`);
}).listen(3000, () => console.log("Open http://localhost:3000"));

Baixar o exemplo pronto (spiro-map-own.mjs) Rode com a sua chave secreta e um SteamID, e abra http://localhost:3000: a imagem, os santuários e o jogador, relido a cada 5 segundos.

Segurança

  • Só a chave secreta lê o mapa. Chame a API do servidor do seu site; o navegador recebe só a url do embutido, ou a imagem e as posições pelo seu servidor.
  • Abra o embutido e leia a posição só do jogador que entrou no seu site: quem tem a url vê aquele jogador e o grupo dele.
  • A página do mapa só abre dentro de um iframe, e só nos sites da chave. Aberta numa aba ela é 404, e o navegador recusa desenhá-la num site que não está na lista.
  • Revogar a chave, tirar a permissão do mapa ou desligar o mapa ao vivo derruba os embutidos na leitura seguinte; desligado, a API responde 403 liveMapOff.
  • A imagem sai só pela chave secreta com a permissão do mapa, com spiro.land gravado num canto. Sirva do seu servidor e não recorte a marca.

Kick: moedas por assistir e conversar

O jogador liga a Kick dele no seu site e passa a ganhar moedas na carteira do servidor assistindo e conversando na live do streamer parceiro, na própria Kick. O spiro.land conta, junta os centavos e credita; o seu site mostra os canais e a regra, e abre a ligação da conta. O jogador não precisa entrar no spiro.land.

Tudo aqui depende da Kick configurada em Onde fica:Economia, Moedas pela Kick: ao menos um canal conectado e a regra ligada. O plano do servidor precisa ter a economia.

Mostrar os canais e a regra

GET /v1/kick devolve os canais conectados, quem está ao vivo e o link de cada um, a regra e a moeda do servidor. Ele pede kick:read e funciona também com a chave pública: é o que a Kick e o painel do jogador já mostram.

JSON
{
  "status": "ready",
  "currency": { "singular": "coin", "plural": "coins" },
  "rule": {
    "coinsPerInterval": 10,
    "intervalMinutes": 10,
    "coinsPerMessage": 0.1,
    "messageCooldownSeconds": 30,
    "packages": [
      { "minutes": 10, "points": 20, "coins": 10 },
      { "minutes": 60, "points": 120, "coins": 60 }
    ]
  },
  "channels": [
    { "slug": "goldzeraa", "name": "Goldzeraa", "live": true, "url": "https://kick.com/goldzeraa" }
  ]
}
ready
Regra ligada e ao menos um canal: assistir e conversar pagam.
off
A regra está desligada ou não há canal conectado. Nada paga até a administração ligar.
unavailable
A Kick está fora do ar no spiro.land agora, ou o plano não a inclui.

O tempo assistido paga pelos pontos do canal: o espectador junta pontos assistindo logado na Kick e troca por um pacote no menu de recompensas do canal. Cada pacote em packages custa points e vale a parte de coinsPerInterval pelos minutos dele. Cada mensagem com a live no ar paga coinsPerMessage, uma por pessoa a cada messageCooldownSeconds. O que fica abaixo de uma moeda acumula e entra na carteira quando fecha uma.

Ligar a Kick do jogador

  1. 01Dê a permissão e o endereço de volta

    Em Onde fica:Integrações, API, numa chave secreta, marque kick:write e cadastre em Endereços de volta o endereço do seu site que recebe o jogador depois da Kick, como https://seusite.com/ligar/volta.

  2. 02Abra a ligação no seu servidor

    Quando o jogador clicar para ligar a Kick, o servidor do seu site chama POST /v1/players/{steamId}/kick-link com o SteamID de quem entrou no seu site (pela verificação ou pelo seu login Steam), o redirectUri cadastrado e um state aleatório guardado na sessão dele. Mande o navegador para kickLink.url.

    JSON
    {
      "kickLink": {
        "id": "kl_Ab3xQ9mK2LwP7tRz",
        "status": "pending",
        "url": "https://spiro.land/api/v1/kick-links/kl_Ab3xQ9mK2LwP7tRz/start",
        "expiresAt": "2026-10-11T15:15:00.000Z"
      }
    }
  3. 03O jogador autoriza na Kick e volta

    O spiro.land só redireciona: o jogador vê a tela de autorização da Kick e volta ao redirectUri com kickLink, state e code, ou com error. Compare o state com o da sessão antes de qualquer coisa.

    https://yoursite.com/kick/return
      ?kickLink=kl_Ab3xQ9mK2LwP7tRz
      &state=f3Kq9xLm2PwZ7tRb
      &code=Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY
  4. 04Troque o código

    O servidor do seu site chama POST /v1/kick-links/{linkId}/exchange com o code. Só aí o vínculo grava: dali em diante, o que o jogador ganha na Kick cai na carteira dele. redemptionsCredited diz quantos resgates feitos antes do vínculo foram pagos agora.

    JSON
    {
      "kickLink": { "id": "kl_Ab3xQ9mK2LwP7tRz", "status": "linked", "linkedAt": "2026-10-11T15:01:12.000Z" },
      "player": { "steamId": "76561198000000001" },
      "account": { "name": "rex_on_kick" },
      "redemptionsCredited": 0
    }

Quando algo dá errado, a volta traz error no lugar de code:

cancelled
O jogador recusou na tela da Kick.
otherAccount
Esta conta da Kick já está ligada a outra Steam. O jogador desliga lá antes.
refused
A volta chegou de um navegador que não começou esta ligação. Nada mudou.
expired
A ligação venceu (15 minutos) ou já tinha acabado. Abra outra.
alreadyStarted
O endereço da ligação foi aberto mais de uma vez. Abra outra.
alreadyUsed
A volta da Kick já tinha chegado antes.
failed
A Kick não confirmou a conta. Tente de novo.
unavailable
A ligação de contas da Kick está fora do ar agora.

O que o jogador está juntando

GET /v1/players/{steamId}/kick-link, com players:read, diz se o jogador ligou a Kick, com o nome da conta, e accumulating: o que ele ganhou abaixo de uma moeda, que entra na carteira quando fecha uma. As moedas inteiras já estão na carteira.

JSON
{
  "linked": true,
  "account": { "name": "rex_on_kick", "linkedAt": "2026-10-11T15:01:12.000Z" },
  "accumulating": 0.7,
  "pendingRedemptions": 0
}

DELETE /v1/players/{steamId}/kick-link desfaz o vínculo e responde { "unlinked": true }. As mensagens e os resgates seguintes deixam de pagar a esta Steam.

JS
// Node 18+ (fetch and crypto are built in). SPIRO_LAND_KEY: a secret key with kick:write and
// players:read, with RETURN in its return addresses.
import { randomBytes } from "node:crypto";

const API = "https://spiro.land/api/v1";
const RETURN = "https://yoursite.com/kick/return"; // registered on the key
const headers = {
  Authorization: `Bearer ${process.env.SPIRO_LAND_KEY}`,
  "Content-Type": "application/json",
};

// 1. The signed-in player clicked "Link Kick": redirect the browser to the url this returns.
//    steamId is the player your site already proved (verification or your own Steam login).
export async function startKickLink(session, steamId) {
  session.kickState = randomBytes(24).toString("base64url");
  const r = await fetch(`${API}/players/${steamId}/kick-link`, {
    method: "POST",
    headers,
    body: JSON.stringify({ redirectUri: RETURN, state: session.kickState }),
  });
  const body = await r.json();
  if (!r.ok) throw new Error(body.error.code);
  return body.kickLink.url;
}

// 2. The browser came back to RETURN. query: the parsed query string of that request.
export async function finishKickLink(session, query) {
  const expected = session.kickState;
  delete session.kickState;
  if (!expected || query.state !== expected) throw new Error("state mismatch");
  if (query.error) throw new Error(query.error); // cancelled, otherAccount, expired...

  const r = await fetch(`${API}/kick-links/${encodeURIComponent(query.kickLink)}/exchange`, {
    method: "POST",
    headers,
    body: JSON.stringify({ code: query.code }),
  });
  const body = await r.json();
  if (!r.ok) throw new Error(body.error.code);
  return body.account.name; // the Kick account now earning coins for this player
}

// 3. The Kick card on your site: the channels, the rule and what the player is collecting.
export async function kickCard(steamId) {
  const [kick, link] = await Promise.all([
    fetch(`${API}/kick`, { headers }).then((r) => r.json()),
    fetch(`${API}/players/${steamId}/kick-link`, { headers }).then((r) => r.json()),
  ]);
  return { kick, link };
}

Segurança

  • Só a chave secreta liga e lê o vínculo. Chame a API do servidor do seu site; o navegador recebe só a url da ligação.
  • Abra a ligação só para a Steam de quem entrou no seu site: é a Steam que você mandou que passa a receber o que aquela Kick ganhar.
  • Compare o state antes de trocar: um link que vazou volta no navegador de outra pessoa, com o state da sessão dela, e não deve ser trocado.
  • Uma conta da Kick paga uma Steam só. Ligada a outra, a troca responde 409 kickAccountInUse e nada muda.
  • As moedas saem do spiro.land, pela regra do servidor. O seu site não credita nada pela Kick: ele só mostra e liga.

Voz de proximidade

O jogador fala com quem está perto do dino dele, pela aba Voz do painel do jogador no spiro.land ou dentro do site do servidor. O áudio vai direto entre o navegador e o painel da VPS do servidor. Pela API, o seu site mostra se a voz está no ar, abre a voz para o jogador e modera.

A chave nunca escolhe quem entra na voz. Quem diz qual dino é o jogador é o próprio jogador, com a conta dele, numa janela do spiro.land. Por isso não há endpoint que abra a voz para um SteamID.

  • O estado da voz: ligada, no ar, a porta, quantos estão nela e o link para entrar.
  • Se um jogador pode entrar e se ele está fora da voz.
  • Tirar e devolver um jogador da voz.
  • A voz dentro do seu site, num iframe: pronta no Dash, sem programar, ou aberta pelo seu servidor. O login do jogador é numa janela do spiro.land.

O que não existe, e não vai existir:

  • Abrir a voz por um jogador, ouvir a voz ou falar por alguém.
  • Saber quem fala, quem ouve quem, o volume ou onde cada um está.
  • Saber se um jogador está na voz agora.

Tudo depende da voz ligada pelo dono em Onde fica:Jogadores, Painel do jogador, e do plano do servidor com a voz.

O estado e o link

GET /v1/voice com voice:read, que a chave pública também recebe. Responde sempre 200 com plano válido, e status diz o estado.

JSON
{
  "status": "ready",
  "enabled": true,
  "panel": { "ready": true, "online": true },
  "port": { "status": "ok", "transport": "udp", "checkedAt": "2026-10-13T18:40:00.000Z" },
  "inVoice": 12,
  "rangeMeters": 50,
  "joinUrl": "https://spiro.land/players?s=Ab3xQ9mK2LwP7tRzYc1VbQ&tab=voice",
  "asOf": "2026-10-13T18:41:02.000Z",
  "stale": false
}
ready
Ligada e no ar: o jogador entra pelo link.
off
A administração do servidor desligou a voz.
panelNotReady
O painel do servidor ainda não tem a voz: precisa ser atualizado.
panelOffline
O painel do servidor está fora do ar agora.
portFailed
Jogadores tentaram e ninguém conectou: a porta da voz está fechada no provedor.
untested
Ligada, e ninguém entrou ainda: a porta não foi testada.

port diz se a porta funciona e por qual transporte, e nunca traz IP nem número. joinUrl leva à aba Voz do painel do jogador e usa o id público do servidor, que muda quando o dono troca a chave do painel: leia daqui, não guarde.

Um botão que abre a voz

O jeito mais simples com a API: o seu site abre joinUrl numa aba nova. É a aba Voz do painel do jogador: o login e a voz rodam no spiro.land, o microfone fica com o spiro.land, e o seu site não muda cabeçalho nenhum.

HTML
<!-- A public key with voice:read, with your site in its origins. -->
<button id="voice" hidden>Join voice</button>
<script>
  const KEY = "sl_pub_...";
  fetch("https://spiro.land/api/v1/voice", { headers: { Authorization: `Bearer ${KEY}` } })
    .then((r) => r.json())
    .then((voice) => {
      if (voice.status !== "ready") return; // off, panelOffline...: keep the button hidden
      const button = document.getElementById("voice");
      button.textContent = `Join voice (${voice.inVoice} in voice now)`;
      button.hidden = false;
      // The Voice tab of the player panel: the player signs in there, never on your site.
      button.onclick = () => window.open(voice.joinUrl, "_blank", "noopener");
    });
</script>

Sem programar: o código do Dash

Em Onde fica:Jogadores, Painel do jogador, Voz de proximidade, Voz no seu site, o dono escreve o endereço do site e copia um iframe pronto. Não precisa de chave, de servidor nem de código: o iframe não vence, só aparece nos endereços que ele escreveu, e continua valendo quando a VPS muda.

HTML
<iframe src="https://spiro.land/embed/voice?id=vs_Ab3xQ9mK2LwP7tRz" title="Proximity voice"
        allow="microphone; autoplay"
        style="width: 100%; max-width: 480px; height: 560px; border: 0"></iframe>

Gerar outro código no Dash desliga o antigo na hora, e quem tinha entrado por ele entra de novo. O que vem abaixo é para quem programa e quer abrir o iframe pelo próprio servidor, a cada visita.

Pelo seu servidor, com a API

Num iframe, a voz fica na sua página. O jogador entra com a conta dele numa janela do spiro.land, que devolve ao iframe uma credencial só para documentos do spiro.land. Ela pula o login por até 30 dias; cada entrada na voz continua pedindo o clique do jogador e confere de novo o vínculo e o ban.

  1. 01Abra o embutido pelo seu servidor

    Dê a permissão voice:embed a uma chave secreta, com o seu site na lista de sites, em Onde fica:Integrações, API. Para cada visita, o seu servidor chama POST /v1/voice/embed, sem SteamID nenhum, e recebe o endereço do iframe.

    JSON
    {
      "embed": {
        "id": "emb_Ab3xQ9mK2LwP7tRz",
        "url": "https://spiro.land/embed/voice?id=emb_Ab3xQ9mK2LwP7tRz#Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY",
        "expiresAt": "2026-10-13T19:41:02.000Z"
      }
    }
  2. 02Ponha o iframe na página

    Com allow="microphone; autoplay": sem ele, o navegador não deixa o iframe pedir o microfone.

    HTML
    <iframe src="{embed.url}" title="Proximity voice"
            allow="microphone; autoplay"
            style="width: 100%; height: 420px; border: 0"></iframe>
  3. 03Confira os cabeçalhos do seu site

    A sua página precisa deixar o spiro.land em frames e não pode usar COOP same-origin: com ele, a janela do login nasce sem caminho de volta ao iframe, e o iframe pede ao jogador que use a janela que abriu (vira o botão do passo anterior). Se você manda uma Permissions-Policy, inclua o spiro.land no microfone.

    Content-Security-Policy: frame-src https://spiro.land
    Cross-Origin-Opener-Policy: same-origin-allow-popups
    Permissions-Policy: microphone=(self "https://spiro.land")

No iframe, o navegador guarda a permissão do microfone para o SEU site, e não para o spiro.land. O jogador vê o seu endereço na pergunta do microfone.

Um jogador

GET /v1/players/{steamId}/voice, com players:read (só chave secreta), diz se aquele jogador pode entrar e por que não. Nunca diz se ele está na voz.

ready
Pode entrar pelo link.
off
A voz está desligada no servidor.
banned
Está fora da voz: o voiceBan diz até quando e por quê.
noProof
O vínculo dele não foi provado pela Steam: ele vincula a conta no spiro.land antes de entrar.
panelNotReady
O painel do servidor ainda não tem a voz.
panelOffline
O painel do servidor está fora do ar agora.

Moderar

POST /v1/players/{steamId}/voice-ban com voice:write tira o jogador da voz, com motivo de 1 a 200 caracteres e durationMinutes de 1 a 525600 minutos, ou null para sem prazo. Funciona com a voz desligada, para moderar antes de ligar.

JSON
{
  "voiceBan": {
    "steamId": "76561198000000002",
    "until": "2026-10-14T18:00:00.000Z",
    "reason": "Ofensas na voz",
    "createdBy": "api:key_8fK2mQ1xZp0a",
    "createdAt": "2026-10-13T18:00:00.000Z"
  },
  "disconnect": "requested"
}

disconnect diz se havia uma sessão para derrubar. DELETE /v1/players/{steamId}/voice-ban devolve a voz. GET /v1/voice/bans com bans:read lista quem está fora. O Dash mostra os mesmos, com a autoria da chave.

Cada servidor tem até 500 jogadores fora da voz ao mesmo tempo. Passou disso, o próximo responde 409 refused com a razão tooManyVoiceBans: devolva a voz a alguém antes.

Privacidade

  • A Spiro não grava nem transcreve a voz, e o áudio não passa pela Spiro: vai do navegador ao painel da VPS do servidor.
  • Nenhuma resposta traz posição, quem fala, quem ouve quem ou volume. A contagem de quem está na voz é agregada.
  • Com a voz desligada, abrir o embutido responde 403 voiceOff, e quem estava na voz sai.
  • Um jogador tem uma sessão de voz só: abrir em outro lugar derruba a anterior, e o jogo avisa na tela quando a voz liga.

Erros

Todo erro tem a mesma forma. code é o contrato e não muda; message é para você ler, em inglês, e pode mudar.

JSON
{
  "error": {
    "code": "scopeMissing",
    "message": "This key does not have the permission this endpoint needs.",
    "scope": "killfeed:read"
  }
}

Toda resposta leva X-Request-Id. Cite esse id se falar com o suporte.

A API não responde 5xx num estado esperado: painel fora do ar, dado que ainda não chegou e API desligada voltam como 4xx, como 409 panelOffline, e quem decide o que fazer é o code, nunca só o número. Um 5xx é defeito nosso (500 internal), e a hospedagem pode trocá-lo por uma página HTML com HTTP 200. Trate resposta que não é JSON como esse defeito: numa escrita, repita com a mesma Idempotency-Key, que o jogo faz uma vez só.

Todos os códigos de erro53
  • 400keyInUrlA chave veio na URL. Mande no cabeçalho Authorization.
  • 400invalidBodyO corpo não é JSON válido.
  • 401missingKeyFaltou o cabeçalho Authorization.
  • 401invalidKeyA chave não existe ou está mal formada. Confira o texto inteiro.
  • 401revokedKeyA chave foi revogada. Peça uma nova ao dono do servidor.
  • 401expiredKeyA chave venceu. Peça uma nova ao dono do servidor.
  • 403secretKeyInBrowserChave secreta chamada do navegador. Mova a chamada para o servidor do seu site.
  • 403originNotAllowedA chave pública não permite este domínio. O dono acrescenta o domínio na chave.
  • 403ipNotAllowedA chave secreta não permite este IP. O dono acrescenta o IP na chave.
  • 403scopeMissingA chave não tem a permissão deste endpoint. O campo scope diz qual falta.
  • 403publicKeyReadOnlyChave pública tentando escrever. Use uma secreta, a partir de um servidor.
  • 403apiNotInPlanO plano do servidor não inclui a API.
  • 403apiSuspendedO Spiro Land suspendeu a API deste servidor. O dono vê o motivo em Onde fica:Integrações, API.
  • 403notInPlanO plano do servidor não inclui este dado.
  • 403redirectNotAllowedO redirectUri não é um dos endereços de volta cadastrados nesta chave, em Onde fica:Integrações, API. A comparação é do endereço inteiro, e a recusa traz o redirectUri como a central leu: é esse que se cadastra.
  • 403invalidCodeO código não confere com a verificação. O campo attemptsLeft diz quantas tentativas restam; na quinta errada ela se encerra.
  • 403liveMapOffA administração do servidor desligou o mapa ao vivo. A posição, o calor e o mapa embutido voltam quando ela religar.
  • 403embedSitesMissingA chave não lista nenhum site que pode embutir o mapa. O dono do servidor adiciona na chave, em Onde fica:Integrações, API.
  • 403voiceOffA administração do servidor desligou a voz de proximidade. O embutido volta quando ela religar.
  • 403skinsOffO dono desligou as peles dos jogadores em Painel do jogador › Peles dos jogadores. Nada delas responde até ele religar.
  • 403playerPanelOffO dono desligou o painel do jogador, e o painel só aplica a pele do jogador com ele ligado. Guardar e editar peles continuam funcionando.
  • 404notFoundNão existe endpoint neste caminho.
  • 404notAvailableYetO endpoint está documentado e ainda não entrou no ar.
  • 404orderNotFoundNão há ordem com este id para esta chave.
  • 404verificationNotFoundNão há verificação com esse id para esta chave: ela não existe, é de outra chave, ou a central reiniciou.
  • 404kickLinkNotFoundNão há ligação da Kick com esse id para esta chave: ela não existe, é de outra chave, ou a central reiniciou.
  • 404skinNotFoundO jogador não tem pele guardada com esse id. Ela pode ter sido apagada pelo seu site ou pelo /players.
  • 409panelNotReadyO painel do servidor ainda não roda ordens da API, ou não conhece o campo que veio em field (com a versão que falta em panelVersion). O dono atualiza o painel.
  • 409panelOfflineO painel do servidor está fora do ar, e nada foi enfileirado. Tente de novo depois de Retry-After segundos, com a mesma Idempotency-Key; lastSeenAt diz quando o painel foi visto.
  • 409dataNotReadyO painel do servidor ainda não mandou este dado. Tente de novo depois de Retry-After segundos; com o painel no ar, leva poucos segundos.
  • 409apiDisabledA API, ou a parte dela que este endpoint usa (a verificação, o mapa embutido), está fora do ar por um momento. Tente de novo depois de Retry-After, quando ele vier, ou mais tarde.
  • 409kickUnavailableA ligação de contas da Kick está fora do ar agora. Tente de novo depois de Retry-After.
  • 409idempotencyConflictEsta Idempotency-Key já foi usada com outro pedido. Pedido novo pede chave nova.
  • 409refusedO painel do servidor recusou. O campo reason diz por quê, e detail traz a frase do painel.
  • 409verificationNotReadyO jogador ainda não voltou da Steam. Troque o código depois que ele chegar ao seu endereço de volta.
  • 409verificationUsedEssa verificação já foi trocada. Cada código vale uma vez.
  • 409kickLinkNotReadyO jogador ainda não voltou da Kick. Troque o código quando ele voltar ao seu site.
  • 409kickLinkUsedEsta ligação já foi trocada. Cada código vale uma vez.
  • 409kickAccountInUseEsta conta da Kick está ligada a outra Steam. O jogador desliga lá antes, e nada mudou aqui.
  • 409playerNotLinkedA conta Steam não tem um Discord vinculado neste servidor, e aplicar a pele roda como o jogador. Vincule antes (POST .../link ou no servidor).
  • 409skinStorageFullO jogador já tem tantas peles guardadas quanto o dono permite (limit). Apague uma antes de guardar outra.
  • 410verificationExpiredA verificação acabou: venceu, o jogador desistiu, a Steam recusou, ou o código errou demais. Comece outra.
  • 410kickLinkExpiredA ligação acabou: venceu, o jogador cancelou, a Kick recusou ou o código errado foi mandado vezes demais. Abra outra.
  • 413bodyTooLargeO corpo passou de 16 KB.
  • 422invalidFieldO campo field diz qual parte do pedido não vale: um SteamID torto, um número fora da faixa.
  • 428idempotencyKeyRequiredEste endpoint mexe em moeda ou item. Mande o cabeçalho Idempotency-Key, para uma nova tentativa nunca fazer duas vezes.
  • 429rateLimitedPassou do teto. Espere Retry-After segundos; retryAfterMs diz o mesmo em milissegundos.
  • 429queueFullHá ordens demais da API esperando o painel deste servidor. Espere Retry-After segundos.
  • 429tooManyVerificationsEsta chave tem verificações abertas demais (até 300 ao mesmo tempo). Espere algumas terminarem e tente depois de Retry-After.
  • 429tooManyEmbedsEsta chave tem mapas embutidos abertos demais (até 2.000 ao mesmo tempo). Tente de novo depois de Retry-After.
  • 429applicationLimitReachedO jogador usou todas as aplicações da janela. used, allowed e windowMinutes dizem o limite; nextInSeconds e Retry-After, quando a próxima libera. Aplicação recusada nunca conta.
  • 500internalDefeito nosso, e o único código 5xx. A hospedagem pode trocar esta resposta por uma página HTML com HTTP 200: trate resposta que não é JSON como este código. Cite o X-Request-Id, quando ele vier, ao falar com o suporte.

Permissões

Cada chave tem as permissões que o dono marcou, e um endpoint sem a permissão dele responde 403 scopeMissing. As que agem mexem no jogo e só existem na chave secreta.

Servidor, mortes ao vivo e placar4
  • server:readEstado do servidor e quantos estão onlineChave pública
  • players.online:readQuem está online agoraChave pública
  • killfeed:readAs mortes ao vivo (kill feed), com cursorChave pública
  • leaderboard:readO placar de abatesChave pública
Jogadores4
  • players:readJogadores vinculados, Discord e saldo
  • players:verifyVerificar o jogador pela Steam no seu site, sem ele entrar no spiro.land
  • activity:readOs minutos em call e jogando de cada jogador, com cursor
  • link:writeVincular o Discord que o seu site provou, abrir conta sem Discord e desvincularMuda o jogo
Mapa1
  • map:readO mapa (a imagem com a marca e as zonas), as zonas ativas agora, onde o jogador e o grupo dele estão, o mapa de calor e o mapa embutido no seu site
Loja e vantagens3
  • shop:readOs produtos da lojaChave pública
  • skins:readA biblioteca de pelesChave pública
  • perks:readAs vantagens por cargoChave pública
Garagem4
  • garage:readA garagem de um jogador
  • garage.admin:readA vaga crua, como a equipe vê
  • garage:writeGuardar, resgatar e excluir dinoMuda o jogo
  • garage.admin:writeCriar e editar dino guardadoMuda o jogo
Inventário4
  • inventory:readO inventário de um jogador
  • inventory:writeEntregar e remover itemMuda o jogo
  • inventory.use:writeUsar um item do próprio inventárioMuda o jogo
  • inventory.gift:writeDoar um item do inventário de um jogador a outroMuda o jogo
Economia e compras3
  • purchases:readAs compras esperando entrega
  • economy:writeDar e tirar moedasMuda o jogo
  • purchases:writeConcluir e estornar compraMuda o jogo
Kick2
  • kick:readOs canais da Kick, quem está ao vivo e quanto se ganha assistindo e conversandoChave pública
  • kick:writeLigar e desligar a Kick do jogador no seu site, para ele ganhar moedas na liveMuda o jogo
Voz de proximidade3
  • voice:readSe a voz está no ar, quantos estão nela e o link para entrarChave pública
  • voice:writeTirar e devolver um jogador da vozMuda o jogo
  • voice:embedAbrir a voz dentro do seu site, num iframe (o jogador entra com a conta dele)
Mensagens1
  • messages:writeMensagem, anúncio e conviteMuda o jogo
Dino do jogador4
  • dino:readO dino, as mutações e as cores
  • dino:writeCurar, prime, crescer, espécie, cores e mutaçõesMuda o jogo
  • teleport:writeTeleportar e levar até outro jogadorMuda o jogo
  • teleport.player:writeO jogador se teleportar, fora de combateMuda o jogo
Moderação, whitelist e mundo5
  • bans:readQuem está banido
  • moderation:writeExpulsar, banir e desbanirMuda o jogo
  • whitelist:writePôr e tirar da whitelistMuda o jogo
  • world:writeSalvar o mundo e limpar corposMuda o jogo
  • skins.player:writeO jogador guarda e aplica as próprias pelesMuda o jogo
Configuração, só leitura1
  • config:readCassino, recompensas, renda e comandos

Referência

Todos os endpoints da v1, pelo assunto. Os que leem campos do pedido trazem a lista e um exemplo pronto para colar.

Chave e ações2 de 2 no ar
  • GET/v1/me

    Quem sou euValida a chave: nome, permissões, o servidor e se a API está aberta para ele.

    qualquer chave

  • GET/v1/orders/{id}

    Uma ação desta chaveO desfecho de uma ação que passou de 8 s. Só enxerga as ações da própria chave.

    qualquer chave

Servidor, mortes ao vivo e placar4 de 4 no ar
  • GET/v1/server

    Estado do servidorNome, se está ligado, se o RCON responde e quantos jogadores estão dentro.

    server:read

  • GET/v1/players/online

    Quem está onlineA lista de quem está jogando agora. Chave pública recebe só o nome.

    players.online:read

  • GET/v1/killfeed

    Mortes ao vivo (kill feed)As últimas 100 mortes, com quem matou, quem morreu e a espécie, e cursor com ?after=.

    killfeed:read

    Pedido e exemplo
    ?limit
    Quantos itens na resposta. No kill feed, de 1 a 100 (50 se faltar); nos minutos, de 1 a 300 (100 se faltar); na lista de jogadores, de 1 a 1000 (100 se faltar).
    ?after
    O cursor da última resposta: só vem o que chegou depois dele. Sem ele, o kill feed traz as mortes mais novas e os minutos trazem os mais antigos que o painel ainda guarda.
    GET /v1/killfeed?limit=50&after=1727280000000
  • GET/v1/leaderboard

    PlacarOs 20 maiores abatedores do período, com abates, mortes e K/D.

    leaderboard:read

Jogadores13 de 13 no ar
  • GET/v1/players

    Jogadores com contaQuem tem conta no servidor: primeiro quem vinculou o Discord, depois quem tem conta sem Discord. Com saldo e se está online.

    players:read

    Pedido e exemplo
    ?limit
    Quantos itens na resposta. No kill feed, de 1 a 100 (50 se faltar); nos minutos, de 1 a 300 (100 se faltar); na lista de jogadores, de 1 a 1000 (100 se faltar).
    ?offset
    Onde a página começa. Cada resposta traz next, o offset da página seguinte, ou null na última. Sem ele, vem a primeira.
    GET /v1/players?limit=100&offset=0
  • GET/v1/players/{steamId}

    Um jogadorUm jogador pelo SteamID: vínculo, Discord, saldo e se está online.

    players:read

  • GET/v1/leaderboard/{species}

    Placar de uma espécieO placar de 30 dias contando só os abates feitos jogando daquela espécie.

    players:read

    Pede chave secreta: esta leitura vai até o painel, e a chave pública nunca chega ao painel.

  • POST/v1/verifications

    Abrir uma verificaçãoComeça a verificação de um jogador pela Steam e devolve o endereço para onde o seu site manda o navegador dele. O jogador não entra no spiro.land.

    players:verify

    Pedido e exemplo
    redirectUri
    Para onde a central devolve o jogador depois da Steam ou da Kick. Tem de ser, por inteiro, um dos endereços de volta cadastrados na chave.
    state
    Um valor aleatório que o seu site guarda na sessão do jogador: de 8 a 128 letras, números, ponto, til, _ ou -. Ele volta igual na URL, e o seu site compara antes de trocar.
    POST /v1/verifications
    
    {
      "redirectUri": "https://yoursite.com/verify/return",
      "state": "f3Kq9xLm2PwZ7tRb"
    }
  • POST/v1/verifications/{verificationId}/exchange

    Trocar o código pelo SteamIDO código que voltou na URL do seu site vira o SteamID confirmado pela Steam, uma vez só. Chame do servidor do seu site, assim que o jogador voltar.

    players:verify

    Pedido e exemplo
    code
    O código que chegou na URL de volta, com 43 caracteres. Vale uma troca, por cinco minutos.
    POST /v1/verifications/{verificationId}/exchange
    
    {
      "code": "Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY"
    }
  • POST/v1/players/{steamId}/account

    Abrir conta sem DiscordAbre a carteira de um jogador que não vinculou o Discord, com o SteamID que o login Steam do seu site provou. O seu site dá e tira moedas, e a renda por tempo paga, sem o jogador passar pelo Discord nem pelo spiro.land. Se ele vincular o Discord depois, a mesma carteira continua, com o saldo. Abrir de novo não muda nada.

    link:write

  • GET/v1/players/{steamId}/wallet/transactions

    Extrato da carteiraOs movimentos da carteira, do mais novo para o mais antigo: data (at), quantia com sinal (amount), motivo curto (reason) e o tipo (kind: credit, debit, refund, transfer ou purchase), com o saldo de agora (balance). Peça a primeira página sem nada e siga o nextBefore com ?before=: movimento novo entre duas páginas não desloca a lista (painel 0.1.87). O offset de antes continua valendo, com next.

    players:read

    Pedido e exemplo
    ?offset
    Onde a página começa. Cada resposta traz next, o offset da página seguinte, ou null na última. Sem ele, vem a primeira.
    ?before
    O cursor do extrato: o id do movimento mais antigo que você já tem. A página traz os movimentos de antes dele, e movimento novo não desloca a lista. Cada resposta traz nextBefore, o before da página seguinte, ou null na última. Com before, o offset não vale.
    GET /v1/players/{steamId}/wallet/transactions?offset=0&before=812
  • GET/v1/activity

    Minutos em call e jogandoOs minutos que o Spiro Land contou em call no Discord e jogando, em eventos com cursor (?after=), dos mais antigos para os mais novos. O seu site soma para o passe, os clãs ou o que quiser. Com o painel 0.1.87, o site que ficou parado lê o que perdeu dos últimos 7 dias, página a página, sem gap.

    activity:read

    Pedido e exemplo
    ?limit
    Quantos itens na resposta. No kill feed, de 1 a 100 (50 se faltar); nos minutos, de 1 a 300 (100 se faltar); na lista de jogadores, de 1 a 1000 (100 se faltar).
    ?after
    O cursor da última resposta: só vem o que chegou depois dele. Sem ele, o kill feed traz as mortes mais novas e os minutos trazem os mais antigos que o painel ainda guarda.
    GET /v1/activity?limit=100&after=4182
  • GET/v1/players/{steamId}/voice

    Ver a voz de um jogadorSe aquele jogador pode entrar na voz (ou por que não) e se está fora dela. Nunca diz se ele está na voz agora.

    players:read

  • GET/v1/players/{steamId}/skins

    Ver as peles de um jogadorA regra do dono (maxPeles guardadas, applications a cada windowMinutes, glow e price em moedas), o uso do jogador agora (used, allowed e nextInSeconds até a próxima liberar, null quando há uma livre) e as peles guardadas. O limite é o mesmo da página /players: aplicar em um lugar conta nos dois. canApply diz se o painel pinta agora (painel do jogador ligado, e o painel 0.2.10 ou mais novo quando há custo); patternSupported, se pattern funciona. Com as peles dos jogadores desligadas, 403 skinsOff.

    players:read

Mapa7 de 7 no ar
  • GET/v1/players/{steamId}/map

    Ver onde o jogador estáA posição do jogador e de quem está no grupo dele agora, com nome, espécie e há quanto tempo foi lida, em centímetros do jogo (as coordenadas do teleporte). Ninguém de fora do grupo aparece. A primeira leitura acorda o painel do servidor e volta 409 dataNotReady: leia de novo a cada 5 s.

    map:read

  • POST/v1/players/{steamId}/map/embed

    Embutir o mapa no seu siteDevolve a url do mapa ao vivo para um iframe: o mesmo mapa da aba Mapa do painel do jogador, com camadas, grupo e zoom, servido pelo spiro.land. Só abre nos sites listados na chave. Abra um por carga da página, para o jogador que entrou no seu site.

    map:read

  • GET/v1/map/heat

    Ver o mapa de calorOnde a população do servidor anda: uma grade de 48 por 48 com níveis de 0 a 8 e a faixa do mapa que ela cobre. Só contagem por célula, sem nome nem ponto. Volta cold sem medição na última hora.

    map:read

  • GET/v1/map

    Ler o mapa para desenharOs limites do Gateway em centímetros, as camadas da imagem com a versão de cada uma, o viewBox do SVG, os rótulos e as zonas (santuários, migração, patrulha e cavernas). Não depende do mapa ao vivo.

    map:read

  • GET/v1/map/image

    Baixar a imagem do mapaO WebP de 975 por 977 de uma camada (color, gray ou water), com spiro.land num canto. A versão vai no ETag: baixe de novo só quando ela mudar, e sirva do seu servidor.

    map:read

    Pedido e exemplo
    ?layer
    A camada da imagem: color (o padrão), gray ou water, a água doce transparente para pôr por cima da cor.
    GET /v1/map/image?layer=color
  • GET/v1/map/zones

    Zonas ativas do servidorOs santuários, as migrações e as patrulhas que o jogo tem ativos agora, com o centro e a forma em centímetros e o path do SVG de GET /v1/map. A migração diz as espécies; a patrulha diz a quantos dinos foi dada, sem dizer a quais.

    map:read

    O painel lê as zonas do jogo a cada 4 minutos e manda só quando uma acende, apaga ou é dada a alguém: asOf é a última mudança e pode ter horas, e quem diz se ainda vale é stale. zonesNotRead quer dizer que o servidor ainda não leu as zonas (o leitor é novo e vem ligado só no servidor de teste por enquanto). Juvenil é crescimento abaixo de 0,675. Uma migração com species null é uma cuja lista de espécies ainda não temos traduzida: ela vale para todos até lá. patrolsKnown false quer dizer que o painel não manda o número do dino, e a patrulha dele não tem como aparecer. O painel anterior a 0.2.2 responde 409 panelNotReady.

  • GET/v1/players/{steamId}/zones

    Zonas que valem para o jogadorAs zonas ativas que contam para as missões do prime do dino dele agora: o santuário só enquanto juvenil, a migração da espécie dele e a patrulha que o jogo deu a ele (assignedToPlayer). Sem dino em campo, player é null.

    map:read

    O painel lê as zonas do jogo a cada 4 minutos e manda só quando uma acende, apaga ou é dada a alguém: asOf é a última mudança e pode ter horas, e quem diz se ainda vale é stale. zonesNotRead quer dizer que o servidor ainda não leu as zonas (o leitor é novo e vem ligado só no servidor de teste por enquanto). Juvenil é crescimento abaixo de 0,675. Uma migração com species null é uma cuja lista de espécies ainda não temos traduzida: ela vale para todos até lá. patrolsKnown false quer dizer que o painel não manda o número do dino, e a patrulha dele não tem como aparecer. O painel anterior a 0.2.2 responde 409 panelNotReady.

Loja e vantagens3 de 3 no ar
  • GET/v1/shop/products

    Produtos da lojaO catálogo da sua loja, para o seu site casar o que vende com o que o jogo entrega. Traz o preço em dinheiro e, com include=all, também o desligado e o vendido só em dinheiro. No grow, growth é o tamanho que ele dá (0 a 1) e tierNames, o nome de cada tier de tiers, na mesma ordem. Em cada item, uses diz quantos usos o item comprado terá (null é ilimitado, a skin permanente) e validityDays, em quantos dias ele vence (null é nunca).

    shop:read

    Pedido e exemplo
    ?include
    all traz também os produtos desligados, esgotados e vendidos só em dinheiro (com price null), para o seu site vender com o gateway dele e entregar pela API. Só com chave secreta. Sem ele, vem só o que está à venda na loja do servidor.
    GET /v1/shop/products?include=all
  • GET/v1/shop/skins

    Peles da lojaAs peles que a sua loja vende, com as cores de cada uma: só as que um produto à venda entrega. Com include=all, a biblioteca inteira, e onSale diz quais estão à venda.

    skins:read

    Pedido e exemplo
    ?include
    all traz também os produtos desligados, esgotados e vendidos só em dinheiro (com price null), para o seu site vender com o gateway dele e entregar pela API. Só com chave secreta. Sem ele, vem só o que está à venda na loja do servidor.
    GET /v1/shop/skins?include=all
  • GET/v1/perks

    Vantagens por cargoO que cada cargo do Discord dá no jogo: moedas por hora, desconto e efeito ao entrar.

    perks:read

Garagem7 de 7 no ar
  • GET/v1/players/{steamId}/garage

    Garagem do jogadorAs vagas ocupadas (espécie, crescimento, sexo, prime, elder e mutações), quantas vagas o jogador tem, quais um cargo libera, e a regra da garagem para ele.

    garage:read

  • POST/v1/players/{steamId}/garage/park

    Guardar dinoTira o dino vivo do jogo e o salva numa vaga, com as regras da garagem: espera, preço e vagas por cargo.

    garage:write

    Pedido e exemplo
    slot
    A vaga da garagem: padrao, ou de a até i. Ao guardar, sem ela o painel escolhe a vaga.
    POST /v1/players/{steamId}/garage/park
    
    {
      "slot": "a"
    }
  • POST/v1/players/{steamId}/garage/{slot}/restore

    Resgatar dinoTraz o dino da vaga de volta ao jogo. Com swap: true, troca de espécie, e o dino atual some.

    garage:write

    Pedido e exemplo
    swap
    true resgata trocando: o dino que o jogador está usando dá lugar ao da vaga. Sem ele, vale false.
    POST /v1/players/{steamId}/garage/{slot}/restore
    
    {
      "swap": false
    }
  • DELETE/v1/players/{steamId}/garage/{slot}

    Excluir dino guardadoApaga a vaga para sempre. Pede o version lido na garagem, para não apagar um dino que mudou no meio.

    garage:write

    Pedido e exemplo
    ?version
    O version da vaga, lido na leitura da garagem. Se a vaga mudou depois da leitura, apagar é recusado, e nada some por engano.
    DELETE /v1/players/{steamId}/garage/{slot}?version=1727280000
  • GET/v1/players/{steamId}/garage/{slot}

    Ler vaga, como a equipeA vaga inteira, como a equipe vê no editor de dino, mesmo com o jogador offline.

    garage.admin:read

  • POST/v1/players/{steamId}/garage/{slot}

    Criar dino na garagemEscreve um dino numa vaga vazia. Nunca grava por cima.

    garage.admin:write

    Pedido e exemplo
    speciesClass
    A classe da espécie no jogo, como BP_Tyrannosaurus_C.
    growth
    O crescimento, de 0.01 a 1, em que 1 é adulto.
    male
    true para macho, false para fêmea.
    prime
    true entrega a versão com prime do produto, quando ele tem uma. Na garagem da equipe, o dino da vaga já sai com prime.
    elder
    O nível de elder do dino, de 0 a 10.
    mutations
    As mutações por vaga, de Slot1 a Slot4, com o nome que o jogo usa, como Wader.
    quest
    As mutações de missão, pelo nome, numa lista.
    vitals
    Os vitais, cada um de 0 a 1: Health, Stamina, Hunger, Thirst e FoodValue. Mande só os que quer mudar.
    nutrients
    Os nutrientes, cada um de 0 a 100, pelo nome que o jogo usa, como ProteinValue e LipidValue. Mande só os que quer mudar.
    POST /v1/players/{steamId}/garage/{slot}
    
    {
      "speciesClass": "BP_Tyrannosaurus_C",
      "growth": 1,
      "male": true,
      "prime": true,
      "elder": 1,
      "mutations": {
        "Slot1": "Wader"
      },
      "quest": [],
      "vitals": {
        "Health": 1
      },
      "nutrients": {
        "ProteinValue": 100
      }
    }
  • PATCH/v1/players/{steamId}/garage/{slot}

    Editar dino guardadoMuda um dino guardado: crescimento, mutações, cores.

    garage.admin:write

    Pedido e exemplo
    speciesClass
    A classe da espécie no jogo, como BP_Tyrannosaurus_C.
    growth
    O crescimento, de 0.01 a 1, em que 1 é adulto.
    male
    true para macho, false para fêmea.
    prime
    true entrega a versão com prime do produto, quando ele tem uma. Na garagem da equipe, o dino da vaga já sai com prime.
    elder
    O nível de elder do dino, de 0 a 10.
    mutations
    As mutações por vaga, de Slot1 a Slot4, com o nome que o jogo usa, como Wader.
    quest
    As mutações de missão, pelo nome, numa lista.
    vitals
    Os vitais, cada um de 0 a 1: Health, Stamina, Hunger, Thirst e FoodValue. Mande só os que quer mudar.
    nutrients
    Os nutrientes, cada um de 0 a 100, pelo nome que o jogo usa, como ProteinValue e LipidValue. Mande só os que quer mudar.
    PATCH /v1/players/{steamId}/garage/{slot}
    
    {
      "speciesClass": "BP_Tyrannosaurus_C",
      "growth": 0.8,
      "male": false,
      "prime": false,
      "elder": 0,
      "mutations": {
        "Slot1": "Wader"
      },
      "quest": [],
      "vitals": {
        "Health": 1
      },
      "nutrients": {
        "ProteinValue": 100
      }
    }
Inventário8 de 8 no ar
  • GET/v1/players/{steamId}/inventory

    Inventário do jogadorOs itens que o jogador tem, com os usos que sobram. type=skins traz só as peles, e type=items, o resto.

    inventory:read

    Pedido e exemplo
    ?offset
    Onde a página começa. Cada resposta traz next, o offset da página seguinte, ou null na última. Sem ele, vem a primeira.
    ?type
    Só um tipo de item: as peles (skins) ou o resto (items). Sem ele, vem tudo.
    ?group
    true junta os itens iguais (mesmo produto, usos e validade) numa linha com count; a linha traz o id de uma unidade, e usar ou doar age sobre ela. A resposta diz grouped e units (quantos itens há). Sem ele, um item por linha.
    GET /v1/players/{steamId}/inventory?offset=0&type=items&group=true
  • POST/v1/players/{steamId}/inventory

    Entregar itemColoca um produto da loja no inventário do jogador e devolve o item criado, com o id e a validade. Com quantity (1 a 50), entrega várias unidades de uma vez, todas ou nenhuma. Pede Idempotency-Key.

    inventory:writepede Idempotency-Key

    Pedido e exemplo
    productId
    O id do produto na loja do servidor, como a leitura da loja o devolve, inclusive o desligado e o vendido só em dinheiro. Pacote de moedas e cargo não entram no inventário e voltam invalidField: moeda vai pelo crédito na carteira.
    prime
    true entrega a versão com prime do produto, quando ele tem uma. Na garagem da equipe, o dino da vaga já sai com prime.
    expiresAt
    Opcional. Quando o item vence, em ISO 8601 com fuso (2035-01-01T00:00:00Z), no futuro e até 10 anos à frente. Vale no lugar da validade do produto; sem ele, o item vence pela validade do produto, ou nunca. Pede o painel 0.1.86 ou mais novo: antes disso volta panelNotReady.
    quantity
    Opcional. Quantas unidades do produto entregar, de 1 a 50 (padrão 1). Entram todas ou nenhuma: a pele para sempre com mais de 1 volta alreadyOwned. Acima de 1 pede o painel 0.2.0 ou mais novo: antes disso volta panelNotReady.
    POST /v1/players/{steamId}/inventory
    
    {
      "productId": "grow-trex",
      "prime": false,
      "expiresAt": "2035-01-01T00:00:00.000Z",
      "quantity": 1
    }
  • DELETE/v1/players/{steamId}/inventory/{itemId}

    Remover itemTira um item do inventário do jogador.

    inventory:write

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    DELETE /v1/players/{steamId}/inventory/{itemId}
    
    {
      "reason": "Refunded on Discord"
    }
  • POST/v1/players/{steamId}/inventory/remove

    Remover vários itensTira de uma vez os itens escolhidos do inventário do jogador, com um motivo só. Pede o painel 0.2.8 ou mais novo.

    inventory:write

    Pedido e exemplo
    itemIds
    Os ids dos itens, sem repetir: os item.id da listagem ou do POST .../inventory. Na retirada em lote, de 1 a 100; no estorno, de 0 a 20. Um item que nunca foi do jogador recusa o pedido inteiro com itemNotFound.
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/inventory/remove
    
    {
      "itemIds": [
        4192,
        4193
      ],
      "reason": "Duplicated by mistake"
    }
  • PATCH/v1/players/{steamId}/inventory/{itemId}

    Ajustar usosMuda quantos usos sobram num item do jogador.

    inventory:write

    Pedido e exemplo
    action
    add soma um uso, take tira um, unlimited deixa o item sem limite de usos.
    PATCH /v1/players/{steamId}/inventory/{itemId}
    
    {
      "action": "add"
    }
  • POST/v1/players/{steamId}/inventory/{itemId}/use

    Usar itemO jogador usa um item do inventário: aplica a pele, o item de uso ou o grow, com as mutações. O vale de pele troca pela pele de skinId. A entrega manual vira pedido para a equipe (ou aplica sozinha, se o produto ganhou entrega automática) e o dino do kit vai para a garagem. A resposta diz qual em outcome: applied, staff ou garage.

    inventory.use:writepede Idempotency-Key

    Pedido e exemplo
    mutations
    As mutações por vaga, de Slot1 a Slot4, com o nome que o jogo usa, como Wader.
    acceptPrimeClose
    true depois que o jogador confirma usar o grow que fecha o prime do dino (closesPrime nas opções do grow). Opcional.
    POST /v1/players/{steamId}/inventory/{itemId}/use
    
    {
      "mutations": {
        "Slot1": "Wader",
        "Slot2": "Nocturnal"
      },
      "acceptPrimeClose": false
    }
  • GET/v1/players/{steamId}/inventory/{itemId}/grow-options

    Mutações do growOs slots que o grow libera e as mutações que cabem em cada um, para o dino em campo. Monte a escolha com isto e use o item com mutations. closesPrime true diz que usar fecha o prime desse dino: avise o jogador e, se ele confirmar, use com acceptPrimeClose true.

    inventory:read

  • POST/v1/players/{steamId}/inventory/{itemId}/gift

    Doar itemO jogador doa um item do próprio inventário a outro jogador do servidor (toSteamId). O item vai inteiro, com os usos e a validade. Só com a doação ligada em Painel do jogador › Recursos; o kit inicial e o grow grátis não se doam, e o limite por dia vale.

    inventory.gift:writepede Idempotency-Key

    A resposta traz o item (id, usesLeft, expiresAt), quem recebeu (to) e quantas doações restam nas últimas 24 horas (giftsLeftToday). A listagem do inventário diz quais itens se doam (giftable), de quem cada um veio (receivedFrom) e a regra do servidor (gifts: enabled, perDay, leftToday). Recusas: giftsDisabled, notGiftable, itemNotFound, limitReached (com perDay), notLinked (com target: true quando é quem recebe), alreadyOwned (pele para sempre que quem recebe já tem) e invalidChoice. O estorno segue o item: o DELETE com a SteamID de quem comprou tira o item de quem recebeu. O painel anterior a este endpoint responde 409 panelNotReady.

    Pedido e exemplo
    toSteamId
    O jogador que recebe. Não pode ser o próprio jogador da rota.
    POST /v1/players/{steamId}/inventory/{itemId}/gift
    
    {
      "toSteamId": "76561198000000002"
    }
Economia e compras10 de 10 no ar
  • POST/v1/players/{steamId}/wallet/credit

    Dar moedasCredita moedas na carteira do jogador, com motivo no histórico. Pede Idempotency-Key.

    economy:writepede Idempotency-Key

    Pedido e exemplo
    amount
    A quantia, um número inteiro de 1 a 1.000.000. Na compra com entrega, pode ser 0: o carrinho de graça entrega sem debitar. No estorno de uma venda, de 0 a 1.000.000: 0 só retira os itens.
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/wallet/credit
    
    {
      "amount": 500,
      "reason": "Saturday event"
    }
  • POST/v1/players/{steamId}/wallet/debit

    Tirar moedasDebita moedas da carteira do jogador. Sem saldo, recusa. Com refund: true é um estorno, que pode deixar o saldo negativo. Pede Idempotency-Key.

    economy:writepede Idempotency-Key

    Pedido e exemplo
    amount
    A quantia, um número inteiro de 1 a 1.000.000. Na compra com entrega, pode ser 0: o carrinho de graça entrega sem debitar. No estorno de uma venda, de 0 a 1.000.000: 0 só retira os itens.
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    refund
    true marca o débito como estorno: é o único que pode deixar o saldo negativo, e com saldo negativo o jogador não gasta até voltar a zero. Sem ele, vale false, e o débito sem saldo é recusado.
    POST /v1/players/{steamId}/wallet/debit
    
    {
      "amount": 200,
      "reason": "Paid for a nest on the site",
      "refund": false
    }
  • GET/v1/shop/purchases

    Compras pendentesAs compras da loja esperando entrega manual.

    purchases:read

  • POST/v1/shop/purchases/{id}/complete

    Concluir compraMarca a compra como entregue e libera o limite do jogador.

    purchases:write

  • POST/v1/shop/purchases/{id}/refund

    Estornar compraDevolve as moedas da compra ao jogador. Pede Idempotency-Key.

    purchases:writepede Idempotency-Key

  • POST/v1/wallet/credits

    Dar moedas em loteCredita de 1 a 50 jogadores numa ação só, cada crédito com a sua key: o mesmo crédito reenviado, em qualquer lote, não paga duas vezes. Quem não tem conta falha sozinho (notLinked), ou ganha a conta com createAccount. Serve a recompensa por tempo, o sorteio, a temporada e a migração de saldos. A resposta traz applied e, em failed, a posição (i) e o motivo de cada crédito que não entrou. Pede Idempotency-Key.

    economy:writepede Idempotency-Key

    Pedido e exemplo
    credits
    A lista de créditos, de 1 a 50. Cada um com steamId, amount (de 1 a 1.000.000), reason (até 120 caracteres) e key (letras, números, _ ou -, até 64 caracteres), que identifica aquele crédito para aquele jogador: reenviar a mesma key não paga de novo.
    createAccount
    true abre a conta de quem ainda não tem, antes de creditar (a mesma conta sem Discord do POST .../account). Sem ele, quem não tem conta falha com notLinked.
    POST /v1/wallet/credits
    
    {
      "credits": [
        {
          "steamId": "76561198000000001",
          "amount": 500,
          "reason": "Season 3 reward",
          "key": "season3-player1"
        },
        {
          "steamId": "76561198000000002",
          "amount": 250,
          "reason": "Season 3 reward",
          "key": "season3-player2"
        }
      ],
      "createAccount": false
    }
  • POST/v1/players/{steamId}/wallet/transfer

    Transferir moedasTira do jogador da rota e dá a toSteamId na mesma transação: ou os dois lados acontecem, ou nenhum. Sem saldo, ou com saldo negativo, recusa (insufficientFunds). Quem recebe sem conta volta notLinked com target: true. A resposta traz os dois saldos, balance e toBalance. Pede Idempotency-Key.

    economy:writepede Idempotency-Key

    Pedido e exemplo
    toSteamId
    O jogador que recebe. Não pode ser o próprio jogador da rota.
    amount
    A quantia, um número inteiro de 1 a 1.000.000. Na compra com entrega, pode ser 0: o carrinho de graça entrega sem debitar. No estorno de uma venda, de 0 a 1.000.000: 0 só retira os itens.
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/wallet/transfer
    
    {
      "toSteamId": "76561198000000002",
      "amount": 100,
      "reason": "Paid a friend on the site"
    }
  • POST/v1/players/{steamId}/purchases

    Comprar com entregaDebita amount (o preço que o seu site calculou, com cupom e tudo) e entrega cada unidade no inventário do jogador, numa ação só: qualquer falha desfaz tudo, e nada é cobrado. Até 10 produtos e 20 unidades. Com saldo negativo, não compra nem de graça. A resposta traz o saldo e os ids dos itens criados (items), para o seu site poder retirar no estorno. A mesma Idempotency-Key devolve a mesma compra, sem cobrar de novo.

    economy:writepede Idempotency-Key

    Pedido e exemplo
    amount
    A quantia, um número inteiro de 1 a 1.000.000. Na compra com entrega, pode ser 0: o carrinho de graça entrega sem debitar. No estorno de uma venda, de 0 a 1.000.000: 0 só retira os itens.
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    items
    Os produtos do carrinho, de 1 a 10, cada um com productId, quantity (de 1 a 20) e prime (opcional). No máximo 20 unidades no total. Pacote de moedas e cargo não entram no inventário e voltam invalidField.
    POST /v1/players/{steamId}/purchases
    
    {
      "amount": 1200,
      "reason": "Site order 4821",
      "items": [
        {
          "productId": "grow-trex",
          "prime": true,
          "quantity": 1
        },
        {
          "productId": "heal",
          "quantity": 2
        }
      ]
    }
  • POST/v1/players/{steamId}/purchases/{purchaseId}/refund

    Estornar a compra com entregaDesfaz numa ação só uma compra feita por POST .../purchases: devolve as moedas que ela debitou e retira os itens dela que ainda não foram usados. O purchaseId é o order.id da resposta da compra. A resposta traz refunded, balance, removed (os itens retirados) e used (os que já tinham sido usados, vencidos ou removidos, e ficam onde estão). Estornar de novo, mesmo com outra Idempotency-Key, não devolve duas vezes: volta alreadyRefunded: true com os mesmos números.

    economy:writepede Idempotency-Key

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/purchases/{purchaseId}/refund
    
    {
      "reason": "Order 4821 cancelled"
    }
  • POST/v1/players/{steamId}/sales/{saleId}/refund

    Estornar uma venda do seu siteDesfaz numa ação só uma venda em dinheiro que o seu site fez pelo gateway dele: retira os itens que você entregou com POST .../inventory (itemIds, os ids que a entrega devolveu) e tira as moedas que você creditou (amount) como estorno, que pode deixar o saldo negativo. O saleId é o id da venda no seu site: a mesma venda nunca tira moedas duas vezes, mesmo com outra Idempotency-Key. A resposta traz debited, balance, removed e used (os itens já usados, que ficam onde estão).

    economy:writepede Idempotency-Key

    Pedido e exemplo
    itemIds
    Os ids dos itens, sem repetir: os item.id da listagem ou do POST .../inventory. Na retirada em lote, de 1 a 100; no estorno, de 0 a 20. Um item que nunca foi do jogador recusa o pedido inteiro com itemNotFound.
    amount
    A quantia, um número inteiro de 1 a 1.000.000. Na compra com entrega, pode ser 0: o carrinho de graça entrega sem debitar. No estorno de uma venda, de 0 a 1.000.000: 0 só retira os itens.
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/sales/{saleId}/refund
    
    {
      "itemIds": [
        812,
        813
      ],
      "amount": 500,
      "reason": "Chargeback on order 4821"
    }
Kick4 de 4 no ar
  • GET/v1/kick

    Ver os canais e a regra da KickOs canais conectados, quem está ao vivo e o link de cada um, a regra (moedas por tempo assistido e por mensagem) e os pacotes de pontos do canal. A chave pública também lê.

    kick:read

Voz de proximidade4 de 4 no ar
  • GET/v1/voice

    Ver o estado da vozSe a voz está ligada e no ar, o estado da porta, quantos estão na voz agora, o alcance e o link para entrar. A chave pública também lê.

    voice:read

  • POST/v1/players/{steamId}/voice-ban

    Tirar um jogador da vozCom motivo e prazo (ou sem prazo). Quem está falando sai na hora, e a resposta diz se havia o que derrubar.

    voice:write

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    durationMinutes
    Quanto tempo o jogador fica fora da voz, em minutos, de 1 a 525600. null deixa sem prazo.
    POST /v1/players/{steamId}/voice-ban
    
    {
      "reason": "Insults in voice chat",
      "durationMinutes": 1440
    }
  • DELETE/v1/players/{steamId}/voice-ban

    Devolver a voz a um jogadorTira o voice-ban. A resposta diz se havia um.

    voice:write

  • POST/v1/voice/embed

    Abrir a voz embutidaDevolve o endereço de um iframe da voz para o seu site, sem jogador nenhum: quem entra prova a conta numa janela do Spiro Land.

    voice:embed

Mensagens3 de 3 no ar
  • POST/v1/players/{steamId}/message

    Mensagem para um jogadorManda texto para um jogador dentro do jogo, com a voz da equipe.

    messages:write

    Pedido e exemplo
    text
    O texto. Recado a um jogador: até 300 caracteres. Aviso no jogo para todos: até 240.
    POST /v1/players/{steamId}/message
    
    {
      "text": "Your order arrived. Check the inventory."
    }
  • POST/v1/announce

    AnúncioManda um anúncio para todo mundo que está no servidor.

    messages:write

    Pedido e exemplo
    text
    O texto. Recado a um jogador: até 300 caracteres. Aviso no jogo para todos: até 240.
    POST /v1/announce
    
    {
      "text": "Server restart in 10 minutes."
    }
  • POST/v1/invite/send

    Mandar o convite agoraManda o convite de vínculo agora para quem está dentro sem vínculo.

    messages:write

Dino do jogador24 de 24 no ar
  • GET/v1/players/{steamId}/dino

    Dino do jogadorO dino de agora: espécie, crescimento (0 a 1), morto, sexo (male), prime, posição, giro, vitais com percent (0 a 100), cores e nutrientes, com nutrientsPercent sobre a fome máxima, que é o cheio da dieta no HUD. prime é a flag da própria engine, a mesma do eligible de /dino/prime. O que o jogo não respondeu sai null, e nunca false: male ou prime null quer dizer não sei (prime também com o dino morto ou no painel anterior ao 0.1.91). colorsFrom diz quem pintou as cores que o mod reafirma: shop (loja ou inventário), player (a skin do jogador) ou staff; null sem cor do mod ou no painel anterior ao 0.2.10. Sem leitura, 409 refused com noDinosaur, playerOffline ou gameNotResponding.

    dino:read

  • GET/v1/players/{steamId}/dino/mutations

    Mutações do dinoOs slots de mutação, as elder stacks, a lista de quest e o padrão do dino do jogador. Sem leitura, 409 refused com noDinosaur, playerOffline ou gameNotResponding.

    dino:read

  • GET/v1/players/{steamId}/dino/loadouts

    Loadouts do jogadorOs loadouts de mutação que o jogador salvou, o ativo primeiro. Vêm do painel, e não do jogo: respondem mesmo com o jogador fora dele.

    dino:read

  • GET/v1/players/{steamId}/dino/colors

    Cores do dinoAs cores aplicadas no dino do jogador, uma por região, null na que o jogo não respondeu. Sem leitura, 409 refused com noDinosaur, playerOffline ou gameNotResponding.

    dino:read

  • POST/v1/players/{steamId}/dino/heal

    CurarEnche a vida do dino do jogador.

    dino:write

  • POST/v1/players/{steamId}/dino/prime

    PrimeDá prime ao dino do jogador.

    dino:write

  • POST/v1/players/{steamId}/dino/growth

    CrescerMuda o crescimento do dino do jogador.

    dino:write

    Pedido e exemplo
    growth
    O crescimento, de 0.01 a 1, em que 1 é adulto.
    POST /v1/players/{steamId}/dino/growth
    
    {
      "growth": 0.75
    }
  • POST/v1/players/{steamId}/dino/vitals

    VitaisAjusta fome, sede e estamina do dino do jogador.

    dino:write

    Pedido e exemplo
    vitals
    Os vitais, cada um de 0 a 1: Health, Stamina, Hunger, Thirst e FoodValue. Mande só os que quer mudar.
    POST /v1/players/{steamId}/dino/vitals
    
    {
      "vitals": {
        "Health": 1,
        "Stamina": 1
      }
    }
  • POST/v1/players/{steamId}/dino/diet

    DietaAjusta a dieta do dino do jogador.

    dino:write

    Pedido e exemplo
    nutrients
    Os nutrientes, cada um de 0 a 100, pelo nome que o jogo usa, como ProteinValue e LipidValue. Mande só os que quer mudar.
    POST /v1/players/{steamId}/dino/diet
    
    {
      "nutrients": {
        "ProteinValue": 80,
        "LipidValue": 60
      }
    }
  • POST/v1/players/{steamId}/dino/species

    Trocar de espécieTroca a espécie do dino vivo, sem morrer.

    dino:write

    Pedido e exemplo
    speciesClass
    A classe da espécie no jogo, como BP_Tyrannosaurus_C.
    growth
    O crescimento, de 0.01 a 1, em que 1 é adulto.
    POST /v1/players/{steamId}/dino/species
    
    {
      "speciesClass": "BP_Tyrannosaurus_C",
      "growth": 0.5
    }
  • POST/v1/players/{steamId}/dino/colors

    PintarAplica cores no dino do jogador.

    dino:write

    Pedido e exemplo
    colors
    As cores, por região do dino (como corpo), cada uma com r, g e b.
    glow
    O brilho por região, de 1 a 10. Só a pintura da equipe (dino-paint) aceita os valores negativos, os níveis de glitch do jogo. Opcional.
    POST /v1/players/{steamId}/dino/colors
    
    {
      "colors": {
        "corpo": {
          "r": 0.8,
          "g": 0.25,
          "b": 0.1
        }
      },
      "glow": {
        "corpo": 5
      }
    }
  • DELETE/v1/players/{steamId}/dino/colors

    Limpar coresTira as cores próprias do dino do jogador.

    dino:write

  • POST/v1/players/{steamId}/dino/pattern

    Padrão da peleEscolhe o padrão da pele do dino do jogador.

    dino:write

    Pedido e exemplo
    variation
    O número da variação. Mande ao menos um entre variation, pattern e theme.
    pattern
    O número do padrão.
    theme
    O número do tema.
    POST /v1/players/{steamId}/dino/pattern
    
    {
      "variation": 1,
      "pattern": 2,
      "theme": 0
    }
  • POST/v1/players/{steamId}/dino/mutations

    Escrever mutaçõesEscreve os slots de mutação do dino do jogador.

    dino:write

    Pedido e exemplo
    mutations
    As mutações por vaga, de Slot1 a Slot4, com o nome que o jogo usa, como Wader.
    quest
    As mutações de missão, pelo nome, numa lista.
    elder
    O nível de elder do dino, de 0 a 10.
    POST /v1/players/{steamId}/dino/mutations
    
    {
      "mutations": {
        "Slot1": "Wader",
        "Slot2": "Nocturnal"
      },
      "quest": [],
      "elder": 2
    }
  • POST/v1/players/{steamId}/dino/carcass

    Soltar carcaçaSolta uma carcaça da espécie e do peso escolhidos perto do jogador.

    dino:write

    Pedido e exemplo
    speciesClass
    A classe da espécie no jogo, como BP_Tyrannosaurus_C.
    growth
    O crescimento, de 0.01 a 1, em que 1 é adulto.
    POST /v1/players/{steamId}/dino/carcass
    
    {
      "speciesClass": "BP_Stegosaurus_C",
      "growth": 1
    }
  • POST/v1/players/{steamId}/dino/teleport

    TeleportarLeva o dino do jogador para um ponto do mapa.

    teleport:write

    Pedido e exemplo
    x
    A posição no mapa. x, y e z vão juntos. No teleporte da equipe, sem os três o painel só empurra o dino para cima.
    y
    A posição no mapa, junto com x e z.
    z
    A altura, junto com x e y.
    rotation
    Para onde o dino fica virado, em graus, de -360 a 360. Opcional.
    POST /v1/players/{steamId}/dino/teleport
    
    {
      "x": 12000,
      "y": -48500,
      "z": 3100,
      "rotation": 90
    }
  • POST/v1/players/{steamId}/dino/bring-to

    Levar até outro jogadorLeva o dino do jogador para perto de outro jogador.

    teleport:write

    Pedido e exemplo
    targetSteamId
    O jogador de destino: o dino da rota vai até ele. Não pode ser o próprio jogador da rota.
    rotation
    Para onde o dino fica virado, em graus, de -360 a 360. Opcional.
    POST /v1/players/{steamId}/dino/bring-to
    
    {
      "targetSteamId": "76561198000000002",
      "rotation": 0
    }
  • GET/v1/players/{steamId}/dino/prime

    Ler o primeAs dez condições de prime do dino de agora, quais contam e quantas faltam, e eligible, a flag da engine (a mesma do prime de /dino). Sem leitura, 409 refused com noDinosaur (também com o dino morto), playerOffline ou gameNotResponding. Com o painel 0.2.2 ou mais novo vem também dinoId, o número do dino no jogo: as zonas das missões guardam a visita por ele, e 0 é o dino cujas visitas não contam (null com painel antigo). No modo servidor de teste vem também migrations, com visited, quantas migrações diferentes o dino já visitou, e required, quantas a condição visited2MigrationZones pede (null fora do modo de teste e com painel antigo).

    dino:read

  • POST/v1/players/{steamId}/teleport

    Teleportar o jogadorLeva o dino do jogador a um ponto do mapa. Recusa em combate.

    teleport.player:write

    Pedido e exemplo
    x
    A posição no mapa. x, y e z vão juntos. No teleporte da equipe, sem os três o painel só empurra o dino para cima.
    y
    A posição no mapa, junto com x e z.
    z
    A altura, junto com x e y.
    rotation
    Para onde o dino fica virado, em graus, de -360 a 360. Opcional.
    POST /v1/players/{steamId}/teleport
    
    {
      "x": 12000,
      "y": -48500,
      "z": 3100,
      "rotation": 90
    }
  • POST/v1/players/{steamId}/teleport/to-player

    Ir até outro jogadorLeva o dino do jogador para onde o outro está ou acabou de pisar. Recusa se um dos dois estiver em combate, ou com notApplied e target quando não há lugar livre ali. Com o limite de crescimento ligado (GET /v1/config/teleport), recusa com growthLimit quem está no limite ou acima, e o amigo nessa faixa com target; sem ler o dino de um dos dois, recusa pelo motivo da leitura.

    teleport.player:write

    Pedido e exemplo
    targetSteamId
    O jogador de destino: o dino da rota vai até ele. Não pode ser o próprio jogador da rota.
    rotation
    Para onde o dino fica virado, em graus, de -360 a 360. Opcional.
    POST /v1/players/{steamId}/teleport/to-player
    
    {
      "targetSteamId": "76561198000000002",
      "rotation": 0
    }
  • POST/v1/players/{steamId}/dino/sex

    Trocar o sexoTroca o sexo do dino vivo do jogador: male true para macho, false para fêmea. Só responde ok depois de o jogo ler o sexo novo de volta, na hora e um instante depois.

    dino:write

    Trocar o sexo com o dino vivo é recente e ainda não foi provado em todo servidor. Quando o jogo não aceita ou desfaz, a resposta é 409 refused com reason notApplied, e nada muda no dino: trate como resposta normal. Sem dino em campo é noDinosaur, fora do jogo é playerOffline, e o painel anterior a este endpoint responde 409 panelNotReady.

    Pedido e exemplo
    male
    true para macho, false para fêmea.
    POST /v1/players/{steamId}/dino/sex
    
    {
      "male": false
    }
  • GET/v1/players/online/dinos

    Dino de todos os onlineQuem está jogando agora, cada um com espécie, crescimento (0 a 1), prime, sexo (male) e o número do dino no jogo (dinoId), numa leitura só. Sai da memória, sem pedir nada ao painel: serve para vigiar espécie bloqueada, contar espécies e as missões do seu site. Posição não vem.

    dino:read

    Os dados vêm da última lista de online que o painel mandou, então asOf diz a idade de todos eles. O painel manda de novo quando alguém entra ou sai, troca de espécie, de prime ou de sexo, ou passa de uma faixa de 5% do crescimento, e pelo menos a cada 5 minutos: o crescimento pode estar até esse tanto atrás. species, growth, prime, male e dinoId saem null quando a pessoa está sem dino em campo (escolhendo espécie, morta ou renascendo) ou quando o jogo não disse, e nunca viram false. O painel anterior a este endpoint responde 409 panelNotReady. Leia uma vez por intervalo, e não uma vez por jogador: a chave divide 120 pedidos por minuto com o resto do seu site.

  • POST/v1/players/{steamId}/dino/growth-pause

    Pausar o crescimentoSegura o dino vivo do jogador no crescimento de agora até retomar. O estado aparece em GET .../dino (growthPaused e growthPausedAt).

    dino:write

    A pausa continua depois de relogar e de reiniciar o servidor, e cai quando o dino morre, troca de espécie ou é resgatado da garagem. Definir o crescimento (POST .../dino/growth) com o dino pausado mantém a pausa no tamanho novo. Recusas: noDinosaur (sem dino em campo), playerOffline, notApplied (já adulto). O painel anterior a estes endpoints responde 409 panelNotReady.

  • DELETE/v1/players/{steamId}/dino/growth-pause

    Retomar o crescimentoTira a pausa e o dino volta a crescer. Retomar o que não estava pausado é ok, sem mudar nada, e funciona com o jogador fora do jogo.

    dino:write

    A pausa continua depois de relogar e de reiniciar o servidor, e cai quando o dino morre, troca de espécie ou é resgatado da garagem. Definir o crescimento (POST .../dino/growth) com o dino pausado mantém a pausa no tamanho novo. Recusas: noDinosaur (sem dino em campo), playerOffline, notApplied (já adulto). O painel anterior a estes endpoints responde 409 panelNotReady.

Moderação, whitelist e mundo14 de 14 no ar
  • POST/v1/players/{steamId}/kick

    ExpulsarTira o jogador do servidor agora.

    moderation:write

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/kick
    
    {
      "reason": "Broke rule 3 in global chat"
    }
  • POST/v1/players/{steamId}/ban

    BanirBane o jogador, com motivo obrigatório e duração em horas.

    moderation:write

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    hours
    Quantas horas o banimento dura, até 87.600 (dez anos). 0, ou sem o campo, é para sempre.
    POST /v1/players/{steamId}/ban
    
    {
      "reason": "Broke rule 3 in global chat",
      "hours": 72
    }
  • DELETE/v1/players/{steamId}/ban

    DesbanirTira o jogador da lista de banidos.

    moderation:write

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    DELETE /v1/players/{steamId}/ban
    
    {
      "reason": "Appeal accepted"
    }
  • POST/v1/players/{steamId}/slay

    Matar o dinoMata o dino do jogador, que volta à seleção. Pede reason, e recusa em combate.

    moderation:write

    Pedido e exemplo
    reason
    O motivo, que fica no histórico. Até 120 caracteres na moeda e no inventário, até 200 na moderação. Num DELETE, pode ir na consulta (?reason=).
    POST /v1/players/{steamId}/slay
    
    {
      "reason": "Stuck under the map"
    }
  • GET/v1/bans

    BanidosOs SteamIDs das listas de banidos do servidor.

    bans:read

    Pedido e exemplo
    ?offset
    Onde a página começa. Cada resposta traz next, o offset da página seguinte, ou null na última. Sem ele, vem a primeira.
    GET /v1/bans?offset=0
  • POST/v1/whitelist

    Pôr na whitelistLibera a entrada de SteamIDs no servidor com whitelist.

    whitelist:write

    Pedido e exemplo
    steamIds
    Os SteamIDs para pôr na whitelist, de 1 a 200 por pedido.
    POST /v1/whitelist
    
    {
      "steamIds": [
        "76561198000000001",
        "76561198000000002"
      ]
    }
  • DELETE/v1/whitelist/{steamId}

    Tirar da whitelistTira um SteamID da whitelist.

    whitelist:write

  • POST/v1/world/save

    Salvar o mundoForça o servidor a salvar o mundo agora.

    world:write

  • POST/v1/world/clear-corpses

    Limpar corposTira os corpos do mapa.

    world:write

  • GET/v1/voice/bans

    Listar quem está fora da vozOs voice-bans ativos, com o prazo, o motivo e quem tirou.

    bans:read

  • POST/v1/players/{steamId}/skins

    Guardar uma peleGuarda uma pele nova do jogador: name (de 1 a 40 caracteres), colors por região (r, g e b de 0 a 1) e glow (de 1 a 10, só com o brilho do dono ligado). Até maxPeles por jogador; depois, 409 skinStorageFull com limit. Não mexe no jogo.

    skins.player:write

    Pedido e exemplo
    name
    O nome da pele, de 1 a 40 caracteres.
    colors
    As cores, por região do dino (como corpo), cada uma com r, g e b.
    glow
    O brilho por região, de 1 a 10. Só a pintura da equipe (dino-paint) aceita os valores negativos, os níveis de glitch do jogo. Opcional.
    POST /v1/players/{steamId}/skins
    
    {
      "name": "Night hunter",
      "colors": {
        "corpo": {
          "r": 0.8,
          "g": 0.25,
          "b": 0.1
        },
        "ventre": {
          "r": 0.9,
          "g": 0.85,
          "b": 0.7
        }
      },
      "glow": {
        "olhos": 4
      }
    }
  • POST/v1/players/{steamId}/skins/apply

    Aplicar uma pelePinta o dino de agora do jogador com uma pele guardada (skinId) ou com o dino editado no seu site (name, colors e glow, as dez regiões que o editor mostrou). speciesClass é a espécie que o editor mostrou: se o dino mudou, 409 refused com speciesMismatch e nada é pintado. pattern (0 em diante) pede o painel 0.2.10. Se o dino tem uma pele comprada (colorsFrom shop em GET .../dino), mande replaceBought true depois que o jogador confirmar; sem isso volta 409 refused com boughtSkin. Cada aplicação conta na janela e cobra o custo do dono; a recusa (em combate, sem dino, sem moedas) devolve o uso e as moedas. Com a janela gasta, 429 applicationLimitReached com Retry-After.

    skins.player:writepede Idempotency-Key

    Pedido e exemplo
    skinId
    O id de uma das peles guardadas do jogador (de GET .../peles).
    name
    O nome da pele, de 1 a 40 caracteres.
    colors
    As cores, por região do dino (como corpo), cada uma com r, g e b.
    glow
    O brilho por região, de 1 a 10. Só a pintura da equipe (dino-paint) aceita os valores negativos, os níveis de glitch do jogo. Opcional.
    speciesClass
    A classe da espécie no jogo, como BP_Tyrannosaurus_C.
    pattern
    O número do padrão.
    replaceBought
    true depois que o jogador confirma trocar a pele comprada que o dino tem. Opcional.
    POST /v1/players/{steamId}/skins/apply
    
    {
      "skinId": "3f6c2a9e-8d41-4b7a-9c2e-5a1f0d7b6e34",
      "name": "Night hunter",
      "colors": {
        "corpo": {
          "r": 0.8,
          "g": 0.25,
          "b": 0.1
        }
      },
      "glow": {
        "olhos": 4
      },
      "speciesClass": "BP_Tyrannosaurus_C",
      "pattern": 1,
      "replaceBought": false
    }
  • PATCH/v1/players/{steamId}/skins/{skinId}

    Editar uma peleMuda só os campos enviados: name, colors ou glow. glow null tira o brilho. Sem glow, o brilho guardado fica, mesmo com o brilho do dono desligado. Id que não existe: 404 skinNotFound.

    skins.player:write

    Pedido e exemplo
    name
    O nome da pele, de 1 a 40 caracteres.
    colors
    As cores, por região do dino (como corpo), cada uma com r, g e b.
    glow
    O brilho por região, de 1 a 10. Só a pintura da equipe (dino-paint) aceita os valores negativos, os níveis de glitch do jogo. Opcional.
    PATCH /v1/players/{steamId}/skins/{skinId}
    
    {
      "name": "Night hunter 2",
      "colors": {
        "corpo": {
          "r": 0.8,
          "g": 0.25,
          "b": 0.1
        }
      },
      "glow": null
    }
  • DELETE/v1/players/{steamId}/skins/{skinId}

    Apagar uma peleApaga uma pele guardada. Apagar a que já saiu responde o mesmo 200, com deleted false. Não mexe no dino.

    skins.player:write

Configuração, só leitura9 de 9 no ar
  • GET/v1/config/casino

    CassinoSe o cassino está ligado e os limites de aposta.

    config:read

  • GET/v1/config/kill-reward

    Recompensa por abateQuanto o servidor paga por abate, por espécie e peso.

    config:read

  • GET/v1/config/income

    Renda por tempoQuanto quem joga recebe de tempos em tempos.

    config:read

  • GET/v1/config/chat-commands

    Comandos no jogo (!)Os comandos que o chat do jogo responde, e quais estão ligados.

    config:read

  • GET/v1/config/invite

    Convite de vínculoA regra do convite para quem entra sem vínculo.

    config:read

  • GET/v1/config/afk

    AusênciaA regra que expulsa quem ficou parado.

    config:read

  • GET/v1/config/mutations

    Mutações do servidorQuais mutações os jogadores podem escolher e quantos slots.

    config:read

  • GET/v1/config/pop-lock

    Pop LockOcupação, limite e disponibilidade de cada espécie, sem dados dos jogadores.

    config:read

  • GET/v1/config/teleport

    Limite do teleporte de amigoO limite de crescimento do teleporte de amigo, como o dono deixou em Painel do jogador › Recursos: friendGrowthLimit.enabled e friendGrowthLimit.maxGrowth (de 0 a 1, a mesma escala do growth do dino). Os dois null: painel anterior ao limite. Use para não gerar código de amigo a quem está no limite; o teleport/to-player recusa com growthLimit de qualquer jeito.

    config:read