OAuth
Deixe as pessoas entrarem no seu cliente com a conta Atli delas, e aja em nome delas, com OAuth 2.1 padrão: descoberta, PKCE, refresh tokens que se renovam a cada uso e revogação. Qualquer cliente pode usar: um app, um bot, um site, um servidor ou um assistente de IA.
Quando usar
Assistentes de IA são um desses clientes. Ao se conectar ao servidor MCP da Atli, eles fazem o login das pessoas desse jeito por conta própria, então não há nada a construir para eles. O mesmo fluxo serve a todo cliente.
Qualquer cliente que age por uma pessoa: um app de celular, um site, um bot ou um servidor que mostra a alguém os eSIMs dela ou compra um para ela, com a própria conta Atli. Registre um cliente seu, e os tokens dele funcionam na API da Atli.
Quem entra por um cliente que é seu, ou pelo seu endereço de parceiro do servidor MCP, pode ser vinculado a você como sua indicação. Isso vem desativado por padrão, e a Atli pode ativar para a sua conta. De qualquer forma, seu cliente pode ele mesmo dar a você o crédito pelas pessoas que traz: veja “Receba o crédito pelas pessoas que você traz”, mais abaixo.
Só pode se conectar uma conta na qual a pessoa consiga entrar de novo em outro lugar: uma com email, inclusive via Google, Apple ou Facebook, ou com Telegram ou WhatsApp.
Como funciona
A API da Atli é o servidor de autorização. O login e a tela de consentimento ficam em heyatli.com, em /connect, então seu app nunca vê uma senha. Leia cada endpoint dos metadados em vez de escrevê-los no código. Os endereços aqui acompanham o seletor de Sandbox e Produção.
Registre um cliente uma vez. Depois, para cada pessoa: envie-a ao endpoint de autorização com um challenge PKCE, pegue o código com que ela volta, troque-o por tokens e chame a API com o access token. Renove-o quando expirar e revogue-o quando a pessoa sair.
curl https://api.heyatli.com/.well-known/oauth-authorization-server
{
"issuer": "https://api.heyatli.com",
"authorization_endpoint": "https://api.heyatli.com/oauth2/authorize",
"token_endpoint": "https://api.heyatli.com/oauth2/token",
"revocation_endpoint": "https://api.heyatli.com/oauth2/revoke",
"registration_endpoint": "https://api.heyatli.com/oauth2/register",
"jwks_uri": "https://api.heyatli.com/oauth2/jwks",
"scopes_supported": ["api", "offline_access"],
"code_challenge_methods_supported": ["S256"],
"client_id_metadata_document_supported": true,
…
}Para que serve um token
Toda solicitação informa o endereço para o qual o token se destina, como resource (RFC 8707), e o token só funciona ali. Um endereço que não seja um destes, ou um código de indicação que não existe, é recusado com invalid_target, então nenhum dos dois pode ser adivinhado.
| Endereço | Quem pode pedir | O que o token faz |
|---|---|---|
A API da Atli https://api.heyatli.com | Só um cliente que é seu | Um bearer token para a API, que age como a pessoa, igual a um de |
O servidor MCP da Atli https://mcp.heyatli.com | Qualquer cliente | Permite que um assistente aja em nome da pessoa pelas ferramentas MCP. |
Seu endereço de parceiro https://mcp.heyatli.com/r/YOUR_CODE | Qualquer cliente | O mesmo, e a pessoa pode ser vinculada a você como sua indicação, depois que isso for ativado para a sua conta. |
Registrar um cliente
Há três formas. Para o seu próprio app, registre um cliente seu: é o único tipo que pode pedir acesso à API da Atli.
Um cliente seu
Com login na sua conta,
POST /user/oauth-clients. Você pode ter 10. Escolhapublicpara tudo que roda no dispositivo de outra pessoa, como um app de celular ou uma página no navegador dela, econfidentialpara um servidor que consegue guardar um segredo.O
client_secretde um cliente confidencial está nesta resposta e em nenhum outro lugar. Se você perdê-lo, substitua-o comPOST /user/oauth-clients/{id}/rotate-secret. Sua conta precisa de um email, porque um cliente precisa de um código de indicação; um é criado para você se ainda não tiver.curl https://api.heyatli.com/user/oauth-clients \ -H 'Authorization: Bearer YOUR_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "name": "Travel Bot", "redirect_uris": ["https://bot.example/oauth/callback"], "client_type": "confidential" }' { "client_id": "atli_client_7f3a9c2e…", "client_secret": "…", "client_type": "confidential", … }Registro dinâmico
POST /oauth2/register(RFC 7591), sem login. É assim que a maioria dos assistentes se registra. Permite 10 registros por hora a partir de um mesmo endereço, e um cliente ao qual ninguém nunca se conecta é excluído após 30 dias.curl https://api.heyatli.com/oauth2/register \ -H 'Content-Type: application/json' \ -d '{ "client_name": "Travel Bot", "redirect_uris": ["https://bot.example/oauth/callback"] }' { "client_id": "atli_client_…", "token_endpoint_auth_method": "none", … }Um documento de metadados de client ID
Use como
client_idum endereçohttpshospedado por você, que sirva um documento JSON descrevendo o cliente. A Atli o lê quando alguém entra, então não há nada a registrar. A especificação do MCP prefere isso ao registro dinâmico.// Served at https://bot.example/oauth/client.json { "client_id": "https://bot.example/oauth/client.json", "client_name": "Travel Bot", "redirect_uris": ["https://bot.example/oauth/callback"], "token_endpoint_auth_method": "none" }
As URIs de redirecionamento são comparadas exatamente. Cada uma é
https, ouhttpemlocalhost,127.0.0.1ou[::1], onde só a porta pode variar, sem fragmento. Um cliente pode ter até 10.
Fazer o login de uma pessoa
O fluxo de código de autorização com PKCE (S256), que todo cliente usa, público ou confidencial.
Gere um par PKCE
Guarde o verifier para a troca do código e envie o challenge SHA-256 dele com a solicitação. Gere um par novo, e um
statenovo, a cada login.const base64url = (bytes) => btoa(String.fromCharCode(...bytes)) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); const verifier = base64url(crypto.getRandomValues(new Uint8Array(32))); const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)); const challenge = base64url(new Uint8Array(digest)); const state = base64url(crypto.getRandomValues(new Uint8Array(16)));Mande a pessoa entrar
Abra o endpoint de autorização no navegador dela.
scopeéapi, eoffline_accessacrescenta um refresh token. A pessoa entra e aceita em heyatli.com/connect, que mostra o nome do seu cliente, para onde ele a manda de volta e se ela viraria indicação de alguém. A solicitação vale por 10 minutos.const url = new URL('https://api.heyatli.com/oauth2/authorize'); url.search = new URLSearchParams({ response_type: 'code', client_id: CLIENT_ID, redirect_uri: 'https://bot.example/oauth/callback', scope: 'api offline_access', state, code_challenge: challenge, code_challenge_method: 'S256', // Atli's API, for a client you own; or the MCP server, https://mcp.heyatli.com resource: 'https://api.heyatli.com', }).toString(); window.location.assign(url);Pegue o código que ela traz
Ela volta à sua URI de redirecionamento com
code, o seustateeiss. Confira sestateé seu e seissé o emissor. Se ela recusou, vemerror=access_deniedno lugar.https://bot.example/oauth/callback?code=4bd81f…&state=…&iss=https%3A%2F%2Fapi.heyatli.com https://bot.example/oauth/callback?error=access_denied&state=…&iss=https%3A%2F%2Fapi.heyatli.comTroque o código por tokens
Envie o código com o verifier, o mesmo
redirect_urie o mesmoresource, e as credenciais do seu cliente, se ele tiver. Um código vale por 60 segundos e funciona uma vez: enviá-lo duas vezes, ou com o verifier errado, encerra o acesso da pessoa, pois outra pessoa pode estar com ele.O access token vale por
expires_insegundos, 15 minutos. Um refresh token sempre vem junto.curl https://api.heyatli.com/oauth2/token \ -u 'CLIENT_ID:CLIENT_SECRET' \ -d grant_type=authorization_code \ -d code=THE_CODE \ -d code_verifier=THE_VERIFIER \ -d redirect_uri=https://bot.example/oauth/callback \ -d client_id=CLIENT_ID \ -d resource=https://api.heyatli.com { "access_token": "…", "token_type": "Bearer", "expires_in": 900, "refresh_token": "…", "scope": "api offline_access" }
Manter a pessoa conectada
Quando o access token expirar, peça um novo com o refresh token. Você pode pedir um scope mais restrito, nunca um mais amplo.
O refresh token muda a cada uso, então guarde o novo toda vez. Se uma resposta nunca chegou até você, enviar o token antigo de novo por um curto período devolve a mesma resposta. Depois disso, o token antigo indica que ele foi copiado, e o acesso da pessoa termina.
curl https://api.heyatli.com/oauth2/token \
-u 'CLIENT_ID:CLIENT_SECRET' \
-d grant_type=refresh_token \
-d refresh_token=THE_REFRESH_TOKEN \
-d client_id=CLIENT_ID \
-d resource=https://api.heyatli.comUsar o token
Envie-o como Authorization: Bearer ACCESS_TOKEN, para o endereço para o qual foi emitido e para nenhum outro.
Um token para a API da Atli age como a pessoa em todas as rotas da API, como um token de /login. Trate-o como opaco: o conteúdo dele pode mudar.
Um token para um servidor MCP é um JWT assinado com EdDSA. O servidor o verifica com as chaves em /oauth2/jwks, pelo kid dele, e confere se o aud é o seu próprio endereço. Ele não traz email nem outros dados: só sub (o id da pessoa), scope, grant_id e os horários.
curl https://api.heyatli.com/esims \
-H 'Authorization: Bearer ACCESS_TOKEN'Receba o crédito pelas pessoas que você traz
Vincular a você as pessoas que entram pelo seu cliente, ou pelo seu endereço de parceiro, vem desativado por padrão, e a Atli pode ativar isso para a sua conta. Esteja ativado ou não, seu cliente pode ele mesmo vincular uma pessoa conectada ao seu código de indicação, com o access token dela: POST /user/referral/redeem com o seu code.
result diz o que aconteceu: ok vinculou a pessoa, already significa que ela já tem quem a indicou, notnew que ela já comprou antes, own que é o seu próprio código e notfound que o código não existe. Só pode ser vinculada uma pessoa sem indicação que ainda não comprou, e só uma vez.
curl https://api.heyatli.com/user/referral/redeem \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "code": "YOUR_CODE" }'
{
"success": true,
"result": "ok",
"message": "…",
"discount_percent": 10
}Sair e remover o acesso
Quando alguém sair do seu app, revogue o refresh token dessa pessoa. Isso encerra o acesso dela no seu cliente, com todos os tokens dele. A resposta é 200 mesmo para um token desconhecido, para que não dê para usá-lo para descobrir quais tokens existem.
As pessoas também podem ver os apps que conectaram e remover o acesso por conta própria (GET /user/oauth-grants, DELETE /user/oauth-grants/{id}). A partir daí seu cliente não consegue um token novo, então trate uma renovação recusada como logout.
curl https://api.heyatli.com/oauth2/revoke \
-u 'CLIENT_ID:CLIENT_SECRET' \
-d token=THE_REFRESH_TOKEN \
-d client_id=CLIENT_IDErros
Os endpoints de token, revogação e registro respondem com o corpo de erro próprio do OAuth, {"error": "invalid_grant", "error_description": "…"}, em vez do habitual da API. Decida com base em error, nunca na descrição. Toda resposta do endpoint de token traz Cache-Control: no-store.
invalid_grant: o código já foi usado ou expirou, o verifier está errado, ou um refresh token foi usado de novo depois do período de tolerância. Faça o login da pessoa novamente.invalid_target:resourcenão é um endereço ao qual um token pode se destinar, ou indica um código de indicação que não existe.invalid_client: as credenciais do cliente estão erradas ou faltando. Todos os tipos falham do mesmo jeito.invalid_scope: uma renovação pediu mais do que a pessoa permitiu.