# Criar um app OAuth

> Permita que os usuários conectem a sua aplicação ao workspace deles no Emailit com OAuth 2.1 e PKCE, incluindo registro do cliente, atualização de tokens, revogação e erros.

O Emailit mantém um servidor de autorização OAuth 2.1 em `https://api.emailit.com`. Use-o quando você criar uma integração, como um CRM, uma ferramenta no-code ou um cliente de IA, que age em nome de usuários do Emailit. Os seus usuários fazem login e aprovam o acesso no navegador, e você recebe tokens para os workspaces que eles escolherem sem nunca lidar com as chaves de API deles. É o mesmo fluxo que o [servidor MCP](/pt/docs/mcp/) usa.

## Como funciona

1. **Registre um cliente** com o registro dinâmico de clientes ou hospede um documento de metadados do client ID.

2. **Envie o usuário para a URL de autorização** com um code challenge PKCE.

3. **O usuário faz login no Emailit**, escolhe os workspaces que a sua aplicação pode usar e aprova o escopo solicitado.

4. **O Emailit redireciona de volta** para a sua URI de redirecionamento com um código de autorização de uso único.

5. **Troque o código** por um token de acesso de 15 minutos e um token de atualização.

6. **Chame a API** com o token de acesso e atualize-o quando ele expirar.

## Endpoints

| Método | Caminho | Finalidade |
| --- | --- | --- |
| `GET` | `/.well-known/oauth-authorization-server` | Metadados do servidor de autorização (RFC 8414) |
| `GET` | `/.well-known/oauth-protected-resource` | Metadados do recurso protegido para a API REST |
| `GET` | `/.well-known/oauth-protected-resource/mcp` | Metadados do recurso protegido para o servidor MCP |
| `POST` | `/oauth/register` | Registro dinâmico de clientes (RFC 7591) |
| `GET` | `/oauth/authorize` | Login e consentimento no navegador |
| `POST` | `/oauth/token` | Trocar um código ou um token de atualização |
| `POST` | `/oauth/revoke` | Revogar uma autorização com o token de atualização dela (RFC 7009) |
| `GET` | `/oauth/grants` | Listar autorizações (as do usuário conectado ou as de um workspace, com uma chave de API) |
| `POST` | `/oauth/grants/:id/revoke` | Revogar uma autorização |
| `PUT` | `/oauth/grants/:id/workspaces` | Alterar quais workspaces uma autorização pode usar (o usuário que conectou o app) |

Todos os caminhos ficam em `https://api.emailit.com`.

## Descobrir o servidor

```bash
curl https://api.emailit.com/.well-known/oauth-authorization-server
```

```json
{
  "issuer": "https://api.emailit.com",
  "authorization_endpoint": "https://api.emailit.com/oauth/authorize",
  "token_endpoint": "https://api.emailit.com/oauth/token",
  "registration_endpoint": "https://api.emailit.com/oauth/register",
  "revocation_endpoint": "https://api.emailit.com/oauth/revoke",
  "jwks_uri": "https://api.emailit.com/.well-known/jwks.json",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "response_types_supported": ["code"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"],
  "client_id_metadata_document_supported": true,
  "authorization_response_iss_parameter_supported": true,
  "scopes_supported": ["sending", "full"]
}
```

Os clientes MCP começam pelos metadados do recurso protegido. Uma requisição não autenticada para `https://api.emailit.com/mcp` retorna `401` com `WWW-Authenticate: Bearer resource_metadata="https://api.emailit.com/.well-known/oauth-protected-resource/mcp"`, e esse documento aponta para o servidor de autorização:

```json
{
  "resource": "https://api.emailit.com/mcp",
  "authorization_servers": ["https://api.emailit.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["sending", "full"]
}
```

## Escopos

| Escopo | Concede |
| --- | --- |
| `sending` | Enviar e encaminhar e-mails, além de reagendar, cancelar e tentar de novo os envios. O mesmo acesso de uma chave de API Sending Only. |
| `full` | Todos os endpoints da API REST e todas as ferramentas MCP. O mesmo acesso de uma chave de API Full Access. |

`full` já inclui tudo o que `sending` permite. Solicite `sending` se a sua aplicação só envia e `full` nos outros casos; `sending full`, separado por espaço, também é aceito. Se você omitir `scope`, o Emailit usa todos os escopos que o cliente registrou.

## Registrar o seu cliente

Você pode registrar um cliente de duas formas. As duas funcionam com todos os fluxos desta página.

### Registro dinâmico de clientes

Envie uma requisição de registro. Não é preciso autenticação, e cada endereço IP pode registrar até 20 clientes por hora.

```bash
curl https://api.emailit.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme CRM",
    "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "scope": "full",
    "token_endpoint_auth_method": "client_secret_basic",
    "client_uri": "https://crm.acme.com",
    "logo_uri": "https://crm.acme.com/logo.png"
  }'
```

| Campo | Obrigatório | Descrição |
| --- | --- | --- |
| `client_name` | Sim | Exibido na página de consentimento. Até 200 caracteres. |
| `redirect_uris` | Sim | De 1 a 10 URIs de redirecionamento. Consulte [Regras das URIs de redirecionamento](#redirect-uri-rules). |
| `grant_types` | Não | Deve incluir `authorization_code`; pode incluir `refresh_token`. O padrão são os dois. |
| `response_types` | Não | Apenas `code`. |
| `scope` | Não | Escopos separados por espaço. O padrão é `sending full`. |
| `token_endpoint_auth_method` | Não | `none` (padrão) para clientes públicos, como apps de desktop, mobile e de navegador; `client_secret_basic` ou `client_secret_post` para aplicações do lado do servidor. |
| `client_uri` | Não | A página inicial da sua aplicação. |
| `logo_uri` | Não | Logo exibido na página de consentimento. |

A resposta `201` repete os metadados e adiciona um `client_id`. Os clientes confidenciais também recebem um `client_secret`, exibido uma única vez:

```json
{
  "client_id": "3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73",
  "client_id_issued_at": 1790847000,
  "client_name": "Acme CRM",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "full",
  "token_endpoint_auth_method": "client_secret_basic",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "client_secret": "Ky7WI2z5HxoT6q1z19tuiIev_U1zX0_FKhIIfh0OBco",
  "client_secret_expires_at": 0
}
```

`client_secret_expires_at` é sempre `0`: os segredos não expiram. Todo cliente, público ou confidencial, deve usar PKCE.

### Documento de metadados do client ID

Se a sua aplicação pode hospedar um arquivo JSON estático, você pode pular o registro. Publique um documento em uma URL HTTPS e use essa URL como o seu `client_id`:

```json title="https://crm.acme.com/oauth/client.json"
{
  "client_id": "https://crm.acme.com/oauth/client.json",
  "client_name": "Acme CRM",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none",
  "scope": "full"
}
```

- O `client_id` do documento deve ser exatamente igual à URL do documento.
- `token_endpoint_auth_method` deve ser `none`. São clientes públicos que dependem de PKCE.
- As URIs de redirecionamento HTTPS devem estar no mesmo host do documento.
- O Emailit busca o documento durante a autorização, com um timeout de 5 segundos e sem seguir redirecionamentos, e o mantém em cache por 5 minutos.
- A página de consentimento mostra o host do documento, por exemplo, `crm.acme.com`, em vez de `client_name`.

Os clientes de IA usam este método: o ChatGPT, o Claude e o Grok se identificam com os próprios documentos de metadados do client ID, então se conectam sem se registrar antes. Para o Grok, o Emailit também aceita redirecionamentos para `console.x.ai`.

### Regras das URIs de redirecionamento

- URIs `https://` são permitidas.
- `http://` só é permitido para hosts de loopback: `127.0.0.1`, `localhost` e `[::1]`. Em URIs de loopback, a porta pode ser diferente no momento da autorização, mas o host, o caminho e a query devem corresponder. `localhost` e `127.0.0.1` são hosts diferentes.
- Esquemas de uso privado, como `cursor://`, `vscode://` ou `com.acme.crm://`, são permitidos para apps nativos.
- URIs `file`, `ftp`, `data`, `javascript`, `blob`, `about` e `vbscript`, e qualquer URI com um `#fragment`, são rejeitadas.
- Fora as portas de loopback, a `redirect_uri` que você envia deve ser exatamente igual a uma URI registrada.

## Enviar o usuário para o Emailit

Crie um verifier e um challenge PKCE, além de um `state` aleatório, para cada autorização:

```javascript

const codeVerifier = randomBytes(32).toString('base64url');
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
const state = randomBytes(16).toString('base64url');
// Store codeVerifier and state in the user's session.
```

Depois, redirecione o navegador do usuário para o endpoint de autorização:

```text
https://api.emailit.com/oauth/authorize
  ?response_type=code
  &client_id=3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73
  &redirect_uri=https%3A%2F%2Fcrm.acme.com%2Foauth%2Femailit%2Fcallback
  &scope=full
  &state=Jq3k9V0n2xR7bLm1
  &code_challenge=zsrvXVr2pbLDHbccAx_NY9osQ6nwqTnDZlIeFWKQOsg
  &code_challenge_method=S256
  &resource=https%3A%2F%2Fapi.emailit.com
```

| Parâmetro | Obrigatório | Descrição |
| --- | --- | --- |
| `response_type` | Sim | `code` |
| `client_id` | Sim | O seu client ID ou a URL do seu documento de metadados. |
| `redirect_uri` | Sim | Uma das suas URIs de redirecionamento registradas. |
| `code_challenge` | Sim | SHA-256 do code verifier, codificado em Base64url. |
| `code_challenge_method` | Recomendado | `S256`. `plain` não é aceito. |
| `scope` | Recomendado | `sending` ou `full`. Deve ser um escopo que o cliente registrou. |
| `state` | Recomendado | Um valor aleatório que você confere no callback. |
| `resource` | Não | O recurso que você quer chamar (RFC 8707): `https://api.emailit.com` para a API REST ou `https://api.emailit.com/mcp` para o MCP. Os tokens funcionam nos dois de qualquer forma. |

Abra essa URL no navegador do usuário. Não a busque a partir do seu backend.

## O que o usuário vê

1. **Login no Emailit.** O usuário digita o e-mail e a senha. Se ele usa um app autenticador para a autenticação de dois fatores, digita em seguida o código dele ou um código de recuperação.
2. **Escolha dos workspaces.** A página mostra o seu logo, o nome do seu cliente (ou o host do seu documento de metadados) e os escopos que você solicitou. O usuário escolhe **All my workspaces**, que inclui os workspaces que ele criar ou em que entrar depois, ou **Only these workspaces** com os que ele marcar, e escolhe por onde a sua aplicação começa.
3. **Permissão de acesso.** O usuário seleciona **Allow access** ou **Deny**.

Uma autorização pode cobrir vários workspaces. O usuário pode alterar a lista depois em **Account > Connected apps** no painel, e a sua aplicação vê a mudança na próxima requisição.

Na aprovação, o Emailit redireciona para a sua URI de redirecionamento:

```text
https://crm.acme.com/oauth/emailit/callback?code=XvRKyG5Pkplrd3xkRNaEayKkpkebEMdF&state=Jq3k9V0n2xR7bLm1&iss=https%3A%2F%2Fapi.emailit.com
```

Confira se `state` corresponde ao valor que você guardou e se `iss` é `https://api.emailit.com`. O código vale por 10 minutos e pode ser usado uma vez. Se o usuário selecionar **Deny**, o redirecionamento leva `error=access_denied`.

## Trocar o código por tokens

Faça um POST para o endpoint de token como `application/x-www-form-urlencoded` (JSON também é aceito). Envie a mesma `redirect_uri` e o code verifier original:

**Cliente confidencial**

```bash
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://crm.acme.com/oauth/emailit/callback \
  -d code_verifier="$CODE_VERIFIER"
```

  Com `client_secret_basic`, envie o client ID e o segredo no cabeçalho `Authorization: Basic`. Com `client_secret_post`, envie `client_id` e `client_secret` como campos do formulário. Não envie o segredo das duas formas.

**Cliente público**

```bash
curl https://api.emailit.com/oauth/token \
  -d grant_type=authorization_code \
  -d client_id="$CLIENT_ID" \
  -d code="$CODE" \
  -d redirect_uri=http://127.0.0.1:53682/callback \
  -d code_verifier="$CODE_VERIFIER"
```

  Os clientes públicos enviam `client_id` e nenhum segredo. O PKCE prova que o mesmo cliente iniciou o fluxo.

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9hdXRoLWhzMjU2In0…",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "XvRKyG5Pkplrd3xkRNaEayKkpkebEMdFn0h1VIuqixAOXybMtJfK8w",
  "scope": "full"
}
```

O token de acesso dura 15 minutos (`expires_in` é em segundos). Trate-o como uma string opaca: não o interprete nem dependa do conteúdo dele.

## Chamar a API

Use o token de acesso como bearer token na API REST e no servidor MCP, exatamente como uma chave de API:

```bash
curl https://api.emailit.com/v2/domains \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

As requisições REST são executadas no workspace padrão da autorização: aquele por onde o usuário escolheu começar ou para o qual ele mudou depois. As ferramentas MCP também podem usar outro workspace permitido com o argumento `workspace`. Toda requisição usa o escopo concedido e o papel do usuário no workspace, então uma requisição fora do escopo, ou uma rota exclusiva de Admin chamada por um membro com o papel Member, retorna `403`. Se o usuário sair de um workspace, a autorização também perde o acesso a ele.

## Atualizar tokens

Antes de o token de acesso expirar, ou quando uma requisição retornar `401`, obtenha um novo:

```bash
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"
```

- **Rotação.** Cada atualização retorna um novo `refresh_token`, válido por 60 dias. Guarde-o e descarte o antigo. Enquanto você atualizar dentro de 60 dias, a conexão não expira.
- **Detecção de reutilização.** Se um token de atualização antigo for usado de novo mais de um minuto depois de ter sido substituído, o Emailit retorna `invalid_grant` (`Refresh token reuse detected`) e revoga a autorização inteira. O usuário então precisa autorizar de novo. Serialize as atualizações para que dois workers nunca usem o mesmo token de atualização.
- **Escopo mais restrito.** Você pode passar `scope` para obter um token de acesso com menos escopos do que a autorização. Não é possível pedir mais.

## Revogar uma autorização

Quando um usuário desconectar o Emailit da sua aplicação, revogue a autorização com o token de atualização dela:

```bash
curl https://api.emailit.com/oauth/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"
```

O endpoint autentica o seu cliente da mesma forma que o endpoint de token e sempre retorna `200` com o corpo vazio. Revogar o token de atualização revoga a autorização inteira: os tokens de acesso dela deixam de funcionar na próxima requisição. Os tokens de acesso não podem ser revogados sozinhos; `token_type_hint=access_token` retorna `invalid_request`.

Os usuários também podem revogar o acesso da sua aplicação do lado deles, em [Apps conectados](/pt/docs/account/connected-apps/) no painel, ou com a API de autorizações abaixo.

## Gerenciar autorizações

A API de autorizações lista e altera autorizações do lado do usuário. Ela aceita a sessão do usuário no painel ou uma chave de API Full Access:

| Quem chama | `GET /oauth/grants` | `POST /oauth/grants/:id/revoke` | `PUT /oauth/grants/:id/workspaces` |
| --- | --- | --- | --- |
| O usuário que conectou o app | As autorizações dele em todos os workspaces | Revoga a autorização inteira | Altera os workspaces da autorização |
| Chave de API Full Access | As autorizações que incluem o workspace da chave | Remove o workspace da chave da autorização; a autorização é revogada quando não sobra nenhum workspace | Não permitido (`403`) |

Cada autorização da lista tem este formato:

```json
{
  "id": "9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b",
  "client_id": "https://chatgpt.com/oauth/client.json",
  "client": { "name": "ChatGPT", "uri": "https://chatgpt.com", "logo_uri": null },
  "default_workspace": { "id": "w3f9a1c2e", "name": "Acme" },
  "workspace_access": "selected",
  "workspaces": [{ "id": "w3f9a1c2e", "name": "Acme" }],
  "scopes": ["sending", "full"],
  "resource": "https://api.emailit.com/mcp",
  "created_at": "2026-10-01T09:30:12Z",
  "revoked_at": null,
  "revoked_reason": null
}
```

Para alterar os workspaces de uma autorização, envie `access` (`all` ou `selected`), `workspace_ids` para `selected` e um `default_workspace_id` opcional, que deve ser um dos workspaces permitidos:

```bash
curl -X PUT https://api.emailit.com/oauth/grants/9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b/workspaces \
  -H "Authorization: Bearer $DASHBOARD_SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "access": "selected", "workspace_ids": ["w3f9a1c2e"], "default_workspace_id": "w3f9a1c2e" }'
```

O usuário deve ser membro de todos os workspaces que selecionar. Uma autorização revogada não pode ser editada (`409`); o app precisa se conectar de novo.

## Erros

Os endpoints OAuth retornam erros no formato OAuth, não no [formato de erro da API REST](/pt/docs/api-reference/errors/):

```json
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired"
}
```

| Erro | Status | Quando |
| --- | --- | --- |
| `invalid_request` | 400 | Um parâmetro está ausente ou é inválido, o cliente é desconhecido na autorização, a `redirect_uri` não está registrada, `code_challenge_method` não é `S256` ou um segredo do cliente foi enviado tanto no cabeçalho quanto no corpo. |
| `invalid_client` | 401 | O endpoint de token ou de revogação não consegue autenticar o cliente: cliente desconhecido, segredo ausente ou errado. |
| `invalid_grant` | 400 | O código é inválido, já foi usado ou expirou; a `redirect_uri` ou o verifier PKCE não corresponde; ou o token de atualização é inválido, expirou, foi revogado ou reutilizado. |
| `invalid_scope` | 400 | O escopo não é aceito, não foi registrado para o cliente ou excede a autorização na atualização. |
| `unsupported_response_type` | 400 | `response_type` não é `code`. |
| `unsupported_grant_type` | 400 | `grant_type` não é `authorization_code` nem `refresh_token`. |
| `access_denied` | Redirecionamento | O usuário selecionou **Deny**. |
| `too_many_requests` | 429 | Mais de 20 registros por hora a partir de um endereço IP. |
| `server_error` | 500 | Algo deu errado do lado do Emailit. Tente de novo. |

Até que `client_id` e `redirect_uri` sejam validados, os erros de autorização são exibidos como JSON no navegador e nunca redirecionados. Depois disso, os erros são redirecionados para o seu callback (com `error`, `error_description`, `state` e `iss`) apenas para URIs de redirecionamento de loopback e de uso privado e para clientes com documento de metadados; os outros clientes recebem uma página de erro em JSON. O **Deny** de um usuário é sempre redirecionado.

## Checklist de segurança

- Mantenha o `client_secret` e os tokens de atualização no seu servidor, criptografados em repouso. Nunca inclua um segredo de cliente em um app mobile, de desktop ou de navegador; use um cliente público com PKCE.
- Gere um novo `state` e um novo code verifier para cada autorização e confira `state` e `iss` no callback.
- Registre URIs de redirecionamento exatas. Não use redirecionadores abertos como callbacks.
- Guarde cada autorização junto com o workspace a que ela pertence e trate `invalid_grant` pedindo ao usuário que se conecte de novo.
- Solicite `sending` se você só envia e-mails.

## Veja também

  - [Workspaces e permissões](/pt/docs/mcp/workspaces-and-permissions/): Autorizações, duração dos tokens e revogação.
  - [Referência da API](/pt/docs/api-reference/): Os endpoints que os seus tokens podem chamar.
  - [Chaves de API](/pt/docs/developers/api-keys/): Para os seus próprios scripts, as chaves são mais simples.
  - [Servidor MCP](/pt/docs/mcp/): O fluxo OAuth na prática.

---
Fonte: https://emailit.com/pt/docs/developers/oauth-apps/
