Tema
Autenticação
Toda chamada ao /v1 leva uma chave de API no header Authorization:
Authorization: Bearer edu_<prefixo>_<segredo>A empresa vem da própria chave. Não existe parâmetro de empresa em nenhuma rota — uma chave enxerga exatamente uma empresa, e nenhum dado de outra.
Escopos
Os escopos da chave são as mesmas permissões que o Edusati usa no painel. Ao criar a chave você escolhe quais liberar.
Quem pode criar a chave
Uma chave enxerga a instituição inteira — ela não tem recorte próprio por unidade ou por programa. Por isso só quem já tem acesso à instituição inteira consegue emitir uma. Se a pessoa que cuida da integração enxerga apenas uma unidade (ou apenas alguns programas), o painel recusa a criação: peça a chave a quem tem acesso total.
| Escopo | O que abre |
|---|---|
surveys:read | GET /forms, GET /forms/{id} |
reports:read | GET /forms/{id}/responses, GET /responses/{id}, GET /forms/{id}/stats |
invites:read | GET /forms/{id}/invites |
invites:write | POST /forms/{id}/invites, POST /invites/{id}/revoke |
invites:send | POST /forms/{id}/invites/send |
GET /v1/me funciona com qualquer chave válida, sem escopo.
Por que invites:send é separado de invites:write
Enviar consome a quota mensal de mensagens da empresa — ou seja, custa dinheiro. Uma integração que só precisa gerar links (para distribuir pelo seu próprio sistema) deve receber invites:write e nada mais.
Peça o mínimo. Uma chave nunca pode ter mais permissões do que quem a criou, e escopo a menos é o tipo de erro que aparece na hora — escopo a mais é o tipo que só aparece no incidente.
Chamar uma rota sem o escopo devido responde 403 FORBIDDEN. Para descobrir o que a sua chave tem, consulte GET /v1/me.
Limite de requisições
O limite é por chave, não por IP:
- Padrão: 60 requisições por minuto. O valor efetivo da sua chave vem em
apiKey.rateLimitPerMinnoGET /v1/me, e pode ser ajustado pelo administrador da conta. - Estourar responde
429comerror.code: RATE_LIMITEDe o headerretry-afterem segundos.
Contar por chave é o que torna o limite previsível para você: um servidor atrás de NAT não divide a cota com terceiros, e trocar de IP não zera nem multiplica nada.
js
async function chamar(url, opts) {
const res = await fetch(url, opts);
if (res.status === 429) {
const espera = Number(res.headers.get('retry-after') ?? 60);
await new Promise((r) => setTimeout(r, espera * 1000));
return chamar(url, opts); // uma retentativa após a janela
}
return res;
}Respeite o retry-after em vez de tentar de novo imediatamente — repetir na mesma janela só gasta o limite da janela seguinte.
Rotas caras
GET /forms/{id}/stats varre as respostas do período para agregar. Ela tem um teto próprio, mais apertado que o da chave. Use-a sob demanda (ao abrir um painel, num relatório diário), nunca em laço.
Formato da chave
edu_a1b2c3d4e5f60718_9f8e7d6c5b4a39281706f5e4d3c2b1a0…
└──── prefixo ───┘└──────────── segredo ───────────┘O prefixo é público: é o que aparece na lista de chaves do painel e o que permite identificar qual chave é qual. O segredo só existe na resposta da criação — guardamos apenas um hash dele, então nem nós conseguimos exibi-lo de novo.
Na prática, isso significa que o prefixo é seguro para colocar em log (chave a1b2c3d4e5f60718 falhou), e o token inteiro nunca é.
Ciclo de vida
Revogar é o caminho normal de desligar uma integração: a chave para de funcionar imediatamente e a linha continua na lista, com o histórico de uso. Excluir remove a chave da lista. Os dois barram a autenticação na hora.
expiresAt é opcional e útil para acessos temporários (uma consultoria, uma migração). Depois da data, a chave responde 401.
lastUsedAt mostra quando a chave foi usada pela última vez — bom para descobrir chaves esquecidas antes de removê-las. É atualizado com atraso de até 5 minutos.
Trocar uma chave sem downtime
Não há rotação automática. O procedimento seguro é:
- Crie a chave nova com os mesmos escopos
- Faça o deploy da sua aplicação com o token novo
- Confirme pelo
lastUsedAtda chave antiga que ela parou de ser usada - Revogue a antiga
Erros de autenticação
| Situação | Resposta |
|---|---|
| Header ausente ou malformado | 401 · Invalid API key |
| Chave inexistente ou segredo errado | 401 · Invalid API key |
| Chave revogada ou excluída | 401 · API key revoked |
Chave vencida (expiresAt) | 401 · API key expired |
| Chave válida, sem o escopo da rota | 403 · FORBIDDEN |
Formato inválido, chave inexistente e segredo errado dão a mesma resposta de propósito — distinguir permitiria descobrir quais prefixos existem por tentativa e erro.
Boas práticas
- Nunca no cliente. A chave é credencial de servidor. Em código de browser ou app móvel, ela é pública na prática — qualquer pessoa a extrai do bundle.
- Uma chave por integração, não uma compartilhada. É o que permite revogar uma sem derrubar as outras, e o que faz o
lastUsedAtsignificar alguma coisa. - No cofre de segredos, não no repositório. Se um token for para o Git, revogue-o: o histórico do Git não esquece.
- Trate
401como fatal, não como transitório. Repetir com uma chave revogada nunca vai funcionar; alerte alguém em vez de entrar em laço.