uloggdDesenvolvedores
Portal do desenvolvedoruloggd
ComeçarRecursosArcade
Portal do desenvolvedoruloggdIdentidadeCatálogoPerfilBibliotecaAvaliaçõesDiárioListasPremiaçõesCapturasSocialContaNotificaçõesComentáriosCurtidasHistóricoPessoasMineraisArcade

Perfil

O perfil do próprio dono.

Nenhum escopo aqui alcança o de outra pessoa. Mandar só os campos que mudam deixa o resto como está.

GET/api/v1/drafts

Lista os rascunhos do dono.

Escopo profile.read · Conta na cota read

POST/api/v1/drafts

Salva um rascunho.

Escopo profile.write · Conta na cota write

GET/api/v1/drafts/{id}

Lê um rascunho do dono.

Escopo profile.read · Conta na cota read

PATCH/api/v1/drafts/{id}

Atualiza um rascunho do dono.

Escopo profile.write · Conta na cota write

DELETE/api/v1/drafts/{id}

Remove um rascunho do dono.

Escopo profile.write · Conta na cota write

GET/api/v1/profile

O perfil do dono.

Escopo profile.read · Conta na cota read

GET/api/v1/profile/images

As fotos que a conta já usou, para poder voltar a uma sem procurar o arquivo de novo. Subir uma nova é trabalho do pipeline de imagens, não daqui.

Escopo profile.read · Conta na cota read

Parâmetros

CampoTipoObservações
kindobrigatóriostringAVATAR ou BANNER.
DELETE/api/v1/profile/images/{id}

Esquece uma foto antiga. A que está no perfil agora continua onde está.

Escopo profile.write · Conta na cota write

PATCH/api/v1/profile

Altera os campos de exibição. Mande só o que muda; o resto fica como está.

Escopo profile.write · Conta na cota write

Corpo

CampoTipoObservações
display_namestringAté 60 caracteres.
biostringAté 500 caracteres.
pronounsstringAté 30 caracteres.
thoughtstringAté 100 caracteres.
youtube_usernamestringAté 60 caracteres.
instagram_usernamestringAté 30 caracteres.
twitter_usernamestringAté 15 caracteres.
library_visibilitystringQuem vê a biblioteca: PUBLIC, FOLLOWERS ou PRIVATE. É do perfil, e não de cada jogo.
GET/api/v1/profile/layout

O layout do Profile Studio do dono: draft (o rascunho em edição), published (o que os visitantes veem, ou null), version (a versão que o próximo salvamento deve citar), published_version, published_at, updated_at, customized e draft_incomplete (o rascunho guardado tinha algo que esta versão do site não sabe ler e veio sem isso). Sem layout salvo, draft é o layout padrão, version é 0 e customized é false. Um documento salvo que esta versão do site não consegue ler aparece como o padrão, mas com a version real, para o próximo salvamento funcionar. media resolve cada imagem enviada para o layout, pelo id, em url, width, height e animated: todas as do dono, porque o rascunho pode citar qualquer uma.

Escopo profile.read · Conta na cota read

Exemplo

{
  "data": {
    "draft": { "schema": 2, "page": { ... }, "sections": [ ... ] },
    "published": null,
    "version": 3,
    "published_version": null,
    "published_at": null,
    "updated_at": "2026-10-09T18:20:00.000Z",
    "customized": true,
    "draft_incomplete": false,
    "media": {
      "k3Jd8Qp2Lm": {
        "url": "https://media.uloggd.com/layout/.../....avif",
        "width": 1600, "height": 900, "animated": false
      }
    }
  }
}
PUT/api/v1/profile/layout

Salva o rascunho. Os visitantes continuam vendo o publicado até POST /api/v1/profile/layout/publish. version é a versão que o editor carregou; se outra aba salvou antes, a resposta é 409 conflict com a version atual em error.version, para recarregar em vez de sobrescrever. Um documento inválido é 400 invalid_request com error.issues (até 20, cada um com code, message e, quando houver, path e type). Responde version e updated_at; salvar um rascunho igual ao atual não muda a version.

Escopo profile.write · Conta na cota write

Corpo

CampoTipoObservações
layoutobrigatórioobjectO documento: schema 2, page (width, density, background) e de 1 a 12 sections, cada uma de um kind. Uma seção flow tem de 1 a 3 columns cujos span somam 12, e cada coluna até 12 blocks. Cada bloco tem id, type, visibility (mobile, tablet, desktop), style e config. type é um destes, e nenhum outro: identity, shortcuts, social, stats, showcase, games, reviews, activity, lists, screenshots, image, comments, text, heading, divider, spacer. Uma seção canvas é uma área de proporção fixa (aspect, a altura sobre a largura, de 0.2 a 3; phone, scale ou stack) com até 24 elements em posição livre, na ordem em que são desenhados e lidos: cada um tem id, type (image, text, shape ou block, este só com stats, social, heading, shortcuts ou image), box (x, y, w, h em frações da largura da área, sem passar da borda; w e h a partir de 0.001), rotation, opacity, locked, visibility e config. background, na página, numa seção ou no style de um bloco, é uma imagem: media (ou null, que não desenha nada), fit, position, dim (0 a 80, o quanto ela escurece) e tone (auto, light ou dark: o tema que as cores do próprio site seguem por cima da imagem; light e dark o fixam, e auto conta como dark uma imagem com dim de 50 ou mais e deixa o tema do visitante abaixo disso; sem imagem desenhada, tone não muda nada). Não existe cor de fundo nem cor de texto: o texto, inclusive o de um elemento text, segue o tema; só uma shape tem cores, fill e stroke (#rrggbb ou null). Uma shape line pode ter thickness (de 0.001 a 0.05 da largura da área); sem ele, strokeWidth conta em pixels numa largura de 1000px. Textos e blocos embutidos são compostos numa largura de referência de 1000px (um text de size 0.05 tem 50px) e escalados com a área; com phone scale um texto não tem tamanho mínimo, e com phone stack os elementos seguem a posição (de cima para baixo e, numa linha, da esquerda para a direita) e um texto fica entre 12px e 40px. Uma imagem é sempre uma referência, nunca uma URL: { kind: media, id } (enviada em /api/v1/profile/layout/media), { kind: screenshot, id } (uma captura do dono) ou { kind: cover, slug } (a capa de um jogo). identity é obrigatório e sempre visível. No máximo 40 blocos, 80 elementos e 48000 bytes em UTF-8. Um documento schema 1 (só seções de colunas, sem background) continua aceito: é convertido para o 2 e guardado assim. Em qualquer schema, as cores que já não existem são descartadas, e não recusadas: color num background (um background que só tinha cor, sem media, vira null), textColor no style de um bloco e color, background e radius no config de um elemento text. Nenhuma resposta traz essas chaves.
versionobrigatóriointegerA version que o editor carregou: 0 quando ainda não há nada salvo (também depois de um DELETE).

Exemplo

{ "data": { "version": 4, "updated_at": "2026-10-09T18:21:02.000Z" } }
DELETE/api/v1/profile/layout

Volta ao layout padrão: apaga o rascunho e o publicado, para o dono e para todos. Repetir não é erro. Depois disso o próximo salvamento cita version 0.

Escopo profile.write · Conta na cota write

Exemplo

{ "data": { "reset": true } }
POST/api/v1/profile/layout/publish

Publica o rascunho salvo na version indicada, que passa a ser o que os visitantes veem. Se o rascunho mudou desde então, 409 conflict com a version atual em error.version; sem layout salvo, 404 not_found. Publicar não muda a version.

Escopo profile.write · Conta na cota write

Corpo

CampoTipoObservações
versionobrigatóriointegerA version do rascunho que o editor mostra, 1 ou mais.

Exemplo

{ "data": { "version": 4, "published_at": "2026-10-09T18:22:10.000Z" } }
GET/api/v1/profile/layout/media

As imagens que o dono enviou para o layout, da mais nova para a mais antiga: id, url, width, height, bytes, animated, created_at e in_use (o rascunho ou o publicado cita o id). quota traz limit (15), count, bytes_limit (10485760, 10 MB) e bytes. Só com a sessão do site; uma chave recebe 403.

Escopo profile.read · Conta na cota read

Exemplo

{
  "data": [
    {
      "id": "k3Jd8Qp2Lm", "url": "https://media.uloggd.com/layout/.../....avif",
      "width": 1600, "height": 900, "bytes": 184320, "animated": false,
      "created_at": "2026-10-11T19:02:00.000Z", "in_use": true
    }
  ],
  "quota": { "limit": 15, "count": 1, "bytes_limit": 10485760, "bytes": 184320 }
}
POST/api/v1/profile/layout/media

Envia uma imagem para usar no layout, como multipart/form-data com o arquivo em image (JPEG, PNG, WebP, GIF ou AVIF, até 15 MB; um GIF ou WebP animado continua animado). Ela é reduzida para caber em 1600x1600, nunca ampliada, e otimizada: uma imagem parada vira um AVIF de cerca de 300 KB e nunca mais de 1 MB, e uma animação um WebP de no máximo 3 MB e 10 segundos. Passa pela mesma verificação de conteúdo de um avatar, vai para a hospedagem de imagens e recebe um id, que é o que o documento do layout cita. Responde 201 com id, url, width, height, bytes, animated e created_at. Uma imagem recusada é 400 invalid_request com reason: invalid_format (não é JPEG, PNG, WebP, GIF nem AVIF, ou o conteúdo não é do tipo enviado), too_large (mais de 15 MB), invalid_image (não deu para ler), too_many_frames (mais de 300 quadros), too_many_pixels (mais de 40 milhões de pixels, somando os quadros), animation_too_long (mais de 10 segundos), unsupported_animation (um PNG ou AVIF animado, ou uma animação sem a duração de cada quadro), too_heavy (continua acima de 1 MB, ou de 3 MB animada, mesmo depois de otimizada) ou sensitive_image (a verificação de conteúdo a recusou). 409 com reason quota_exceeded (e limit, count, bytes_limit, bytes) quando a conta já tem 15 imagens ou 10 MB, ou quando esta, já otimizada, passaria dos 10 MB; 429 pela cota de 30 envios a cada 10 minutos ou com reason busy quando o processamento está cheio; 403 com reason mfa_required numa sessão que ainda deve o segundo fator; 500 internal com reason upload_unavailable, screening_unavailable ou upload_failed quando a hospedagem ou a verificação falham. Só com a sessão do site.

Escopo profile.write · Conta na cota write

Corpo

CampoTipoObservações
imageobrigatóriofileO arquivo, num campo de formulário multipart.

Exemplo

{
  "data": {
    "id": "k3Jd8Qp2Lm", "url": "https://media.uloggd.com/layout/.../....avif",
    "width": 1600, "height": 900, "bytes": 184320, "animated": false,
    "created_at": "2026-10-11T19:02:00.000Z"
  }
}
DELETE/api/v1/profile/layout/media/{id}

Apaga uma imagem do layout. Enquanto o rascunho ou o publicado ainda a citam, a resposta é 409 conflict com reason in_use, a não ser com force=true, que apaga assim mesmo (o layout fica com um espaço vazio onde ela estava). 404 quando não há imagem sua com esse id; 403 com reason mfa_required numa sessão que ainda deve o segundo fator. Só com a sessão do site.

Escopo profile.write · Conta na cota write

Parâmetros

CampoTipoObservações
forcebooleantrue para apagar mesmo que um layout ainda a cite.

Exemplo

{ "data": { "id": "k3Jd8Qp2Lm", "deleted": true } }

O layout do Profile Studio

Um perfil pode ser montado com blocos organizados em seções e colunas, e com áreas livres onde imagens, textos e formas ficam onde o dono os coloca. A montagem é um documento JSON por perfil, guardado em duas cópias: o rascunho (draft), que só o dono lê e que cada PUT substitui, e a cópia publicada (published), que é o que os visitantes veem. Salvar nunca muda o que os visitantes veem; POST /api/v1/profile/layout/publish copia o rascunho salvo por cima do publicado. Sem nada salvo, o rascunho é o layout padrão, o perfil que todo mundo já conhece, e published é null (os visitantes veem o padrão). DELETE volta a ele para todos. Quando o rascunho guardado tem algo que esta versão do site não sabe ler (um tipo de bloco de uma versão mais nova), draft_incomplete é true e draft vem sem essa parte: salvar por cima a apaga.

O type de cada bloco é um de um conjunto fechado, e qualquer outro é recusado: identity, shortcuts, social, stats, showcase, games, reviews, activity, lists, screenshots, image, comments, text, heading, divider, spacer. O config de um bloco guarda opções e referências (o slug de um jogo, o id público de uma captura), nunca marcação, script ou URL. identity, o cabeçalho do perfil, é obrigatório e não pode ser escondido.

O documento

O documento atual é o schema 2. page guarda a largura (width), o espaço entre as seções (density) e um fundo opcional (background), e sections tem de 1 a 12 seções, cada uma de um kind:

  • flow: de 1 a 3 columns numa grade de 12 (os span somam 12), cada coluna com até 12 blocos, um embaixo do outro.
  • canvas: uma área de proporção fixa, com aspect (a altura sobre a largura, de 0.2 a 3) e phone (scale encolhe a área inteira no celular, stack empilha os elementos numa coluna na ordem em que aparecem: de cima para baixo e, numa mesma linha, da esquerda para a direita). Ela guarda até 24 elements em posição livre: box dá x, y, w e h em frações da largura da área, com w e h a partir de 0.001, e nenhum elemento passa da borda (o editor mantém o que não é linha em 0.02 ou mais). Cada elemento tem rotation, opacity, locked, visibility e um type: image, text, shape ou block, que embute um bloco stats, social, heading, shortcuts ou image. A ordem da lista é a ordem do desenho (o último fica por cima) e também a ordem em que um leitor de tela lê.

Textos e blocos embutidos são compostos numa largura de referência de 1000px e depois escalados junto com a área, como uma imagem: um text de size 0.05 tem 50px nessa largura, e uma caixa de w 0.4 tem 400px. Assim eles quebram as linhas nos mesmos lugares em qualquer largura. Com phone scale, um texto encolhe com a área no celular, sem tamanho mínimo; com stack, ele volta a ser texto na largura da coluna, com 60% do tamanho que tem na largura de referência, entre 12px e 40px. O editor mantém a altura da caixa de um texto, de um bloco embutido e de uma linha ajustada ao que ela contém.

Uma shape line é um traço no meio da sua caixa, com thickness opcional (de 0.001 a 0.05 da largura da área), que escala com a área e nunca fica abaixo de 1px. Uma linha salva antes de thickness existir continua usando strokeWidth, contado em pixels numa largura de 1000px. Num retângulo ou num círculo, strokeWidth é a borda, em pixels.

A página, uma seção e o style de um bloco podem ter background, que é sempre uma imagem: media (a imagem, ou null, que não desenha nada), fit, position, dim (de 0 a 80, o quanto a imagem escurece para o texto continuar legível) e tone (auto, light ou dark: o tema que as cores do próprio site seguem por cima da imagem, para o texto continuar legível em qualquer tema). light e dark fixam esse tema; auto usa dark quando o dim é 50 ou mais e, abaixo disso, deixa o tema do visitante. Sem imagem desenhada, tone não muda nada. Não existe cor de fundo nem cor de texto: o texto, inclusive o de um elemento text, segue sempre o tema, e só uma shape tem cores próprias (fill e stroke, #rrggbb ou null). Uma imagem nunca é uma URL, e sim uma referência: { "kind": "media", "id": "..." } para uma imagem enviada em Imagens do layout, { "kind": "screenshot", "id": "..." } para uma captura do dono e { "kind": "cover", "slug": "..." } para a capa de um jogo. Uma referência à imagem de outra pessoa, ou a uma que já não existe, não desenha nada.

O perfil todo tem no máximo 40 blocos (os das colunas e os embutidos nas áreas livres, somados) e 80 elementos. Um documento schema 1, de antes das áreas livres e dos fundos, continua aceito num PUT: ele é convertido para o 2 (cada seção vira flow, sem fundo, e o screenshot do bloco image vira source) e já é guardado assim, então o GET seguinte responde schema 2.

Um PUT que ainda manda as cores de antes não é recusado: elas são descartadas, em qualquer schema. São color num background (um background que tinha cor e nenhuma media vira null), textColor no style de um bloco e color, background e radius no config de um elemento text. Um documento guardado com elas é lido sem elas, então nenhum GET as traz.

Versões

version conta as mudanças no rascunho. GET responde a atual (0 quando não há nada salvo), e cada salvamento e publicação cita a versão em que se baseou. Se o rascunho mudou nesse meio tempo, em outra aba ou outro aparelho, a resposta é um conflito que traz a versão atual, para o editor oferecer recarregar em vez de sobrescrever o trabalho de alguém:

{
  "error": {
    "code": "conflict",
    "message": "The layout changed elsewhere. Reload it before saving.",
    "version": 5
  }
}

Salvar um rascunho idêntico ao guardado mantém a mesma versão. Depois de um DELETE, o próximo salvamento cita a versão 0 de novo.

Validação

O documento é conferido por inteiro antes de ser guardado: a forma, ids únicos, colunas cujos spans somam 12, os limites de blocos por tipo e no total, os de elementos, elementos dentro da sua área, o cabeçalho obrigatório e no máximo 48000 bytes. Uma recusa lista o que está errado, até 20 problemas, cada um com um code estável (shape, duplicate_id, spans, too_many_blocks, too_many_of_type, too_many_elements, out_of_bounds, required_missing, required_hidden, too_large), uma message para pessoas e, quando se aplica, o path dentro do documento e o type do bloco:

{
  "error": {
    "code": "invalid_request",
    "message": "The layout is not valid.",
    "issues": [
      {
        "code": "spans",
        "message": "The columns of a section must add up to 12.",
        "path": ["sections", 1, "columns"]
      }
    ]
  }
}

Imagens do layout

Um layout pode mostrar imagens que o dono enviou para ele, e o documento as cita pelo id, nunca por URL. POST /api/v1/profile/layout/media recebe o arquivo como multipart/form-data no campo image (JPEG, PNG, WebP, GIF ou AVIF, até 15 MB), reduz para caber em 1600x1600 e otimiza: uma imagem parada vira um AVIF de cerca de 300 KB, nunca acima de 1 MB, e uma animação (GIF ou WebP) vira um WebP de até 3 MB e 10 segundos. Depois passa pela mesma verificação de conteúdo de um avatar (uma imagem sensível é recusada, porque uma página de perfil não tem como cobri-la), guarda na hospedagem de imagens e responde com o id, a url, width, height, bytes e animated.

Uma imagem recusada é 400 invalid_request com um reason: invalid_format, too_large, invalid_image, too_many_frames, too_many_pixels, animation_too_long, unsupported_animation, too_heavy ou sensitive_image (a tabela acima diz o que cada um quer dizer). too_heavy é uma imagem que continua pesada demais mesmo depois de otimizada, quase sempre uma animação longa ou grande:

{
  "error": {
    "code": "invalid_request",
    "message": "That image is still too heavy after optimization. Use a shorter or smaller animation.",
    "reason": "too_heavy"
  }
}

Uma conta guarda até 15 imagens e 10 MB (10485760 bytes); ao passar disso a resposta é 409 com reason: "quota_exceeded", limit, count, bytes_limit e bytes. Uma conta que já tinha mais do que isso quando o limite baixou guarda tudo o que tem, mas só envia de novo depois de apagar o bastante para ficar abaixo.

GET /api/v1/profile/layout/media lista as imagens com in_use, que diz se o rascunho ou o publicado cita o id, e a quota ao lado de data. DELETE /api/v1/profile/layout/media/{id} apaga uma; enquanto um layout ainda a cita, a resposta é 409 com reason: "in_use", e ?force=true apaga assim mesmo. Uma imagem que nenhum layout cita há sete dias é removida sozinha.

Esses três endpoints são só da sessão do site: uma chave recebe 403, como em Conta, e o envio e a remoção respondem 403 com reason: "mfa_required" enquanto a sessão ainda deve o segundo fator. Nas leituras, media resolve cada id em url, width, height e animated: em GET /api/v1/profile/layout, todas as imagens do dono; em GET /api/v1/profiles/{username}/layout, só as que o documento publicado cita, para que uma imagem enviada para o rascunho não apareça antes de ser publicada.

Os visitantes leem o layout publicado por GET /api/v1/profiles/{username}/layout, em Pessoas.

Catálogo

Jogos, do mesmo catálogo que o site lê.

Biblioteca

O que o dono está jogando, já jogou e quer jogar.