Emitir un token personal
Cuándo usar un token personal, qué alcanza a ver y cuándo vence.
Usa un token personal para lo que debe ver exactamente lo que ves tú. Usa una API key para lo que pertenece al workspace.
Los dos son una credencial sk_ en el mismo header y llaman a las mismas rutas. Lo que cambia es de quién son los permisos. Una API key lleva los suyos escritos encima el día que se emite. Un token personal no lleva permisos propios. Cada llamada se resuelve contra la membresía de la persona que representa, en ese momento.
| API key | Token personal | |
|---|---|---|
| ¿De quién es? | Del workspace. Nadie en particular. | De un miembro. |
| ¿Qué ve? | Todo el workspace. | Exactamente lo que ve esa persona. |
| ¿Quién la emite? | Alguien con api_keys:write. | Cualquier miembro, para sí mismo. |
| ¿Cuánto dura? | Hasta que alguien la revoque. | Hasta que se revoque, venza, o esa persona pierda el acceso. |
Un token personal sirve para tus propios scripts, tu cliente de IA o una planilla que se actualiza sola. Todo eso debe ver lo mismo que tú y apagarse cuando dejas el workspace.
Una API key sirve para lo que pertenece al workspace: el sitio público, una integración con otro sistema, un proceso nocturno. Esas cosas siguen funcionando cuando quien las conectó se va.
Emitirlo
La aplicación lo emite en «Mi perfil › Tokens personales › Nuevo token». Pide un nombre y un vencimiento, y deja elegir los permisos. El secreto aparece una sola vez.
Ninguna otra credencial puede emitir un token personal. Solo vale una sesión iniciada:
{
"error": {
"code": "FORBIDDEN",
"message": "A personal token can only be minted by the member themselves, from a signed-in session",
"requestId": "0e95dbb6-9e26-4e02-aeb8-1283670577fa"
}
}Una aplicación conectada tampoco puede emitir uno en tu nombre.
El 201 devuelve esto:
{
"id": "a03db697-d779-4610-b2c2-a9fc4ba24302",
"tenant_id": "00000000-0000-4000-8000-000000000001",
"user_id": "cba9acd8-ab10-4b07-ada5-c7d436d1403e",
"name": "Reportes semanales",
"prefix": "sk_t6pgh",
"scopes": ["leads:read", "contacts:read", "conversations:read", "messages:read"],
"created_at": "2026-09-22T08:23:19.679476+00:00",
"last_used_at": null,
"expires_at": "2026-12-21T08:23:19.677+00:00",
"revoked_at": null,
"connector": false,
"secret": "sk_t6pgh…"
}user_id es el miembro al que el token representa. Es la única diferencia visible con una API key: en GET /api-keys una API key trae ese campo en null.
secret se muestra una vez. Después queda el prefix, sus primeros caracteres, que sirve para reconocerlo en una lista sin guardarlo en ninguna parte.
El techo de permisos
Si no pides scopes, el token nace con todos los que tiene tu rol hoy. Puedes quitarle permisos: pedir menos siempre se acepta. Pedir más, no.
{
"error": {
"code": "FORBIDDEN",
"message": "You cannot grant a personal token that includes a permission you do not have (billing:write)",
"requestId": "ff27c537-3f3d-4da4-be98-4f9088a2cb2e"
}
}El 403 nombra el permiso que sobra.
La lista guardada es un techo que se vuelve a aplicar en cada llamada, contra los permisos que tu rol tenga en ese momento. Si mañana te sacan contacts:read, el token deja de leer contactos ese mismo día, sin que nadie lo re-emita. Al revés no ocurre: un permiso nuevo en tu rol no aparece en un token que no lo pidió.
Trampa
Tu token ve solo los registros que tú ves
Un token personal hereda tu visibilidad. Si tu rol solo alcanza los registros que tienes asignados, el token alcanza esos mismos y ninguno más. Lo mismo vale para las sucursales cuando el rol está acotado a las suyas.
Por eso un miembro con visibilidad acotada puede emitir un token personal y no puede emitir una API key: la API key vería el workspace entero.
El vencimiento
Si no dices nada, 90 días. Puedes pedir una fecha exacta, o null para que no venza.
{ "name": "Reportes semanales", "expires_at": null }Un token sin vencimiento tiene sentido para algo que corre todos los días, como el conector de IA. Para un script puntual, deja el vencimiento.
Un token vencido sigue apareciendo en la lista, con su expires_at en el pasado.
Listar y revocar
curl https://api.vitrinadev.com/api/v1/personal-tokens \
-H "Authorization: Bearer $VITRINA_TOKEN"Los tuyos, sin pedir ningún permiso especial. Los de otra persona con ?user_id=, que sí pide api_keys:read, el mismo permiso que pide listar las API keys del workspace. Los revocados no salen a menos que los pidas con include_revoked=true.
curl -X DELETE https://api.vitrinadev.com/api/v1/personal-tokens/a03db697-d779-4610-b2c2-a9fc4ba24302 \
-H "Authorization: Bearer $VITRINA_TOKEN"204, sin body. Los tuyos siempre; los de otra persona con api_keys:write. Es inmediato: la siguiente llamada con ese secreto responde 401.
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key",
"requestId": "3fe5839c-7b53-42b3-aa0a-5fbe1dec107b"
}
}El día que esa persona se va
No hay que acordarse de revocar nada. Si la membresía deja de estar activa, el token deja de servir en la siguiente llamada. Da lo mismo si se desactivó la cuenta, si se suspendió o si se sacó a la persona del workspace:
{
"error": {
"code": "UNAUTHORIZED",
"message": "The member this token acts as no longer has access to this workspace",
"requestId": "5bf3efb6-d32d-407e-99f5-262b97ad5e88"
}
}La fila del token queda como estaba, así que esto no es una revocación. Si se restituye el acceso, el token vuelve a funcionar. Una API key no se comporta así: sobrevive a quien la emitió porque no es de nadie.
El conector de IA
El cliente sin navegador que conectas desde «Configuración › Conexiones › MCP» usa un token personal. Sus permisos son los del conector, recortados a los tuyos. Se reconoce por connector: true en la lista.
Por eso el conector de un miembro con visibilidad acotada responde lo mismo que respondería esa persona. La casilla de datos económicos no entrega nada a quien no puede leerlos. El permiso no queda en el token, y la respuesta lo dice con economics: false.
El cupo de llamadas
Todos tus tokens personales comparten un cupo, el tuyo, separado del que usa tu sesión en la aplicación. Un token nuevo no trae cupo nuevo. El estado vigente viaja en cada respuesta:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119El contrato de los tres endpoints está en Referencia · Tokens personales.