Saltar al contenido principal

Autenticación

Cada solicitud lleva un token de tipo bearer:

Authorization: Bearer rw_...

Hay dos tipos de quien llama, y a propósito no son lo mismo.

Claves de API​

Una clave pertenece a un espacio de trabajo, no a una persona. Lleva permisos y contiene solo lo que se le dio.

Tenga la clave lo que tenga, ninguna clave puede:

  • listar, crear, cambiar, mostrar de nuevo, revocar o eliminar claves de API, ni rotar ni mostrar de nuevo el secreto de firma del webhook,
  • leer, guardar ni eliminar las claves del espacio de trabajo para los servicios de IA,
  • configurar ni eliminar las credenciales del conector de plataforma de un canal (una clave las lee enmascaradas con Channels: read y las prueba con Channels: write),
  • enviar un evento de prueba desde la herramienta de prueba de un feed propio (POST /v1/streams/{id}/test): una clave envía eventos a través de POST /v1/stream, con Custom feeds - Send raw data,
  • cambiar el plan, comprar, conservar o devolver cuota, dividir el almacenamiento, ni leer y escribir la dirección de facturación, las facturas y la comisión de afiliado,
  • cambiar el nombre del espacio de trabajo, elegir sus sectores ni abandonarlo,
  • iniciar sesión por nadie ni actuar sobre sesiones,
  • acceder al lado de la plataforma ni a ningún espacio de trabajo que no sea el suyo.

Los secretos y el dinero no son permisos que se puedan marcar. Una credencial que puede crear otra credencial o cambiar lo que paga un espacio de trabajo es una que puede abrir una puerta o costar dinero real, y ambas decisiones corresponden a alguien que inició sesión y a quien se puede nombrar en el registro de auditoría. Ni siquiera se ofrecen en el selector.

Las personas son la única excepción, y merece una segunda mirada. team:write permite a una clave invitar a colegas, cambiar lo que puede hacer cada uno y eliminarlos, de modo que un espacio de trabajo configurado por un asistente pueda añadir a sus colegas de la misma manera. A un colega invitado se le puede dar el permiso de Administración, que gestiona las claves de API, así que una clave con team:write es una vía de entrada al espacio de trabajo y no solo una forma de leerlo. Márquelo solo para una clave cuya tarea sea configurar espacios de trabajo. Por la misma razón, la conexión de un asistente de IA nunca lo tiene: consulte La conexión de un asistente.

Todo lo demás en esas rutas no pertenece a ningún área de permisos, y una solicitud de una clave a una de ellas se rechaza en lugar de permitirse por omisión: olvidar clasificar un endpoint falla por el lado cerrado, que es la única dirección segura.

Crear una clave​

Una clave solo la puede crear una persona con sesión iniciada que tenga derecho a gestionar credenciales, es decir, el propietario y cualquiera que tenga el permiso de Administración (admin entre sus powers). Por eso la siguiente llamada usa un token de sesión.

curl http://localhost:4000/v1/keys \
-H "Authorization: Bearer rws_your_session_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Docs examples",
"permissions": ["ingest:write", "posts:read", "media:write"],
"expiresAt": "2026-12-31T23:00:00Z"
}'
{
"key": {
"id": "01M2SRXSGEPAJ7VY8W8M3EXHT0",
"name": "Docs examples",
"hint": "rw_0...0000",
"permissions": ["ingest:write", "posts:read", "media:write"],
"expiresAt": "2026-12-31T23:00:00.000Z",
"remindExpiry": false
},
"secret": "rw_0000000000000000000000000000000000"
}

201, con secret dentro. Cada solicitud se comprueba solo con el hash de la clave. La clave también se conserva cifrada, para que una persona que puede crear claves pueda verla de nuevo detrás de su propia contraseña (consulte Mostrar una clave de nuevo), pero ningún script puede pedirla: guárdela en su gestor de secretos en el momento de crearla. Al propietario y a quien creó la clave se les envía un correo de que existe; el correo nombra la clave y nunca lleva su secreto.

name tiene hasta 80 caracteres y por defecto es «API key». permissions necesita al menos uno, y cada entrada se comprueba con el catálogo: una cadena desconocida parecería un permiso en la pantalla y no concedería nada, que es la peor clase de error. GET /v1/keys/permissions sirve ese catálogo, con preajustes listos, de modo que un cliente nunca fija la lista en su código. expiresAt es opcional: un instante futuro (ISO 8601, con su desfase), o null u omitido para una clave que nunca caduca. remindExpiry: true envía un correo al propietario y a todos los que tienen Administración una semana y un día antes de que caduque; necesita un expiresAt, y PATCH /v1/keys/{keyId} lo cambia como el resto.

Un espacio de trabajo tiene un número limitado de claves que aún funcionan, 25 a menos que Reelwire lo haya ampliado a petición. Pasado ese límite, crear una es un 409 conflict que lo dice; una clave revocada o caducada no cuenta, así que revocar una deja sitio. La conexión de un asistente de IA también es una clave y cuenta igual. Un espacio de trabajo cuya suscripción se canceló no crea claves nuevas (409) hasta que vuelva a suscribirse, en cualquier plan, Free incluido.

Dos permisos solo los concede quien tiene lo que abren: approvals:write una persona con el permiso de Aprobaciones y plan:read el propietario. Pedir cualquiera de ellos de otra forma es un 403 que lo nombra, en POST /v1/keys y en PATCH /v1/keys/{keyId}, donde una clave conserva uno que ya tiene. GET /v1/keys/permissions los marca con withheld ("read" o "write") y withheldWhy, y los deja fuera de los preajustes.

No puede traer su propio secreto. De un valor elegido en otra parte Reelwire no puede garantizar nada, llega en el cuerpo de una solicitud y en los logs por el camino, y la elección de un cliente podría chocar con la de otro.

Listar y revocar​

curl http://localhost:4000/v1/keys \
-H "Authorization: Bearer rws_your_session_token"
{
"keys": [
{
"id": "01M2SRXSGEPAJ7VY8W8M3EXHT0",
"name": "Docs examples",
"hint": "rw_0...0000",
"permissions": ["ingest:write", "posts:read", "media:write"],
"enabled": true,
"lastUsedAt": "2026-09-18T08:08:07.898Z",
"expiresAt": "2026-12-31T23:00:00.000Z",
"remindExpiry": false,
"expired": false,
"state": "ACTIVE",
"connection": false,
"revealable": true,
"createdAt": "2026-09-18T08:08:01.807Z"
}
]
}

revealable indica si la clave se conservó para poder mostrarse de nuevo: true para una clave creada desde que se conservan las claves, false para una anterior (de ella solo existe el hash) y para la conexión de un asistente.

POST /v1/keys/{keyId}/revoke detiene una clave de inmediato y responde { "revoked": true, "id": "..." }. La fila permanece en la lista con lastUsedAt, porque esa es la prueba que quería cuando la revocó. POST /v1/keys/{keyId}/remove elimina después la fila de una clave revocada o caducada y responde { "removed": true, "id": "..." }; su último uso se conserva en el registro de auditoría. Una clave que aún funciona es un 409: revóquela primero.

Una clave con expiresAt deja de funcionar en ese instante, exactamente como una revocada: una solicitud que la lleva recibe 401. Su fila también permanece, y state distingue ambas: REVOKED cuando alguien la desactivó, EXPIRED cuando se agotó su propio tiempo, ACTIVE en los demás casos. Una clave revocada o caducada no se puede cambiar ni recuperar (409); cree una nueva. connection: true marca la conexión de un asistente de IA por MCP, que renueva su propio token y no muestra caducidad: consulte más abajo La conexión de un asistente.

PATCH /v1/keys/{keyId} cambia el nombre de una clave, modifica lo que puede hacer, o establece, mueve o elimina su expiresAt (null para nunca) y su remindExpiry, sin entregar un secreto nuevo, y responde { "key": { ... } }. Rotar una clave significa tocar cada sistema que la tiene, así que restringir o ampliar una tiene que ser posible por sí solo.

Mostrar una clave de nuevo​

curl http://localhost:4000/v1/keys/01M2SRXSGEPAJ7VY8W8M3EXHT0/reveal \
-H "Authorization: Bearer rws_your_session_token" \
-H "Content-Type: application/json" \
-d '{"password":"..."}'
{
"key": { "id": "01M2SRXSGEPAJ7VY8W8M3EXHT0", "name": "Docs examples", "hint": "rw_0...0000" },
"secret": "rw_0000000000000000000000000000000000"
}

POST /v1/keys/{keyId}/reveal devuelve una clave a la persona que la pide, una vez que se comprueba la contraseña con la que inicia sesión. Requiere el mismo derecho que crear una clave, el del propietario o de Administración, y una sesión: una clave nunca puede leer otra clave. Una contraseña incorrecta es un 403 y no un 401, porque la sesión en sí está bien, y los intentos están sujetos al límite del inicio de sesión, 10 por minuto desde una dirección, de modo que aquí no se puede adivinar una contraseña más rápido que al iniciar sesión. Cada clave mostrada se escribe en el registro de auditoría del espacio de trabajo (key.reveal) con quién la pidió, porque una clave leída de nuevo es una clave que ahora puede estar en otro sitio.

Solo una clave que aún funciona y que se conservó: una clave revocada o caducada es un 409, y también una clave con revealable: false, de la que solo existe el hash. Cree una clave nueva, póngala donde se usa la antigua y luego revoque la antigua. El secreto de firma del webhook se muestra de nuevo de la misma manera, con POST /v1/webhook/secret/reveal, el mismo cuerpo y las mismas reglas: consulte Webhooks.

La conexión de un asistente​

Un asistente de IA que se conecta al servidor MCP de Reelwire iniciando sesión (OAuth) en lugar de con una clave pegada recibe una clave rw_ ordinaria propia. Está vinculada a ese único servidor MCP, que nombra resource en /v1/whoami, dura una hora y el asistente la renueva en el sitio, de modo que una conexión es una fila en GET /v1/keys, con connection: true y sin expiresAt (un expiresAt enviado para ella se ignora). Lo que puede hacer es el preajuste Read only o Run the whole workspace, el que la persona permitió, menos los dos permisos mencionados arriba que esta persona no puede dar a una clave. Una conexión autorizada a actuar nunca tiene Team: write ni Webhook: write, y tiene Posts - Approvals: write solo cuando la persona también lo activó. Esto se calcula de nuevo en cada renovación, de modo que quien pierde Aprobaciones se lo quita a su asistente en la hora siguiente. Conectar requiere el mismo derecho que crear una clave a mano. Revocar la fila termina la conexión, y eliminarla después la termina definitivamente. Para la página de consentimiento, consulte Conectar a su asistente.

Su token solo funciona a través del servidor MCP de Reelwire: enviado directamente a la API, desde cualquier lugar fuera de Reelwire, se rechaza con un 401 que lo dice, lo que evita que un token copiado de un asistente sea una llave de toda la API. Un token de renovación presentado por segunda vez, un minuto o más después de usarse, termina la conexión, y también un código de inicio de sesión canjeado dos veces: uno de los dos que lo tienen no es el asistente. Un segundo uso dentro de ese minuto solo se rechaza, porque un asistente que perdió la respuesta a su propia renovación y vuelve a preguntar es mucho más común que un ladrón tan rápido.

El inicio de sesión de Reelwire sigue los estándares que espera un cliente corriente, así que no hay que enseñarle nada sobre Reelwire: el código de autorización con PKCE (solo S256), respuestas que nombran al servidor (iss, RFC 9207), la dirección de loopback propia de un asistente aceptada en cualquier puerto, el esquema de dirección propio de una aplicación aceptado (como el cursor:// al que recurre un editor cuando su puerto local está ocupado), y un asistente que se nombra con la dirección de un documento sobre sí mismo (un Client ID Metadata Document) aceptado en lugar de registrarse.

Qué clave tengo​

GET /v1/whoami es la única llamada que puede hacer toda clave, sean cuales sean sus permisos. Rechazarla significaría que una clave no podría comprobar sus propios permisos, que es lo primero que hace un cliente bien escrito. /v1/health, /v1/ready, /v1/version, /v1/_meta y /v1/_routes no necesitan ningún token, y una clave enviada a ellos tampoco se rechaza. /v1/version responde { "version": "...", "commit": "..." }, la versión y el commit que ejecuta el sistema; /v1/_meta lleva las mismas dos junto con contractSha, apiVersion, startedAt y catalogueVersion; /v1/_routes lista cada ruta con el permiso que necesita una clave para ella, y de ahí se genera Todos los endpoints.

curl http://localhost:4000/v1/whoami \
-H "Authorization: Bearer rw_your_key_here"
{
"accountId": "01M1VFRWE3A35H892VAAEKKDA6",
"accountName": "Enterprise 2",
"scopes": ["ingest:write", "posts:read", "media:write"],
"permissions": [
{ "scope": "ingest:write", "label": "Custom feeds - Send raw data: write" },
{ "scope": "posts:read", "label": "Posts: read" },
{ "scope": "media:write", "label": "Media: write" }
],
"kind": "apikey",
"resource": null
}

kind es apikey o session. permissions, para una clave, indica cada permiso que tiene con las palabras que usan el selector de claves y todos los rechazos. resource nombra el servidor MCP para el que se emitió una clave cuando la creó la conexión de un asistente de IA, y es null para una clave creada a mano.

Sesiones​

Una persona con sesión iniciada recibe un token de sesión, rws_..., que es lo que usan los paneles y lo que necesitan las pocas rutas solo para personas. Una sesión lleva lo que permite el rol de esa persona en lugar de permisos por área, y nunca incluye el envío de datos sin procesar (ingest:write): eso es tarea de una clave, de modo que una sesión robada del panel no puede introducir datos y una clave de productor filtrada no lee nada que no se le haya dado.

curl http://localhost:4000/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"...","takeOver":true}'
{
"status": "signed-in",
"token": "rws_...",
"expiresAt": "2026-09-18T19:44:11.500Z",
"expiresAtEpoch": 1789760651,
"verified": true,
"user": {
"id": "01M1VFRWEN9BM7GW2YVJNMZAZK",
"email": "you@example.com",
"displayName": "Alex Fischer",
"optionalMail": true,
"role": "OWNER",
"scope": "TENANT",
"powers": ["operate", "approve", "admin"],
"tenantId": "01M1VFRWE3A35H892VAAEKKDA6"
}
}

Reelwire permite una sesión activa por persona. Iniciar sesión en otro sitio termina la primera, y a la primera se le dice por qué. takeOver: true es la forma de decir que lo pretende. Sin él, mientras la otra sesión se haya usado en los últimos 15 minutos, la respuesta es una pregunta y no una sesión: { "status": "in-use", "lastSeenAt": "...", "minutes": 15 }, y ningún token hasta que envíe de nuevo el mismo inicio de sesión con takeOver: true. Por eso un token de sesión es lo que no hay que poner en un script: dos scripts que comparten una cuenta se cierran la sesión por turnos. Use una clave.

En reelwire.io, iniciar sesión, registrarse y pedir el restablecimiento de la contraseña también son para un navegador: cada uno lleva un token de reCAPTCHA que genera la página, y una solicitud sin él se rechaza con un 403 «We could not verify that this came from a browser». Una razón más para que un script use una clave.

Una dirección o contraseña incorrecta es un 401, «Invalid email or password», igual para ambas a propósito.

Con la verificación en dos pasos activada (cada persona la activa en Mi cuenta), una contraseña correcta da un ticket en lugar de una sesión: { "status": "two-factor", "ticket": "...", "expiresAt": "...", "minutes": 5 }. Envíe el ticket con el código que muestra la aplicación de autenticación de la persona, o uno de sus códigos de recuperación, a POST /v1/auth/login/two-factor, con takeOver como arriba; la respuesta es la sesión, o la pregunta «en uso», formulada antes de que se gaste el código:

curl http://localhost:4000/v1/auth/login/two-factor \
-H "Content-Type: application/json" \
-d '{"ticket":"...","code":"123456","takeOver":true}'

El ticket funciona cinco minutos y deja de funcionar si la contraseña cambia entretanto. Un código incorrecto es un 401; diez códigos incorrectos en un cuarto de hora hacen que esa persona espere quince minutos. Un código se acepta una sola vez. Activar y desactivar la verificación en dos pasos y crear nuevos códigos de recuperación corresponde solo a una persona con sesión iniciada: POST /v1/auth/two-factor/setup, /enable, /disable y /recovery-codes.

GET /v1/auth/session describe la sesión que tiene, incluido el plan y sus funciones. Una sesión dura 12 horas y termina antes tras una hora en la que nadie hizo nada: POST /v1/auth/heartbeat indica que todavía hay alguien y responde con el nuevo plazo (idleUntil); la respuesta de sesión indica cuánto tiempo puede estar inactiva y con qué frecuencia enviar uno (idle.minutes, idle.heartbeatSeconds). POST /v1/auth/logout con el token termina esa sesión.

Cómo es un rechazo​

Un token ausente y uno no válido son ambos 401, y el mensaje indica cuál:

{
"error": {
"type": "https://reelwire.io/errors/unauthenticated",
"code": "unauthenticated",
"message": "Provide a bearer token",
"requestId": "01M2SR17V1VCT4R68VQXE5PXGH",
"details": []
}
}
{
"error": {
"type": "https://reelwire.io/errors/unauthenticated",
"code": "unauthenticated",
"message": "Invalid credentials",
"requestId": "01M2SR17SJRFQ844N1FX9FFSKJ",
"details": []
}
}

«Invalid credentials» es la misma respuesta tanto si la clave nunca existió, como si fue revocada, caducó o pertenece a otro espacio de trabajo, a propósito: distinguirlas le diría a un atacante qué intentos se acercaron. El único 401 con algo más que decir es un token de asistente de IA enviado directamente a la API: «This token belongs to an assistant's connection and works only through Reelwire's MCP server.»

Una credencial válida a la que no se permite la solicitud es un 403, y ese mensaje es tan específico como se puede con seguridad, porque es para usted y no para un extraño:

{
"error": {
"type": "https://reelwire.io/errors/forbidden",
"code": "forbidden",
"message": "This key may not read here. Give it the \"Team: read\" permission, or use a key that has it.",
"requestId": "01M2SR17R25EKN6DM1H9DVDS0D",
"details": []
}
}

Una clave que llega a una ruta que pertenece a una persona con sesión iniciada recibe una frase distinta:

{
"error": {
"type": "https://reelwire.io/errors/forbidden",
"code": "forbidden",
"message": "Sign in to do this. Secrets and money are for a signed-in person: an API key cannot mint a credential, change what a workspace pays, or buy allowance. Nor can it rename this workspace or leave it, because a key holds no role and is nobody's membership.",
"requestId": "01M2SQPYK8ZC7G18BEQ4PHAQB8",
"details": []
}
}

Una ruta que no está en ningún área de permisos responde a una clave «An API key cannot use this endpoint», y un cambio en un área que una clave solo puede leer, como POST /v1/licence en Plan, dice que esa área «is read-only for an API key». Ambos son un 403, y ningún permiso cambia ninguna de las dos respuestas.

Algunas rutas comprueban un derecho exacto antes que nada y responden con palabras más antiguas: «This credential lacks the credentials:write scope» al listar, crear, cambiar, mostrar, revocar o eliminar una clave y desde las rutas del secreto del webhook, a cualquier clave y a una persona sin Administración, y «This credential lacks the team:write scope» al invitar, cambiar o eliminar a un colega, a una clave sin Team: write y a una persona sin Administración. Significan lo que significan las frases anteriores.

Consulte Errores para ver el envoltorio completo.

Límites de frecuencia​

429 es un límite de frecuencia. Cada solicitud que lleva una clave o una sesión cuenta contra esa única credencial, 600 por minuto sea cual sea la ruta a la que llame, de modo que una integración ocupada no pueda agotar el presupuesto de otra. Algunas rutas añaden un límite propio más estricto, contado por dirección:

RutaLímite
Iniciar sesión, registrarse, confirmar una dirección, los dos pasos del restablecimiento de contraseña10 por minuto
Mostrar de nuevo una clave o el secreto de firma del webhook10 por minuto, como al iniciar sesión
Enviar un mensaje al asistente de IA o iniciar un chat con él (POST /v1/ai/generate)30 por minuto
Adjuntar un archivo para el asistente de IA (POST /v1/ai/attachments)120 por minuto
Enviar un correo de prueba de sindicación (POST /v1/syndication/sender/test)10 por minuto

El 429 de un límite de frecuencia lleva retryAfterSeconds y un encabezado Retry-After. Cada respuesta contada contra un límite indica además en qué punto está, en x-ratelimit-limit, x-ratelimit-remaining y x-ratelimit-reset (segundos hasta que se rellena); en una ruta con un límite propio, describen ese límite.