Mapa da documentaçãoAPIComece em três passos
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.
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ó.
02Confira a chave
GET /v1/meresponde quem é a chave, de qual servidor, e se a API está aberta para ela. É a chamada certa para testar uma chave nova.curlcurl https://spiro.land/api/v1/me \ -H "Authorization: Bearer $SPIRO_LAND_KEY"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.statusdiz 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
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.
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.
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.
<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.
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 -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:
{
"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.
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.
{
"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.
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 jogador01Ligue a permissão e cadastre a volta
Em Onde fica:Integrações, API, crie ou edite uma chave secreta, marque
players:verifye 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.02Comece pelo servidor do seu site
Quando o jogador clicar, o servidor do seu site sorteia um
state, guarda na sessão dele e chamaPOST /v1/verificationscom oredirectUrie ostate. A resposta trazverification.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" } }03Receba a volta
O jogador volta ao seu
redirectUricomverification,stateecode, ou comerrorquando não deu certo. Compare ostatecom 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=Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY04Troque o código pelo SteamID
Do servidor do seu site, chame
POST /v1/verifications/{id}/exchangecom ocode. Oplayer.steamIdda 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" } }linkeddiz se o jogador já vinculou a conta ao Discord do seu servidor. Sem vínculo, abra a conta dele comPOST /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.nullquer 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.
// 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.
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
01Dê a permissão e os sites
Em Onde fica:Integrações, API, numa chave secreta, marque
map:reade liste em Sites que podem embutir o mapa os endereços do seu site, como https://seusite.com. O mapa só abre dentro deles.02Abra o embutido no seu servidor
Quando o jogador abrir a página, o servidor do seu site chama
POST /v1/players/{steamId}/map/embedcom 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" } }03Ponha a url num iframe
Use
embed.urlcomo 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.
// 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.
{
"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 https://spiro.land/api/v1/map/image?layer=color \
-H "Authorization: Bearer $SPIRO_LAND_KEY" \
-o gateway-color.webpA 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.
{
"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.
// 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).
<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".
{
"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 }
}// 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.
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.
{
"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
01Dê a permissão e o endereço de volta
Em Onde fica:Integrações, API, numa chave secreta, marque
kick:writee 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.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-linkcom o SteamID de quem entrou no seu site (pela verificação ou pelo seu login Steam), oredirectUricadastrado e umstatealeatório guardado na sessão dele. Mande o navegador parakickLink.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" } }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
redirectUricomkickLink,stateecode, ou comerror. Compare o state com o da sessão antes de qualquer coisa.https://yoursite.com/kick/return ?kickLink=kl_Ab3xQ9mK2LwP7tRz &state=f3Kq9xLm2PwZ7tRb &code=Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY04Troque o código
O servidor do seu site chama
POST /v1/kick-links/{linkId}/exchangecom ocode. Só aí o vínculo grava: dali em diante, o que o jogador ganha na Kick cai na carteira dele.redemptionsCrediteddiz 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.
{
"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.
// 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 kickAccountInUsee 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.
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.
{
"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.
<!-- 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.
<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.
01Abra o embutido pelo seu servidor
Dê a permissão
voice:embeda uma chave secreta, com o seu site na lista de sites, em Onde fica:Integrações, API. Para cada visita, o seu servidor chamaPOST /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" } }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>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.
{
"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.
Todo erro tem a mesma forma. code é o contrato e não muda; message é para você ler, em inglês, e pode mudar.
{
"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.
- 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.
- 429tooManyKickLinksEsta chave tem ligações da Kick abertas demais (até 300 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.
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
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:readGET/v1/players/online
Quem está onlineA lista de quem está jogando agora. Chave pública recebe só o nome.
players.online:readGET/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:readPedido 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=1727280000000GET/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:readPedido 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=0GET/v1/players/{steamId}
Um jogadorUm jogador pelo SteamID: vínculo, Discord, saldo e se está online.
players:readDELETE/v1/players/{steamId}/link
DesvincularDesfaz o vínculo do jogador com o Discord.
link:writeGET/v1/leaderboard/{species}
Placar de uma espécieO placar de 30 dias contando só os abates feitos jogando daquela espécie.
players:readPede 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:verifyPedido 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:verifyPedido 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:writeGET/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:readPedido 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=812GET/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:readPedido 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=4182POST/v1/players/{steamId}/link
Vincular o Discord pelo seu siteRegistra o vínculo entre o Discord e a Steam de um jogador que o seu site já provou pelos dois logins. Sem isso o bot não sabe de quem é a call. Repetir não muda nada, e conflito recusa sem trocar nada.
link:writePedido e exemplo
- discordId
- O id do usuário no Discord (17 a 20 dígitos), que o login do Discord no seu site provou.
POST /v1/players/{steamId}/link { "discordId": "123456789012345678" }GET/v1/players/{steamId}/kick-link
Ver a Kick do jogadorSe o jogador ligou a Kick, com o nome da conta, o que ele está juntando abaixo de uma moeda e os resgates esperando o vínculo.
players:readGET/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:readGET/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:readPOST/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:readGET/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:readGET/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:readGET/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:readPedido 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=colorGET/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:readO 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:readO 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:readPedido 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=allGET/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:readPedido 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=allGET/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:readPOST/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:writePedido 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:writePedido 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:writePedido 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=1727280000GET/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:readPOST/v1/players/{steamId}/garage/{slot}
Criar dino na garagemEscreve um dino numa vaga vazia. Nunca grava por cima.
garage.admin:writePedido 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:writePedido 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:readPedido 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=truePOST/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-KeyPedido 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:writePedido 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:writePedido 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:writePedido 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-KeyPedido 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:readPOST/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-KeyA 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-KeyPedido 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-KeyPedido 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:readPOST/v1/shop/purchases/{id}/complete
Concluir compraMarca a compra como entregue e libera o limite do jogador.
purchases:writePOST/v1/shop/purchases/{id}/refund
Estornar compraDevolve as moedas da compra ao jogador. Pede Idempotency-Key.
purchases:writepede Idempotency-KeyPOST/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-KeyPedido 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-KeyPedido 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-KeyPedido 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-KeyPedido 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-KeyPedido 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:readPOST/v1/players/{steamId}/kick-link
Ligar a Kick do jogadorAbre a ligação para a Steam que o seu site provou e devolve o endereço para onde o seu site manda o navegador. O jogador autoriza na Kick e volta ao seu site com um código.
kick:writePedido 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/players/{steamId}/kick-link { "redirectUri": "https://yoursite.com/kick/return", "state": "f3Kq9xLm2PwZ7tRb" }DELETE/v1/players/{steamId}/kick-link
Desligar a Kick do jogadorDesfaz o vínculo da Kick com esta Steam: as mensagens e os resgates seguintes deixam de pagar a ela.
kick:writePOST/v1/kick-links/{linkId}/exchange
Concluir a ligação da KickTroca o código que voltou na URL e grava o vínculo, uma vez só. Paga na hora os resgates que esperavam o vínculo.
kick:writePedido e exemplo
- code
- O código que chegou na URL de volta, com 43 caracteres. Vale uma troca, por cinco minutos.
POST /v1/kick-links/{linkId}/exchange { "code": "Qm8xT2vLk9PzR4wN7cJ1yB5hD3sF6gA0eU8iO2pK4tY" }
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:readPOST/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:writePedido 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:writePOST/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:writePedido 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:writePedido 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:readGET/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:readGET/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:readGET/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:readPOST/v1/players/{steamId}/dino/heal
CurarEnche a vida do dino do jogador.
dino:writePOST/v1/players/{steamId}/dino/prime
PrimeDá prime ao dino do jogador.
dino:writePOST/v1/players/{steamId}/dino/growth
CrescerMuda o crescimento do dino do jogador.
dino:writePedido 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:writePedido 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:writePedido 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:writePedido 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:writePedido 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:writePOST/v1/players/{steamId}/dino/pattern
Padrão da peleEscolhe o padrão da pele do dino do jogador.
dino:writePedido 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:writePedido 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:writePedido 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:writePedido 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:writePedido 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:readPOST/v1/players/{steamId}/teleport
Teleportar o jogadorLeva o dino do jogador a um ponto do mapa. Recusa em combate.
teleport.player:writePedido 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:writePedido 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:writeTrocar 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:readOs 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:writeA 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:writeA 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:writePedido 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:writePedido 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:writePedido 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:writePedido 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:readPedido 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=0POST/v1/whitelist
Pôr na whitelistLibera a entrada de SteamIDs no servidor com whitelist.
whitelist:writePedido 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:writePOST/v1/world/save
Salvar o mundoForça o servidor a salvar o mundo agora.
world:writePOST/v1/world/clear-corpses
Limpar corposTira os corpos do mapa.
world:writeGET/v1/voice/bans
Listar quem está fora da vozOs voice-bans ativos, com o prazo, o motivo e quem tirou.
bans:readPOST/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:writePedido 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-KeyPedido 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:writePedido 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:readGET/v1/config/kill-reward
Recompensa por abateQuanto o servidor paga por abate, por espécie e peso.
config:readGET/v1/config/income
Renda por tempoQuanto quem joga recebe de tempos em tempos.
config:readGET/v1/config/chat-commands
Comandos no jogo (!)Os comandos que o chat do jogo responde, e quais estão ligados.
config:readGET/v1/config/invite
Convite de vínculoA regra do convite para quem entra sem vínculo.
config:readGET/v1/config/afk
AusênciaA regra que expulsa quem ficou parado.
config:readGET/v1/config/mutations
Mutações do servidorQuais mutações os jogadores podem escolher e quantos slots.
config:readGET/v1/config/pop-lock
Pop LockOcupação, limite e disponibilidade de cada espécie, sem dados dos jogadores.
config:readGET/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