Erros e limites
Formato dos erros, códigos de resposta, limites de uso e como repetir chamadas com segurança.
A API responde com os códigos HTTP padrão e, quando algo dá errado, com um corpo que diz o motivo de forma estável — para o seu código tratar cada caso sem depender do texto da mensagem.
Formato do erro
Todo erro vem como application/problem+json (RFC 9457):
{
"type": "validation-failed",
"title": "Dados inválidos.",
"status": 422,
"errors": {
"email": ["Este e-mail já está em uso."]
},
"request_id": "9b3adf86-f581-4f68-af3b-6dae067f956a"
}
| Campo | O que é |
|---|---|
type | Identificador estável do tipo de erro. É este campo que o seu código deve testar. |
title | Frase curta, para exibir ou registrar em log. |
status | O mesmo código HTTP da resposta. |
detail | Explicação adicional, quando houver algo útil a dizer. |
errors | Na validação: a lista de problemas de cada campo. |
reason | Só no 409: o motivo do conflito. Veja a lista. |
request_id | Identificador da chamada. Informe-o ao suporte. |
Códigos de resposta
| Código | type | Quando acontece |
|---|---|---|
200 201 202 204 | — | Deu certo. 201 quando algo foi criado, 202 quando um e-mail foi enfileirado e 204 em exclusões, sem corpo. |
400 | token-in-query-string | O token foi enviado na URL. Envie só no cabeçalho Authorization. |
401 | unauthenticated | Sem token, ou token inválido, vencido, suspenso, revogado ou usado de um IP fora da lista. A resposta é a mesma para todos os casos; o motivo fica no histórico do token, no painel. |
403 | forbidden | O token não tem a permissão que a rota exige. O detail diz qual falta. |
403 | tls-required | Chamada sem HTTPS. |
404 | not-found | O registro não existe, foi excluído ou não é desta escola. |
404 | api-disabled | A API não está disponível para esta escola. |
405 | method-not-allowed | Método HTTP que a rota não aceita. |
409 | conflict | Uma regra da escola impede a operação. O reason diz qual. |
409 | idempotency-key-conflict | A mesma Idempotency-Key foi reaproveitada num pedido diferente. |
413 | payload-too-large | Corpo acima de 1 MB. Para muitos registros, use as rotas em lote. |
415 | unsupported-media-type | O corpo não é JSON. Envie Content-Type: application/json. |
422 | validation-failed | Dados inválidos. Os problemas de cada campo vêm em errors. |
422 | unknown-field | Campo que a rota não conhece. A API recusa em vez de ignorar, para um nome digitado errado não passar em silêncio. |
422 | plan-limit-reached | A operação passaria do limite do plano da escola (por exemplo, de alunos ativos). |
429 | rate-limited | Limite de chamadas atingido. Espere os segundos de Retry-After. |
500 | server-error | Falha do nosso lado. Pode repetir com a mesma Idempotency-Key; se persistir, fale com o suporte citando o request_id. |
503 | school-unavailable | A escola está temporariamente indisponível. Tente de novo em alguns minutos. |
Motivos de conflito (409)
Quando uma regra da escola barra a operação, o campo reason traz um destes códigos:
reason | Significado |
|---|---|
has_courses | A categoria ainda tem cursos. |
has_enrollments | O curso ainda tem matrículas. |
missing_price | O curso não tem preço ou duração configurados; não pode ser publicado. |
missing_plan | O curso não tem plano configurado; não aceita matrícula. |
order_mismatch | A lista enviada para reordenar não traz exatamente os ids existentes, uma vez cada. |
category_cycle | A categoria ficaria dentro de si mesma. |
parent_not_found | A categoria-mãe informada não existe. |
max_depth | Passaria do limite de níveis de categorias. |
lesson_type_not_supported | Esse tipo de aula não pode ser criado ou alterado pela API. |
quiz_visibility_managed | A visibilidade do quiz é controlada pelo painel. |
recently_sent | O link de senha foi enviado há pouco; aguarde antes de pedir outro. |
user_inactive | O usuário está inativo. |
policy_* | Regras da escola sobre a gestão de usuários (o texto explica qual). |
Limites de uso
| Limite | Valor |
|---|---|
| Chamadas por token, por minuto | 60 (o administrador pode definir outro valor ao criar o token) |
| Chamadas por token, por dia | 10.000 |
Relatórios (/reports), por minuto | 10 |
| Chamadas da escola inteira, por minuto (somando todos os tokens) | 300 |
| Itens por chamada em lote | 100 |
| Tamanho do corpo | 1 MB |
| Itens por página nas listas | até 100 (padrão 25) |
Cada resposta informa onde você está no limite:
| Cabeçalho | O que diz |
|---|---|
X-RateLimit-Limit | Limite por minuto deste token. |
X-RateLimit-Remaining | Quantas chamadas restam na janela atual. |
X-RateLimit-Reset | Quando a janela reinicia (epoch, em segundos). |
Retry-After | No 429: quantos segundos esperar antes de tentar de novo. |
Proteção contra tentativas: 20 falhas de autenticação num minuto, vindas do mesmo IP, bloqueiam esse IP na escola por 15 minutos. Se a sua integração receber 401, pare e confira o token em vez de repetir em laço.
Repetir sem duplicar (idempotência)
Rede falha: às vezes a chamada chega, a operação acontece e a resposta se perde. Para repetir sem criar o registro duas vezes, envie o cabeçalho Idempotency-Key nas escritas.
- Use uma chave de 8 a 128 caracteres, com letras, números, hífen e sublinhado — um UUID serve. Gere uma chave por operação.
- Repetir a chamada com a mesma chave devolve a resposta guardada, sem executar de novo. Ela vem com o cabeçalho
Idempotent-Replay: true. - A chave fica guardada por 24 horas.
- Usar a mesma chave num pedido diferente gera
409 idempotency-key-conflict. - É recomendada em toda escrita e obrigatória no cadastro de usuário e na matrícula em lote.
curl -X POST "https://minhaescola.lmsestudio.com/api/v1/groups" \
-H "Authorization: Bearer $LMS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0c9d2a7e-3f41-4b8e-9f0a-2d6b1c5e8a11" \
-d '{ "name": "Turma Noite 2026" }'
Paginação
As listas aceitam page (a partir de 1) e per_page (até 100, padrão 25) e respondem sempre no mesmo envelope:
{
"data": [ ... ],
"meta": { "page": 1, "per_page": 25, "total": 42 },
"links": {
"first": "https://minhaescola.lmsestudio.com/api/v1/courses?page=1",
"last": "https://minhaescola.lmsestudio.com/api/v1/courses?page=2",
"prev": null,
"next": "https://minhaescola.lmsestudio.com/api/v1/courses?page=2"
}
}
Para percorrer tudo, siga links.next até ele vir null. Algumas listas também aceitam sort e dir (asc ou desc); veja quais na Referência.