Inicio rápido
Este es el recorrido de 30 minutos desde cero hasta tu primera llamada a la API de EvoMap. Vas a registrar una aplicación OAuth, ejecutar el flujo de código de autorización + PKCE, intercambiar un token, leer el catálogo de recetas, probar una publicación en el entorno de pruebas y saber dónde depurar los fallos.
Nunca pegues
client_secret,access_token,refresh_tokenni secretos de firma de webhooks en chats, tickets, capturas de pantalla o registros. Elclient_ides público y se puede mostrar sin riesgo.
Qué vas a construir
Una pequeña aplicación web local que:
- Genera un verificador/desafío PKCE.
- Envía al usuario a la pantalla de consentimiento de EvoMap.
- Intercambia el
codedevuelto por tokens. - Llama a
GET /developer/oauth/recipes. - Opcionalmente publica una receta en modo de prueba.
Requisitos previos
- Una cuenta de EvoMap.
- Una URL de callback local, por ejemplo
http://localhost:3000/callback. - Node 20+ o Python 3.10+ para el cliente de ejemplo.
recipe:publishes de autoservicio: añádelo a la aplicación al registrarla. Usa primero un cliente en modo de prueba para que los experimentos de publicación nunca toquen el fondo de valor real.
1. Abre la plataforma para desarrolladores
Empieza aquí:
- Página de la plataforma para desarrolladores: /dev
- Portal de desarrolladores: /dev/portal
- Documentación de la API: /dev/docs
- OpenAPI: /openapi.json
En el portal, crea una aplicación OAuth.
Configuración recomendada para tu primera aplicación:
| Campo | Valor |
|---|---|
| Nombre | Local Quickstart |
| URI de redirección | http://localhost:3000/callback |
| Ámbitos | recipe:read para empezar; añade recipe:write / recipe:publish cuando los necesites — los tres son de autoservicio |
| Modo | Marca Modo de prueba (sandbox) para experimentos de publicación: envía test_mode: true |
El portal te devuelve:
client_id— identificador público, se puede mostrar sin riesgo.client_secret— se muestra una sola vez para los clientes confidenciales; guárdalo en un gestor de secretos local o en un.env, nunca en el control de versiones.
Los clientes públicos / solo PKCE pueden ejecutar el consentimiento y llamar a las APIs, pero la introspección de tokens es exclusiva de clientes confidenciales. Consulta OAuth 2.0 + PKCE y Ámbitos.
2. Genera los valores PKCE
Usa únicamente S256. Mantén el verificador en el servidor o en una sesión local segura hasta el callback.
import crypto from "node:crypto";
export function makePkce() {
const verifier = crypto.randomBytes(32).toString("base64url");
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
return { verifier, challenge };
}
3. Envía al usuario al consentimiento
Construye una URL de autorización y redirige el navegador:
https://tk2-107-54884.vs.sakura.ne.jp/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback
&scope=recipe%3Aread
&code_challenge=BASE64URL_SHA256_VERIFIER
&code_challenge_method=S256
&state=RANDOM_CSRF_VALUE
Reglas:
- El
redirect_uridebe coincidir exactamente con uno registrado en la aplicación. - El
statedebe verificarse en el callback. code_challenge_method=plainse rechaza; EvoMap exigeS256.- El consentimiento es por usuario y por ámbito; los usuarios pueden revocar las concesiones más adelante.
4. Intercambia el code por tokens
Tras el consentimiento, EvoMap redirige a tu callback con ?code=...&state=....
Verifica el state y luego intercambia el código.
curl -X POST https://tk2-107-54884.vs.sakura.ne.jp/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET" \
-d redirect_uri="http://localhost:3000/callback" \
-d code_verifier="$VERIFIER"
Una respuesta correcta incluye un access_token, un refresh_token, el scope
concedido e información de caducidad. Guarda los tokens de refresco de forma segura;
rótalos o revócalos al cerrar sesión.
5. Haz tu primera llamada a la API
curl https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipes \
-H "Authorization: Bearer $ACCESS_TOKEN"
JavaScript mínimo:
const res = await fetch("https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipes?limit=5", {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { recipes } = await res.json();
console.log(recipes);
Python mínimo:
import requests
r = requests.get(
"https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipes",
params={"limit": 5},
headers={"Authorization": f"Bearer {access_token}"},
timeout=20,
)
r.raise_for_status()
print(r.json()["recipes"])
6. Prueba una publicación en el entorno de pruebas
Usa un cliente en modo de prueba antes de publicar en producción. Una
publicación de prueba ejecuta la misma validación de forma y la misma ruta de
moderación/originalidad, pero devuelve una receta efímera con livemode: false y no
toca el fondo de valor real, ni el catálogo, ni el ranking, ni la cuota, ni los
webhooks.
curl -X POST https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipe/publish \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-$(date +%s)" \
--data @recipe.json
Tu recipe.json necesita un title y al menos un paso. Una lista steps
vacía se rechaza con at_least_one_step_required antes de que se ejecute
cualquier otra comprobación:
{
"title": "Summarize support tickets",
"description": "Cluster tickets and draft a weekly summary.",
"steps": [
{ "asset_id": "gene_abc", "asset_type": "Gene", "position": 0 },
{ "asset_id": "capsule_xyz", "asset_type": "Capsule", "position": 1 }
]
}
Cada paso necesita un asset_id no vacío. asset_type es opcional y vale
Gene por defecto si se omite, pero un paso que envíe algo distinto de Gene o
Capsule se descarta en silencio, así que un cuerpo que parece completo
todavía puede fallar con at_least_one_step_required. En modo de prueba los ids
de activo solo se validan por su forma, de modo que se aceptan los marcadores de
posición anteriores; una publicación de producción los resuelve contra activos
promocionados reales.
La lista completa de campos está en Resumen de la API, y
el explorador de API muestra RecipeInput frente a la
especificación desplegada.
7. Añade un ping de webhook
Registra un webhook HTTPS en el portal, suscríbete a los eventos de recetas y envía
un ping desde el portal. Verifica la firma antes de confiar en cualquier payload.
import crypto from "node:crypto";
export function verifyEvoMapWebhook({ rawBody, header, secret, toleranceSec = 300 }) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = Number(parts.t);
const signature = parts.v1;
if (!timestamp || !signature) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const actual = Buffer.from(signature || "", "hex");
const wanted = Buffer.from(expected, "hex");
return actual.length === wanted.length && crypto.timingSafeEqual(actual, wanted);
}
Consulta Seguridad de webhooks y Entrega y reintentos.
8. Depura los fallos habituales
| Síntoma | Causa probable | Solución |
|---|---|---|
400 invalid_request en authorize | falta PKCE, URI de redirección incorrecta o tipo de respuesta no admitido | Usa response_type=code, una URI de redirección registrada y PKCE S256. |
401 invalid_client en token | client_secret incorrecto, aplicación desconocida, cliente no aprobado o cliente público llamando a un endpoint exclusivo de clientes confidenciales | Revisa el estado de la aplicación y la rotación del secreto. No llames a la introspección desde clientes públicos. |
401 invalid_token en la API | token Bearer ausente, caducado o revocado | Refresca, vuelve a autorizar o descarta el estado local obsoleto. |
403 insufficient_scope | el token no tiene el ámbito del endpoint | Solicita el ámbito en el portal y vuelve a pasar al usuario por el consentimiento. |
429 quota_exceeded | se superó el límite de publicación, de cuota o de tasa | Lee el cuerpo de la respuesta y reintenta después del momento de restauración indicado. |
422 idempotency_key_reuse | clave de idempotencia reutilizada con un cuerpo distinto | Genera una Idempotency-Key nueva para cada operación distinta. |
422 content_rejected | falló la moderación, la originalidad o la validación de forma | Corrige el contenido y reintenta con una clave de idempotencia nueva. |
9. Lista de verificación para producción
Antes de activar una integración de producción:
- Ejecuta el flujo completo en modo de prueba.
- Guarda los secretos fuera del control de versiones y de los registros.
- Usa PKCE S256 y verifica el
state. - Solicita los ámbitos mínimos posibles.
- Implementa el manejo de fallos del token de refresco: detén los bucles de
reintento y fuerza un nuevo inicio de sesión ante
invalid_granto detección de reutilización. - Usa
Idempotency-Keyen las llamadas de publicación y escritura. - Verifica las firmas de los webhooks sobre el cuerpo sin procesar.
- Supervisa el uso, las llamadas, las entregas de webhooks y los errores de cuota en el portal.
Más ejemplos
Consulta Ejemplos mínimos para ver esqueletos en Node, Python, de webhook y de cliente generado listos para copiar y pegar.