Saltar al contenido
Docs

Guía

Crear una aplicación OAuth

Permite que los usuarios conecten tu aplicación a su espacio de trabajo de Emailit con OAuth 2.1 y PKCE, con el registro del cliente, la actualización de tokens, la revocación y los errores.

Actualizado el 1 oct 2026

Emailit ejecuta un servidor de autorización OAuth 2.1 en https://api.emailit.com. Úsalo cuando crees una integración, como un CRM, una herramienta no-code o un cliente de IA, que actúe en nombre de usuarios de Emailit. Tus usuarios inician sesión y aprueban el acceso en el navegador, y tú obtienes tokens para los espacios de trabajo que elijan sin manejar nunca sus claves de API. Es el mismo flujo que usa el servidor MCP.

Cómo funciona

  1. Registra un cliente con el registro dinámico de clientes o aloja un documento de metadatos del ID de cliente.

  2. Envía al usuario a la URL de autorización con un desafío de código PKCE.

  3. El usuario inicia sesión en Emailit, elige los espacios de trabajo que puede usar tu aplicación y aprueba el permiso solicitado.

  4. Emailit redirige de vuelta a tu URI de redirección con un código de autorización de un solo uso.

  5. Canjea el código por un token de acceso de 15 minutos y un token de actualización.

  6. Llama a la API con el token de acceso y actualízalo cuando caduque.

Endpoints

Método Ruta Finalidad
GET /.well-known/oauth-authorization-server Metadatos del servidor de autorización (RFC 8414)
GET /.well-known/oauth-protected-resource Metadatos del recurso protegido para la API REST
GET /.well-known/oauth-protected-resource/mcp Metadatos del recurso protegido para el servidor MCP
POST /oauth/register Registro dinámico de clientes (RFC 7591)
GET /oauth/authorize Inicio de sesión y consentimiento en el navegador
POST /oauth/token Canjear un código o un token de actualización
POST /oauth/revoke Revocar una autorización con su token de actualización (RFC 7009)
GET /oauth/grants Listar autorizaciones (las del usuario que ha iniciado sesión, o las de un espacio de trabajo con una clave de API)
POST /oauth/grants/:id/revoke Revocar una autorización
PUT /oauth/grants/:id/workspaces Cambiar los espacios de trabajo que puede usar una autorización (el usuario que conectó la aplicación)

Todas las rutas están en https://api.emailit.com.

Descubrir el servidor

Terminal
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"]
}

Los clientes MCP empiezan, en cambio, por los metadatos del recurso protegido. Una petición sin autenticar a https://api.emailit.com/mcp devuelve 401 con WWW-Authenticate: Bearer resource_metadata="https://api.emailit.com/.well-known/oauth-protected-resource/mcp", y ese documento apunta al servidor de autorización:

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

Permisos

Permiso Concede
sending Enviar y reenviar emails, y reprogramar, cancelar y reintentar envíos. El mismo acceso que una clave de API Sending Only.
full Todos los endpoints de la API REST y todas las herramientas MCP. El mismo acceso que una clave de API Full Access.

full ya incluye todo lo que permite sending. Solicita sending si tu aplicación solo envía y full en los demás casos; también se acepta sending full, separado por un espacio. Si omites scope, Emailit usa todos los permisos que registró el cliente.

Registrar tu cliente

Puedes registrar un cliente de dos formas. Ambas funcionan con todos los flujos de esta página.

Registro dinámico de clientes

Envía una petición de registro. No hace falta autenticación, y cada dirección IP puede registrar hasta 20 clientes por hora.

Terminal
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 Obligatorio Descripción
client_name Sí Se muestra en la página de consentimiento. Hasta 200 caracteres.
redirect_uris Sí De 1 a 10 URI de redirección. Consulta Reglas de las URI de redirección.
grant_types No Debe incluir authorization_code; puede incluir refresh_token. Por defecto, ambos.
response_types No Solo code.
scope No Permisos separados por espacios. Por defecto, sending full.
token_endpoint_auth_method No none (por defecto) para clientes públicos, como las aplicaciones de escritorio, móviles y de navegador; client_secret_basic o client_secret_post para las aplicaciones de servidor.
client_uri No La página de inicio de tu aplicación.
logo_uri No El logotipo que se muestra en la página de consentimiento.

La respuesta 201 repite los metadatos y añade un client_id. Los clientes confidenciales también reciben un client_secret, que se muestra una sola 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 siempre es 0: los secretos no caducan. Todos los clientes, públicos o confidenciales, deben usar PKCE.

Documento de metadatos del ID de cliente

Si tu aplicación puede alojar un archivo JSON estático, puedes saltarte el registro. Publica un documento en una URL HTTPS y usa esa URL como client_id:

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"
}
  • El client_id del documento debe ser exactamente igual a la URL del documento.
  • token_endpoint_auth_method debe ser none. Son clientes públicos que dependen de PKCE.
  • Las URI de redirección HTTPS deben estar en el mismo host que el documento.
  • Emailit descarga el documento durante la autorización, con un tiempo de espera de 5 segundos y sin seguir redirecciones, y lo guarda en caché durante 5 minutos.
  • La página de consentimiento muestra el host del documento, por ejemplo crm.acme.com, en lugar de client_name.

Los clientes de IA usan este método: ChatGPT, Claude y Grok se identifican con sus propios documentos de metadatos del ID de cliente, así que se conectan sin registrarse antes. Para Grok, Emailit también acepta redirecciones a console.x.ai.

Reglas de las URI de redirección

  • Se permiten las URI https://.
  • http:// solo se permite para hosts de loopback: 127.0.0.1, localhost y [::1]. En las URI de loopback, el puerto puede ser distinto en el momento de la autorización, pero el host, la ruta y la consulta deben coincidir. localhost y 127.0.0.1 son hosts distintos.
  • Los esquemas de uso privado, como cursor://, vscode:// o com.acme.crm://, se permiten para las aplicaciones nativas.
  • Se rechazan las URI file, ftp, data, javascript, blob, about y vbscript, y cualquier URI con un #fragment.
  • Salvo los puertos de loopback, la redirect_uri que envíes debe coincidir exactamente con una registrada.

Enviar al usuario a Emailit

Crea un verificador y un desafío PKCE, y un state aleatorio, para cada autorización:

JavaScript
import { createHash, randomBytes } from 'node:crypto';

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.

Después, redirige el navegador del usuario al endpoint de autorización:

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 Obligatorio Descripción
response_type Sí code
client_id Sí Tu ID de cliente o la URL de tu documento de metadatos.
redirect_uri Sí Una de tus URI de redirección registradas.
code_challenge Sí El SHA-256 del verificador de código, codificado en Base64url.
code_challenge_method Recomendado S256. plain no se admite.
scope Recomendado sending o full. Debe ser un permiso que haya registrado el cliente.
state Recomendado Un valor aleatorio que compruebas en el callback.
resource No El recurso al que quieres llamar (RFC 8707): https://api.emailit.com para la API REST o https://api.emailit.com/mcp para MCP. Los tokens funcionan en ambos de todos modos.

Abre esta URL en el navegador del usuario. No la solicites desde tu backend.

Qué ve el usuario

  1. Inicia sesión en Emailit. El usuario introduce su email y su contraseña. Si usa una app de autenticación para la autenticación en dos pasos, a continuación introduce su código o un código de recuperación.
  2. Elige los espacios de trabajo. La página muestra tu logotipo, el nombre de tu cliente (o el host de tu documento de metadatos) y los permisos que solicitaste. El usuario elige All my workspaces, que incluye los espacios de trabajo que cree o a los que se una más adelante, o Only these workspaces con los que marque, y elige en cuál empieza tu aplicación.
  3. Permite el acceso. Selecciona Allow access o Deny.

Una autorización puede cubrir varios espacios de trabajo. El usuario puede cambiar la lista más adelante en Account > Connected apps en el panel, y tu aplicación ve el cambio en su siguiente petición.

Al aprobar, Emailit redirige a tu URI de redirección:

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

Comprueba que state coincide con el valor que guardaste y que iss es https://api.emailit.com. El código es válido durante 10 minutos y solo se puede usar una vez. Si el usuario selecciona Deny, la redirección lleva en su lugar error=access_denied.

Canjear el código por tokens

Envía un POST al endpoint de tokens como application/x-www-form-urlencoded (también se acepta JSON). Envía la misma redirect_uri y el verificador de código original:

Terminal
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"

Con client_secret_basic, envía el ID y el secreto del cliente en la cabecera Authorization: Basic. Con client_secret_post, envía en su lugar client_id y client_secret como campos del formulario. No envíes el secreto de las dos formas.

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

El token de acceso dura 15 minutos (expires_in está en segundos). Trátalo como una cadena opaca: no lo analices ni dependas de su contenido.

Llamar a la API

Usa el token de acceso como token bearer en la API REST y en el servidor MCP, exactamente igual que una clave de API:

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

Las peticiones REST se ejecutan en el espacio de trabajo por defecto de la autorización: el que el usuario eligió para empezar o el que haya elegido después. Las herramientas MCP también pueden actuar sobre otro espacio de trabajo permitido con su argumento workspace. Cada petición usa el permiso concedido y el rol del usuario en el espacio de trabajo, así que una petición fuera del permiso, o una ruta reservada a administradores a la que llama un miembro del espacio de trabajo, devuelve 403. Si el usuario abandona un espacio de trabajo, la autorización también lo pierde.

Actualizar los tokens

Antes de que caduque el token de acceso, o cuando una petición devuelva 401, obtén uno nuevo:

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"
  • Rotación. Cada actualización devuelve un refresh_token nuevo, válido durante 60 días. Guárdalo y descarta el anterior. Mientras actualices en un plazo de 60 días, la conexión no caduca.
  • Detección de reutilización. Si un token de actualización antiguo se vuelve a usar más de un minuto después de que se sustituyera, Emailit devuelve invalid_grant (Refresh token reuse detected) y revoca toda la autorización. Entonces el usuario tiene que volver a autorizar. Serializa las actualizaciones para que dos workers nunca usen el mismo token de actualización.
  • Permiso más limitado. Puedes pasar scope para obtener un token de acceso con menos permisos que la autorización. No puedes pedir más.

Revocar una autorización

Cuando un usuario desconecte Emailit de tu aplicación, revoca la autorización con su token de actualización:

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

El endpoint autentica a tu cliente igual que el endpoint de tokens y siempre devuelve 200 con el cuerpo vacío. Revocar el token de actualización revoca toda la autorización: sus tokens de acceso dejan de funcionar en la siguiente petición. Los tokens de acceso no se pueden revocar por separado; token_type_hint=access_token devuelve invalid_request.

Los usuarios también pueden revocar el acceso de tu aplicación desde su lado, en Connected apps en el panel, o con la API de autorizaciones que se describe a continuación.

Gestionar las autorizaciones

La API de autorizaciones lista y modifica las autorizaciones desde el lado del usuario. Acepta la sesión del panel del usuario o una clave de API Full Access:

Quién llama GET /oauth/grants POST /oauth/grants/:id/revoke PUT /oauth/grants/:id/workspaces
El usuario que conectó la aplicación Sus autorizaciones en todos los espacios de trabajo Revoca toda la autorización Cambia los espacios de trabajo de la autorización
Clave de API Full Access Las autorizaciones que incluyen el espacio de trabajo de la clave Quita el espacio de trabajo de la clave de la autorización; la autorización se revoca cuando no le queda ningún espacio de trabajo No permitido (403)

Cada autorización de la lista tiene esta forma:

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 cambiar los espacios de trabajo de una autorización, envía access (all o selected), workspace_ids para selected y un default_workspace_id opcional que debe ser uno de los espacios de trabajo permitidos:

Terminal
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" }'

El usuario debe ser miembro de todos los espacios de trabajo que seleccione. Una autorización revocada no se puede editar (409); la aplicación tiene que volver a conectarse.

Errores

Los endpoints OAuth devuelven los errores en el formato OAuth, no en el formato de errores de la API REST:

JSON
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired"
}
Error Código Cuándo
invalid_request 400 Falta un parámetro o no es válido, el cliente es desconocido en la autorización, la redirect_uri no está registrada, code_challenge_method no es S256 o se envió un secreto de cliente tanto en la cabecera como en el cuerpo.
invalid_client 401 El endpoint de tokens o de revocación no puede autenticar al cliente: cliente desconocido, o secreto ausente o incorrecto.
invalid_grant 400 El código no es válido, ya se usó o ha caducado; la redirect_uri o el verificador PKCE no coinciden; o el token de actualización no es válido, ha caducado, se revocó o se reutilizó.
invalid_scope 400 El permiso no se admite, no se registró para el cliente o, al actualizar, supera el de la autorización.
unsupported_response_type 400 response_type no es code.
unsupported_grant_type 400 grant_type no es authorization_code ni refresh_token.
access_denied Redirección El usuario seleccionó Deny.
too_many_requests 429 Más de 20 registros por hora desde una misma dirección IP.
server_error 500 Algo ha fallado en Emailit. Vuelve a intentarlo.

Hasta que se validan el client_id y la redirect_uri, los errores de autorización se muestran como JSON en el navegador y nunca se redirigen. Después, los errores se redirigen a tu callback (con error, error_description, state e iss) solo en las URI de redirección de loopback y de uso privado y en los clientes con documento de metadatos; los demás clientes reciben una página de error JSON. El Deny de un usuario siempre se redirige.

Lista de comprobación de seguridad

  • Guarda client_secret y los tokens de actualización en tu servidor, cifrados en reposo. No incluyas nunca un secreto de cliente en una aplicación móvil, de escritorio o de navegador; usa en su lugar un cliente público con PKCE.
  • Genera un state y un verificador de código nuevos para cada autorización, y comprueba state e iss en el callback.
  • Registra URI de redirección exactas. No uses redirectores abiertos como callbacks.
  • Guarda cada autorización con el espacio de trabajo al que pertenece y gestiona invalid_grant pidiendo al usuario que vuelva a conectarse.
  • Solicita sending si solo envías emails.
Autorizaciones, duración de los tokens y revocación.
Los endpoints a los que pueden llamar tus tokens.
Para tus propios scripts, las claves son más sencillas.
El flujo OAuth en la práctica.

¿Te ha resultado útil esta página?

Gracias por tu opinión.

Gracias. Leemos todos los mensajes.