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á.
/api/ v1/ draftsLista os rascunhos do dono.
/api/ v1/ draftsSalva um rascunho.
/api/ v1/ drafts/ {id}Lê um rascunho do dono.
/api/ v1/ drafts/ {id}Atualiza um rascunho do dono.
/api/ v1/ drafts/ {id}Remove um rascunho do dono.
/api/ v1/ profileO perfil do dono.
/api/ v1/ profile/ imagesAs 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.
Parâmetros
| Campo | Tipo | Observações |
|---|---|---|
kindobrigatório | string | AVATAR ou BANNER. |
/api/ v1/ profile/ images/ {id}Esquece uma foto antiga. A que está no perfil agora continua onde está.
/api/ v1/ profileAltera os campos de exibição. Mande só o que muda; o resto fica como está.
Corpo
| Campo | Tipo | Observações |
|---|---|---|
display_name | string | Até 60 caracteres. |
bio | string | Até 500 caracteres. |
pronouns | string | Até 30 caracteres. |
thought | string | Até 100 caracteres. |
youtube_username | string | Até 60 caracteres. |
instagram_username | string | Até 30 caracteres. |
twitter_username | string | Até 15 caracteres. |
library_visibility | string | Quem vê a biblioteca: PUBLIC, FOLLOWERS ou PRIVATE. É do perfil, e não de cada jogo. |
/api/ v1/ profile/ layoutO 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.
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
}
}
}
}/api/ v1/ profile/ layoutSalva 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.
Corpo
| Campo | Tipo | Observações |
|---|---|---|
layoutobrigatório | object | O 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ório | integer | A 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" } }/api/ v1/ profile/ layoutVolta 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.
Exemplo
{ "data": { "reset": true } }/api/ v1/ profile/ layout/ publishPublica 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.
Corpo
| Campo | Tipo | Observações |
|---|---|---|
versionobrigatório | integer | A version do rascunho que o editor mostra, 1 ou mais. |
Exemplo
{ "data": { "version": 4, "published_at": "2026-10-09T18:22:10.000Z" } }/api/ v1/ profile/ layout/ mediaAs 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.
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 }
}/api/ v1/ profile/ layout/ mediaEnvia 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.
Corpo
| Campo | Tipo | Observações |
|---|---|---|
imageobrigatório | file | O 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"
}
}/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.
Parâmetros
| Campo | Tipo | Observações |
|---|---|---|
force | boolean | true 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 3columnsnuma grade de 12 (osspansomam 12), cada coluna com até 12 blocos, um embaixo do outro.canvas: uma área de proporção fixa, comaspect(a altura sobre a largura, de 0.2 a 3) ephone(scaleencolhe a área inteira no celular,stackempilha 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é 24elementsem posição livre:boxdáx,y,wehem frações da largura da área, comweha 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 temrotation,opacity,locked,visibilitye umtype:image,text,shapeoublock, que embute um blocostats,social,heading,shortcutsouimage. 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.