PassFortressPassFortress
Ir a la app
Documentación para desarrolladores

API de PassFortress

Una API REST 100% programática para gestionar tu bóveda de secretos cifrada del lado servidor. Crea una API key, llama a los endpoints y deja que tus aplicaciones lean y escriban secretos de forma segura.

Visión general

Introducción

Qué es la API y cómo encaja en tu integración.

Todos los endpoints viven bajo /api/v1/, relativos al host del despliegue. En estos ejemplos usamos https://api.passfortress.com como host de referencia. La API expone tus secretos, contenedores, identificadores, sitios web, IPs y perfil. Los metadatos (nombres, URLs, tipos) se sirven sin la master key; los valores descifrados exigen enviar la master key en cada petición (ver Master key y cifrado).

URL base

https://api.passfortress.com/api/v1

Autenticación

API key (Bearer)

Formato

JSON · UTF-8

Quick-start

Primeros pasos

De cero a tu primera petición autenticada.

  1. 1

    Crea una API key en /developers/api-keys con los scopes que necesites. El token se muestra una sola vez: cópialo y guárdalo en un gestor de secretos o variable de entorno.

  2. 2

    Haz tu primera petición: lista los metadatos de tus secretos (no requiere master key).

cURL
curl "https://api.passfortress.com/api/v1/secrets/" \
  -H "Authorization: Bearer pf_live_xxx"

La misma petición en varios lenguajes:

cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/?type=password&page=1" \
  -H "Authorization: Bearer pf_live_xxx"

¿Y los valores?

Para leer el valor descifrado de un secreto añade la cabecera X-Master-Key con la master key del usuario. Por ejemplo, GET /secrets/{uuid}/value/ requiere el scope values:read y la master key.

Seguridad

Autenticación

API keys Bearer para acceso servidor-a-servidor.

Los clientes programáticos se autentican con una API key en la cabeceraAuthorization. Las claves se crean en el panel web y se muestran una única vez.

Cabecera
Authorization: Bearer pf_live_xxx

Entornos

El prefijo identifica el entorno: pf_live_ para producción y pf_test_ para pruebas. Mantén separadas las claves de cada entorno.

JWT (apps de usuario)

Las apps de usuario final pueden usar el flujo JWT (/auth/login/). Para integraciones servidor-a-servidor, usa API keys.

Nunca expongas una API key en el navegador

Las API keys son servidor-a-servidor. No las incrustes en JavaScript de cliente, apps móviles ni repositorios públicos: cualquiera que las lea obtiene acceso a la bóveda. Úsalas solo desde tu backend.

Cifrado

Master key y cifrado

Por qué los valores necesitan una cabecera extra.

PassFortress cifra los valores de los secretos en el servidor con la master key del usuario, que nunca se almacena (ni en el servidor ni en los backups). Para leer o escribir un valor descifrado debes enviar la cabecera X-Master-Key: <master-key> en esa petición, además de la API key. Los metadatos (listar secretos, nombres, URLs, contenedores) no la requieren.

Leer un valor
curl "https://api.passfortress.com/api/v1/secrets/SECRET_UUID/value/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>"

428: falta la master key

Una operación sobre un valor sin X-Master-Key responde 428 Precondition Required con código master_key_required.

403: master key incorrecta

Si la master key no valida, responde 403 con código master_key_invalid. No revela ningún secreto.

¿Por qué 428 y no 401?

Un 401 se confunde con un token caducado y dispararía un refresh/logout en muchos clientes, expulsando al usuario de una sesión válida. El 428 deja claro que la credencial es correcta pero falta una precondición: la master key.

Implicación de confianza

Si tu integración envía la master key, está custodiando la clave que descifra toda la bóveda. Trátala con el mismo cuidado que la propia API key: solo en memoria del backend, nunca en logs ni en el navegador.

Permisos

Scopes

Cada clave concede scopes; cada endpoint exige uno. Aplica el mínimo privilegio.

ScopeRecursoConcede
secrets:readSecretosListar y leer los metadatos de los secretos (nombre, tipo, URL, contenedor). No descifra valores.
secrets:writeSecretosCrear, editar, duplicar y eliminar secretos.
values:readSecretosLeer valores descifrados: valor actual, historial y descarga de archivos. Requiere también la master key.
containers:readContenedoresListar y leer contenedores (carpetas).
containers:writeContenedoresCrear, renombrar y eliminar contenedores.
identifiers:readIdentificadoresLeer los pares clave-valor asociados a cada secreto.
identifiers:writeIdentificadoresCrear, editar y eliminar identificadores.
websites:readSitios webListar los sitios web asociados a los secretos.
websites:writeSitios webCrear y modificar sitios web.
ips:readIPsLeer las listas blanca/negra de direcciones IP.
ips:writeIPsAñadir y eliminar reglas de IP.
profile:readPerfilLeer los datos del perfil y las preferencias.
profile:writePerfilActualizar perfil, contraseña, tema y rotar la master key.
share:writeCompartirCompartir secretos y aceptar secretos compartidos. Requiere la master key.
import:writeImportaciónImportar secretos desde un CSV de Chrome. Requiere la master key.
apikeys:readAPI keysListar las API keys y sus metadatos.
apikeys:write
sensible
API keysCrear, rotar y revocar API keys. Una clave con este scope puede acuñar otras claves.

apikeys:write es peligroso

Una clave con apikeys:write puede acuñar otras claves (con cualquier scope). Concédelo solo a integraciones de aprovisionamiento de confianza.

Operación

Límites y paginación

Cuotas por clave y cómo recorrer colecciones.

Rate limits

El límite es por clave. Superarlo devuelve 429 con la cabecera Retry-After.

TierLímiteUso
default1 000 / horaLímite por defecto para nuevas claves.
high10 000 / horaPara integraciones con mayor volumen.
unlimitedSin límiteReservado para casos especiales. No disponible para claves con el scope values:read (devuelve invalid_rate_tier).

Paginación

Las colecciones usan paginación por número de página, 50 elementos por página. Usa ?page=N y sigue el campo next hasta que sea null.

Respuesta paginada
{
  "count": 673,
  "next": "https://api.passfortress.com/api/v1/secrets/?page=2",
  "previous": null,
  "results": [ /* … 50 elementos … */ ]
}

Endpoints

Referencia de la API

Cada endpoint con su método, scope, parámetros y ejemplos.

Autenticación

Login con usuario/contraseña y gestión de la sesión JWT. Los clientes programáticos normalmente usan API keys; estos endpoints existen para apps de usuario final.

POST/api/v1/auth/login/
Sin scope

Iniciar sesión y obtener tokens JWT.

Devuelve un access token (corta duración), un refresh token (1 año) y el usuario.

Cuerpo (JSON)
username*stringNombre de usuario de la cuenta.
password*stringContraseña.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/login/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ada",
    "password": "mi-contraseña"
  }'
Respuesta
200 OK · application/json
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "uuid": "c0ffee00-1111-2222-3333-444455556666",
    "username": "ada",
    "email": "dev@example.com",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "mobile": "+34600000000",
    "address": "Madrid",
    "language": "es",
    "theme": "system",
    "email_verified": true,
    "mobile_verified": false,
    "tfa_email_enabled": false,
    "tfa_mobile_enabled": false,
    "enabled_envfiles": true,
    "enabled_files": true
  }
}
POST/api/v1/auth/register/
Sin scope

Registrar un nuevo usuario.

Devuelve los tokens JWT y el usuario creado (201).

Cuerpo (JSON)
username*stringNombre de usuario único.
email*stringCorreo único.
password*stringContraseña (mínimo 8).
master_key*stringMaster key inicial (mínimo 8; no repetitiva, secuencial ni común).
first_namestringNombre.
last_namestringApellidos.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/register/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ada",
    "email": "nuevo@example.com",
    "password": "mi-contraseña",
    "master_key": "<tu-master-key>",
    "first_name": "Ada"
  }'
Respuesta
200 OK · application/json
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "uuid": "c0ffee00-1111-2222-3333-444455556666",
    "username": "ada",
    "email": "dev@example.com",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "mobile": "+34600000000",
    "address": "Madrid",
    "language": "es",
    "theme": "system",
    "email_verified": true,
    "mobile_verified": false,
    "tfa_email_enabled": false,
    "tfa_mobile_enabled": false,
    "enabled_envfiles": true,
    "enabled_files": true
  }
}
POST/api/v1/auth/token/refresh/
Sin scope

Renovar el access token.

Cuerpo (JSON)
refresh*stringRefresh token vigente.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/token/refresh/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
Respuesta
200 OK · application/json
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
GET/api/v1/auth/me/
profile:read

Obtener el usuario autenticado.

Devuelve el objeto del usuario directamente (sin envoltorio).

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/auth/me/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "c0ffee00-1111-2222-3333-444455556666",
  "username": "ada",
  "email": "dev@example.com",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "mobile": "+34600000000",
  "address": "Madrid",
  "language": "es",
  "theme": "system",
  "email_verified": true,
  "mobile_verified": false,
  "tfa_email_enabled": false,
  "tfa_mobile_enabled": false,
  "enabled_envfiles": true,
  "enabled_files": true
}
POST/api/v1/auth/logout/
profile:write

Cerrar sesión (revoca el refresh token).

Devuelve 205 Reset Content sin cuerpo. Idempotente.

Cuerpo (JSON)
refresh*stringRefresh token a invalidar.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/logout/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
Respuesta
200 OK · application/json
"205 Reset Content (sin cuerpo)"
POST/api/v1/auth/master-key/validate/
values:read

Validar la master key sin revelar secretos.

Para claves API requiere el scope values:read (validar la master key es la misma capacidad que leer un valor). Necesita también la cabecera X-Master-Key. Sujeto al lockout anti-fuerza-bruta: tras varios intentos fallidos devuelve 429 master_key_locked con Retry-After.

Cuerpo (JSON)
master_key*stringMaster key a comprobar.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/master-key/validate/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "master_key": "<tu-master-key>"
  }'
Respuesta
200 OK · application/json
{
  "valid": true
}
POST/api/v1/auth/verify/request/
profile:write

Solicitar un código de verificación.

Cuerpo (JSON)
scope*stringverify_email, verify_mobile, verify_email_tfa o verify_mobile_tfa.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/verify/request/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "verify_email"
  }'
Respuesta
200 OK · application/json
{
  "scope": "verify_email",
  "channel": "email",
  "sent": true
}
POST/api/v1/auth/verify/confirm/
profile:write

Confirmar el código de verificación.

Cuerpo (JSON)
scope*stringEl mismo scope usado en la solicitud.
code*stringCódigo recibido.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/verify/confirm/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "verify_email",
    "code": "123456"
  }'
Respuesta
200 OK · application/json
{
  "verified": true
}
POST/api/v1/auth/password-reset/
Sin scope

Solicitar el restablecimiento de contraseña.

Responde 200 siempre (no revela si el correo existe).

Cuerpo (JSON)
email*stringCorreo de la cuenta.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/password-reset/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "dev@example.com"
  }'
Respuesta
200 OK · application/json
{
  "sent": true
}
POST/api/v1/auth/password-reset/confirm/
Sin scope

Confirmar el restablecimiento con el código.

Cuerpo (JSON)
email*stringCorreo de la cuenta.
code*stringCódigo recibido por correo.
new_password*stringNueva contraseña (mínimo 8).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/auth/password-reset/confirm/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "dev@example.com",
    "code": "123456",
    "new_password": "nueva-contraseña"
  }'
Respuesta
200 OK · application/json
{
  "reset": true
}

Perfil

Datos del usuario, contraseña, tema y rotación de la master key.

GET/api/v1/profile/
profile:read

Leer el perfil del usuario.

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/profile/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "c0ffee00-1111-2222-3333-444455556666",
  "username": "ada",
  "email": "dev@example.com",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "mobile": "+34600000000",
  "address": "Madrid",
  "language": "es",
  "theme": "system",
  "email_verified": true,
  "mobile_verified": false,
  "tfa_email_enabled": false,
  "tfa_mobile_enabled": false,
  "enabled_envfiles": true,
  "enabled_files": true
}
PATCH/api/v1/profile/
profile:write

Actualizar campos del perfil.

Cuerpo (JSON)
first_namestringNombre.
last_namestringApellidos.
mobilestringTeléfono móvil.
addressstringDirección.
languagestringIdioma preferido.
themestringlight, dark o system.
Petición
cURL
curl -X PATCH "https://api.passfortress.com/api/v1/profile/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Ada",
    "last_name": "Lovelace"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "c0ffee00-1111-2222-3333-444455556666",
  "username": "ada",
  "email": "dev@example.com",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "mobile": "+34600000000",
  "address": "Madrid",
  "language": "es",
  "theme": "system",
  "email_verified": true,
  "mobile_verified": false,
  "tfa_email_enabled": false,
  "tfa_mobile_enabled": false,
  "enabled_envfiles": true,
  "enabled_files": true
}
POST/api/v1/profile/password/
profile:write

Cambiar la contraseña de la cuenta.

Cuerpo (JSON)
old_password*stringContraseña actual.
new_password*stringNueva contraseña (mínimo 8).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/profile/password/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "old_password": "actual",
    "new_password": "nueva"
  }'
Respuesta
200 OK · application/json
{
  "changed": true
}
POST/api/v1/profile/master-key/
profile:write

Rotar la master key (re-cifra todos los secretos).

Descifra cada secreto con la master key antigua y lo vuelve a cifrar con la nueva. Ambas claves viajan en el cuerpo (no usa la cabecera X-Master-Key). Operación pesada e irreversible si se pierde la nueva clave.

Cuerpo (JSON)
old_master_key*stringMaster key actual.
new_master_key*stringNueva master key (mínimo 8; no repetitiva, secuencial ni común).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/profile/master-key/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "old_master_key": "<actual>",
    "new_master_key": "<nueva>"
  }'
Respuesta
200 OK · application/json
{
  "changed": true
}
PATCH/api/v1/profile/theme/
profile:write

Cambiar el tema preferido.

Devuelve el perfil completo actualizado.

Cuerpo (JSON)
theme*stringlight, dark o system.
Petición
cURL
curl -X PATCH "https://api.passfortress.com/api/v1/profile/theme/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "theme": "dark"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "c0ffee00-1111-2222-3333-444455556666",
  "username": "ada",
  "email": "dev@example.com",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "mobile": "+34600000000",
  "address": "Madrid",
  "language": "es",
  "theme": "dark",
  "email_verified": true,
  "mobile_verified": false,
  "tfa_email_enabled": false,
  "tfa_mobile_enabled": false,
  "enabled_envfiles": true,
  "enabled_files": true
}

Países

Catálogo de países (datos de referencia, no requiere scope).

GET/api/v1/countries/
Sin scope

Listar países disponibles.

Devuelve un array plano (sin paginación).

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/countries/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
[
  {
    "id": 1,
    "name_en": "Spain",
    "name_es": "España",
    "iso2": "ES",
    "iso3": "ESP",
    "phone_code": "34"
  },
  {
    "id": 2,
    "name_en": "Mexico",
    "name_es": "México",
    "iso2": "MX",
    "iso3": "MEX",
    "phone_code": "52"
  }
]

IPs

Listas blanca/negra de direcciones IP para el control de acceso de la cuenta.

GET/api/v1/ips/
ips:read

Listar reglas de IP.

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/ips/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "ip1",
      "name": "Oficina",
      "ip_range": "203.0.113.0/24",
      "list": "white"
    }
  ]
}
POST/api/v1/ips/
ips:write

Añadir una regla de IP.

Cuerpo (JSON)
ip_range*stringIP o rango (CIDR).
liststringwhite o black.
namestringEtiqueta descriptiva (opcional).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/ips/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Oficina",
    "ip_range": "203.0.113.0/24",
    "list": "white"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "ip1",
  "name": "Oficina",
  "ip_range": "203.0.113.0/24",
  "list": "white"
}
GET/api/v1/ips/{uuid}/
ips:read

Obtener una regla de IP.

Parámetros de ruta
uuid*stringUUID de la regla.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/ips/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "ip1",
  "name": "Oficina",
  "ip_range": "203.0.113.0/24",
  "list": "white"
}
DELETE/api/v1/ips/{uuid}/
ips:write

Eliminar una regla de IP.

Devuelve 204 No Content sin cuerpo.

Parámetros de ruta
uuid*stringUUID de la regla.
Petición
cURL
curl -X DELETE "https://api.passfortress.com/api/v1/ips/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
"204 No Content (sin cuerpo)"

Contenedores

Carpetas para organizar los secretos.

GET/api/v1/containers/
containers:read

Listar contenedores.

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/containers/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ]
}
POST/api/v1/containers/
containers:write

Crear un contenedor.

Cuerpo (JSON)
name*stringNombre del contenedor.
descriptionstringDescripción (opcional).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/containers/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Trabajo",
    "description": ""
  }'
Respuesta
200 OK · application/json
{
  "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
  "name": "Trabajo",
  "description": "",
  "secrets_count": 0
}
GET/api/v1/containers/{uuid}/
containers:read

Obtener un contenedor.

Parámetros de ruta
uuid*stringUUID del contenedor.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/containers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
  "name": "Trabajo",
  "description": "",
  "secrets_count": 42
}
PUT/api/v1/containers/{uuid}/
containers:write

Reemplazar un contenedor.

Parámetros de ruta
uuid*stringUUID del contenedor.
Cuerpo (JSON)
name*stringNuevo nombre.
descriptionstringNueva descripción.
Petición
cURL
curl -X PUT "https://api.passfortress.com/api/v1/containers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Trabajo (2026)",
    "description": ""
  }'
Respuesta
200 OK · application/json
{
  "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
  "name": "Trabajo (2026)",
  "description": "",
  "secrets_count": 42
}
PATCH/api/v1/containers/{uuid}/
containers:write

Actualizar parcialmente un contenedor.

Parámetros de ruta
uuid*stringUUID del contenedor.
Cuerpo (JSON)
namestringNuevo nombre.
descriptionstringNueva descripción.
Petición
cURL
curl -X PATCH "https://api.passfortress.com/api/v1/containers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Personal"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
  "name": "Personal",
  "description": "",
  "secrets_count": 42
}
DELETE/api/v1/containers/{uuid}/
containers:write

Eliminar un contenedor.

Devuelve 204 No Content sin cuerpo.

Parámetros de ruta
uuid*stringUUID del contenedor.
Petición
cURL
curl -X DELETE "https://api.passfortress.com/api/v1/containers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
"204 No Content (sin cuerpo)"

Secretos

El núcleo de la API. Los METADATOS (nombre, tipo, URL, contenedores) no requieren master key; leer o escribir VALORES descifrados sí requiere la cabecera X-Master-Key.

GET/api/v1/secrets/
secrets:read

Listar secretos (solo metadatos).

Devuelve metadatos paginados (50 por página). Nunca incluye valores descifrados.

Parámetros de consulta
typestringpassword, envfile, totp o file.
containerstringUUID del contenedor para filtrar.
searchstringBúsqueda por nombre, URL o nombre de archivo.
pageintegerNúmero de página (50 por página).
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/?type=password&page=1" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 673,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
      "secret_type": "password",
      "name": "GitHub",
      "file_name": null,
      "url": "https://github.com",
      "notes": "",
      "available": true,
      "shared": false,
      "automatic_password_change": false,
      "website": {
        "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
        "hostname": "github.com",
        "login_url": "https://github.com/login",
        "automatic_password_change": false
      },
      "containers": [
        {
          "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
          "name": "Trabajo",
          "description": "",
          "secrets_count": 42
        }
      ],
      "container_uuids": [
        "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
      ],
      "identifiers": [
        {
          "uuid": "id1",
          "key": "usuario",
          "value": "ada@example.com"
        }
      ],
      "has_value": true,
      "imported_at": null
    }
  ]
}
POST/api/v1/secrets/
secrets:write
X-Master-Key

Crear un secreto con su primer valor.

Requiere X-Master-Key porque cifra el valor en el servidor. Devuelve los metadatos (201).

Cuerpo (JSON)
secret_type*stringpassword, envfile, totp o file.
namestringNombre del secreto.
valuestringValor en claro para password/envfile (se cifra en el servidor). Para totp es la URI otpauth://totp/... completa (semilla base32 + issuer + algoritmo/dígitos/periodo); el servidor la guarda cifrada sin parsearla.
file_content_b64stringContenido del archivo en base64 (tipo file).
file_namestringNombre del archivo (tipo file).
urlstringURL asociada.
notesstringNotas.
containersarrayUUIDs de contenedores (alias: container_uuids).
identifiersarrayPares {key, value}.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/secrets/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "secret_type": "password",
    "name": "GitHub",
    "url": "https://github.com",
    "value": "s3cr3t-p4ss",
    "containers": [
      "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
    ],
    "identifiers": [
      {
        "key": "usuario",
        "value": "ada@example.com"
      }
    ]
  }'
Respuesta
200 OK · application/json
{
  "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
  "secret_type": "password",
  "name": "GitHub",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": false,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
POST/api/v1/secrets/
secrets:write
X-Master-Key

Crear un autenticador TOTP (segundo factor).

Mismo endpoint que crear cualquier secreto, con secret_type: "totp". El value es la URI otpauth://totp/... completa (semilla base32 + issuer + algoritmo/dígitos/periodo). El servidor la cifra bajo la master key sin parsearla (conocimiento cero): NUNCA calcula códigos. El código de 6 dígitos se genera en el CLIENTE tras leer el valor. Compatible con el estándar TOTP (RFC 6238). Requiere X-Master-Key.

Cuerpo (JSON)
secret_type*stringDebe ser "totp".
namestringEtiqueta visible (p. ej. el issuer del servicio).
value*stringURI otpauth://totp/... completa (se guarda cifrada sin parsearse).
urlstringURL asociada.
notesstringNotas.
containersarrayUUIDs de contenedores (alias: container_uuids).
identifiersarrayPares {key, value} (p. ej. la cuenta).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/secrets/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "secret_type": "totp",
    "name": "GitHub",
    "value": "otpauth://totp/GitHub:ada@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub&algorithm=SHA1&digits=6&period=30",
    "url": "https://github.com",
    "identifiers": [
      {
        "key": "account",
        "value": "ada@example.com"
      }
    ]
  }'
Respuesta
200 OK · application/json
{
  "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
  "secret_type": "totp",
  "name": "GitHub",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": false,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
GET/api/v1/secrets/{uuid}/
secrets:read

Obtener los metadatos de un secreto.

Parámetros de ruta
uuid*stringUUID del secreto.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
  "secret_type": "password",
  "name": "GitHub",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": false,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
PUT/api/v1/secrets/{uuid}/
secrets:write
X-Master-Key

Reemplazar un secreto.

Requiere X-Master-Key (cifra el valor en el servidor). El tipo no se puede cambiar.

Parámetros de ruta
uuid*stringUUID del secreto.
Cuerpo (JSON)
namestringNombre.
valuestringNuevo valor (cifra en el servidor).
urlstringURL asociada.
notesstringNotas.
containersarrayUUIDs de contenedores.
identifiersarrayPares {key, value}.
Petición
cURL
curl -X PUT "https://api.passfortress.com/api/v1/secrets/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GitHub",
    "value": "nuevo-valor"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
  "secret_type": "password",
  "name": "GitHub",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": false,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
PATCH/api/v1/secrets/{uuid}/
secrets:write
X-Master-Key

Actualizar parcialmente un secreto.

Requiere X-Master-Key (la escritura cifra en el servidor).

Parámetros de ruta
uuid*stringUUID del secreto.
Cuerpo (JSON)
namestringNombre.
valuestringNuevo valor.
Petición
cURL
curl -X PATCH "https://api.passfortress.com/api/v1/secrets/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "value": "nuevo-valor"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
  "secret_type": "password",
  "name": "GitHub",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": false,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
DELETE/api/v1/secrets/{uuid}/
secrets:write

Eliminar un secreto.

Devuelve 204 No Content sin cuerpo.

Parámetros de ruta
uuid*stringUUID del secreto.
Petición
cURL
curl -X DELETE "https://api.passfortress.com/api/v1/secrets/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
"204 No Content (sin cuerpo)"
GET/api/v1/secrets/{uuid}/value/
values:read
X-Master-Key

Leer el valor descifrado de un secreto.

Descifra el valor en memoria usando la master key. Devuelve 428 si falta X-Master-Key y 403 si es incorrecta. Los intentos fallidos cuentan para el lockout: tras superar el umbral devuelve 429 master_key_locked con Retry-After. Para un secreto totp el value es la URI otpauth://...; el código de 6 dígitos se calcula en el cliente (el servidor no genera códigos).

Parámetros de ruta
uuid*stringUUID del secreto.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/{uuid}/value/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>"
Respuesta
200 OK · application/json
{
  "uuid": "a1b2c3d4-0000-1111-2222-333344445555",
  "secret_type": "password",
  "value": "s3cr3t-p4ss"
}
GET/api/v1/secrets/{uuid}/history/
values:read
X-Master-Key

Historial de valores descifrados.

Devuelve un array plano (sin paginación) de valores históricos descifrados.

Parámetros de ruta
uuid*stringUUID del secreto.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/{uuid}/history/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>"
Respuesta
200 OK · application/json
[
  {
    "uuid": "hv1",
    "value": "s3cr3t-p4ss",
    "created_date_time": "2026-06-01T11:00:00Z"
  },
  {
    "uuid": "hv2",
    "value": "old-pass",
    "created_date_time": "2026-01-12T09:30:00Z"
  }
]
POST/api/v1/secrets/{uuid}/duplicate/
secrets:write

Duplicar un secreto.

Devuelve los metadatos de la copia (201). No requiere master key.

Parámetros de ruta
uuid*stringUUID del secreto a copiar.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/secrets/{uuid}/duplicate/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "nuevo-uuid",
  "secret_type": "password",
  "name": "GitHub (copia)",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": false,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
POST/api/v1/secrets/{uuid}/share/
share:write
X-Master-Key

Compartir un secreto con otros usuarios.

Re-cifra el valor para cada destinatario; requiere X-Master-Key. Devuelve un resultado por correo.

Parámetros de ruta
uuid*stringUUID del secreto.
Cuerpo (JSON)
emails*arrayLista de correos destinatarios (al menos uno).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/secrets/{uuid}/share/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": [
      "colega@example.com"
    ]
  }'
Respuesta
200 OK · application/json
[
  {
    "email": "colega@example.com",
    "shared_uuid": "b2c3d4e5-1111-2222-3333-444455556666",
    "tmp_master_key": "F9Xk3pQ7mW2vN8sR4tY6uI1oP5aD0fG2hJ7kL9zC8bA",
    "error": null
  }
]
GET/api/v1/secrets/{uuid}/download/
values:read
X-Master-Key

Descargar el archivo descifrado (tipo file).

Devuelve el archivo binario descifrado. Requiere X-Master-Key.

Parámetros de ruta
uuid*stringUUID del secreto.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/{uuid}/download/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>"
Respuesta (binaria)

<contenido binario del archivo>

GET/api/v1/secrets/shared/
secrets:read

Listar secretos compartidos pendientes de aceptar.

Metadatos paginados de los secretos recibidos (shared = true).

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/secrets/shared/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "share1",
      "secret_type": "password",
      "name": "GitHub",
      "file_name": null,
      "url": "https://github.com",
      "notes": "",
      "available": true,
      "shared": true,
      "automatic_password_change": false,
      "website": {
        "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
        "hostname": "github.com",
        "login_url": "https://github.com/login",
        "automatic_password_change": false
      },
      "containers": [
        {
          "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
          "name": "Trabajo",
          "description": "",
          "secrets_count": 42
        }
      ],
      "container_uuids": [
        "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
      ],
      "identifiers": [
        {
          "uuid": "id1",
          "key": "usuario",
          "value": "ada@example.com"
        }
      ],
      "has_value": true,
      "imported_at": null
    }
  ]
}
POST/api/v1/secrets/accept-shared/
share:write
X-Master-Key

Aceptar un secreto compartido.

Re-cifra el valor recibido con tu master key. Requiere X-Master-Key y la clave temporal devuelta al compartir.

Cuerpo (JSON)
uuid*stringUUID del secreto compartido.
tmp_master_key*stringClave temporal recibida al compartir.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/secrets/accept-shared/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "uuid": "share1",
    "tmp_master_key": "F9Xk3pQ7mW2vN8sR4tY6uI1oP5aD0fG2hJ7kL9zC8bA"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "share1",
  "secret_type": "password",
  "name": "GitHub",
  "file_name": null,
  "url": "https://github.com",
  "notes": "",
  "available": true,
  "shared": true,
  "automatic_password_change": false,
  "website": {
    "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
    "hostname": "github.com",
    "login_url": "https://github.com/login",
    "automatic_password_change": false
  },
  "containers": [
    {
      "uuid": "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff",
      "name": "Trabajo",
      "description": "",
      "secrets_count": 42
    }
  ],
  "container_uuids": [
    "f1f2f3f4-aaaa-bbbb-cccc-ddddeeeeffff"
  ],
  "identifiers": [
    {
      "uuid": "id1",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ],
  "has_value": true,
  "imported_at": null
}
POST/api/v1/secrets/import/
import:write
X-Master-Key

Importar contraseñas desde un CSV de Chrome.

multipart/form-data con el CSV exportado de Chrome. Cifra cada valor; requiere X-Master-Key.

Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/secrets/import/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "X-Master-Key: <tu-master-key>" \
  -F "file=@passwords.csv"
Respuesta
200 OK · application/json
{
  "imported": 128,
  "skipped": 3,
  "errors": []
}

Identificadores

Pares clave-valor asociados a un secreto (p. ej. usuario, email, nota).

GET/api/v1/identifiers/
identifiers:read

Listar identificadores.

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/identifiers/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "id1",
      "secret": "a1b2c3d4-0000-1111-2222-333344445555",
      "key": "usuario",
      "value": "ada@example.com"
    }
  ]
}
POST/api/v1/identifiers/
identifiers:write

Crear un identificador.

Cuerpo (JSON)
secret*stringUUID del secreto.
keystringClave.
value*stringValor.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/identifiers/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "secret": "a1b2c3d4-0000-1111-2222-333344445555",
    "key": "usuario",
    "value": "ada@example.com"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "id1",
  "secret": "a1b2c3d4-0000-1111-2222-333344445555",
  "key": "usuario",
  "value": "ada@example.com"
}
GET/api/v1/identifiers/{uuid}/
identifiers:read

Obtener un identificador.

Parámetros de ruta
uuid*stringUUID del identificador.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/identifiers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "uuid": "id1",
  "secret": "a1b2c3d4-0000-1111-2222-333344445555",
  "key": "usuario",
  "value": "ada@example.com"
}
PUT/api/v1/identifiers/{uuid}/
identifiers:write

Reemplazar un identificador.

Parámetros de ruta
uuid*stringUUID del identificador.
Cuerpo (JSON)
keystringClave.
value*stringValor.
Petición
cURL
curl -X PUT "https://api.passfortress.com/api/v1/identifiers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "usuario",
    "value": "ada.lovelace@example.com"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "id1",
  "secret": "a1b2c3d4-0000-1111-2222-333344445555",
  "key": "usuario",
  "value": "ada.lovelace@example.com"
}
PATCH/api/v1/identifiers/{uuid}/
identifiers:write

Actualizar parcialmente un identificador.

Parámetros de ruta
uuid*stringUUID del identificador.
Cuerpo (JSON)
valuestringNuevo valor.
Petición
cURL
curl -X PATCH "https://api.passfortress.com/api/v1/identifiers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "value": "nuevo-valor"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "id1",
  "secret": "a1b2c3d4-0000-1111-2222-333344445555",
  "key": "usuario",
  "value": "nuevo-valor"
}
DELETE/api/v1/identifiers/{uuid}/
identifiers:write

Eliminar un identificador.

Devuelve 204 No Content sin cuerpo.

Parámetros de ruta
uuid*stringUUID del identificador.
Petición
cURL
curl -X DELETE "https://api.passfortress.com/api/v1/identifiers/{uuid}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
"204 No Content (sin cuerpo)"

Sitios web

Sitios web vinculados a los secretos (para favicons y autocompletado).

GET/api/v1/websites/
websites:read

Listar sitios web.

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/websites/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
      "hostname": "github.com",
      "login_url": "https://github.com/login",
      "automatic_password_change": false
    }
  ]
}
POST/api/v1/websites/
websites:write

Crear un sitio web.

Cuerpo (JSON)
hostname*stringHost del sitio (p. ej. github.com).
login_urlstringURL de inicio de sesión (opcional).
automatic_password_changebooleanSoporta cambio automático de contraseña.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/websites/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "hostname": "github.com",
    "login_url": "https://github.com/login"
  }'
Respuesta
200 OK · application/json
{
  "uuid": "w1a2b3c4-0000-1111-2222-333344445555",
  "hostname": "github.com",
  "login_url": "https://github.com/login",
  "automatic_password_change": false
}

API keys

Gestión programática de las propias API keys. Normalmente se administran desde el panel web; usar estos endpoints requiere el scope apikeys:*.

GET/api/v1/api-keys/
apikeys:read

Listar las API keys.

Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/api-keys/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "public_id": "key_abc123",
      "name": "CI deploy",
      "prefix": "pf_live_",
      "last4": "a1b2",
      "masked": "pf_live_…a1b2",
      "scopes": [
        "secrets:read",
        "values:read"
      ],
      "is_active": true,
      "expires_at": null,
      "last_used_at": "2026-06-20T08:00:00Z",
      "created_at": "2026-05-01T10:00:00Z",
      "revoked_at": null,
      "rate_tier": "default",
      "ip_allowlist": [],
      "environment": "live"
    }
  ]
}
POST/api/v1/api-keys/
apikeys:write

Crear una API key (el token se muestra una sola vez).

La respuesta añade el campo token (texto plano) que NO se vuelve a mostrar.

Cuerpo (JSON)
name*stringNombre descriptivo.
scopesarrayLista de scopes.
environmentstringlive o test.
rate_tierstringdefault, high o unlimited.
expires_atstringFecha de expiración ISO-8601 (opcional).
ip_allowlistarrayAllowlist de IPs/CIDR (opcional).
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/api-keys/" \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI deploy",
    "scopes": [
      "secrets:read",
      "values:read"
    ],
    "environment": "live",
    "rate_tier": "default"
  }'
Respuesta
200 OK · application/json
{
  "public_id": "key_abc123",
  "name": "CI deploy",
  "prefix": "pf_live_",
  "last4": "a1b2",
  "masked": "pf_live_…a1b2",
  "scopes": [
    "secrets:read",
    "values:read"
  ],
  "is_active": true,
  "expires_at": null,
  "last_used_at": null,
  "created_at": "2026-05-01T10:00:00Z",
  "revoked_at": null,
  "rate_tier": "default",
  "ip_allowlist": [],
  "environment": "live",
  "token": "pf_live_3f9a2c8e7b1d4f0a6c5e9d8b7a1f2e3c"
}
GET/api/v1/api-keys/{public_id}/
apikeys:read

Obtener una API key.

Parámetros de ruta
public_id*stringIdentificador público de la clave.
Petición
cURL
curl -X GET "https://api.passfortress.com/api/v1/api-keys/{public_id}/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "public_id": "key_abc123",
  "name": "CI deploy",
  "prefix": "pf_live_",
  "last4": "a1b2",
  "masked": "pf_live_…a1b2",
  "scopes": [
    "secrets:read",
    "values:read"
  ],
  "is_active": true,
  "expires_at": null,
  "last_used_at": "2026-06-20T08:00:00Z",
  "created_at": "2026-05-01T10:00:00Z",
  "revoked_at": null,
  "rate_tier": "default",
  "ip_allowlist": [],
  "environment": "live"
}
POST/api/v1/api-keys/{public_id}/revoke/
apikeys:write

Revocar una API key.

Devuelve la clave actualizada (is_active = false, revoked_at fijado).

Parámetros de ruta
public_id*stringIdentificador público de la clave.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/api-keys/{public_id}/revoke/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "public_id": "key_abc123",
  "name": "CI deploy",
  "prefix": "pf_live_",
  "last4": "a1b2",
  "masked": "pf_live_…a1b2",
  "scopes": [
    "secrets:read",
    "values:read"
  ],
  "is_active": false,
  "expires_at": null,
  "last_used_at": "2026-06-20T08:00:00Z",
  "created_at": "2026-05-01T10:00:00Z",
  "revoked_at": "2026-06-26T12:00:00Z",
  "rate_tier": "default",
  "ip_allowlist": [],
  "environment": "live"
}
POST/api/v1/api-keys/{public_id}/rotate/
apikeys:write

Rotar una API key (genera un token nuevo).

Devuelve los campos de lectura más un token de un solo uso.

Parámetros de ruta
public_id*stringIdentificador público de la clave.
Petición
cURL
curl -X POST "https://api.passfortress.com/api/v1/api-keys/{public_id}/rotate/" \
  -H "Authorization: Bearer pf_live_xxx"
Respuesta
200 OK · application/json
{
  "public_id": "key_abc123",
  "name": "CI deploy",
  "prefix": "pf_live_",
  "last4": "c5e9",
  "masked": "pf_live_…c5e9",
  "scopes": [
    "secrets:read",
    "values:read"
  ],
  "is_active": true,
  "expires_at": null,
  "last_used_at": "2026-06-20T08:00:00Z",
  "created_at": "2026-05-01T10:00:00Z",
  "revoked_at": null,
  "rate_tier": "default",
  "ip_allowlist": [],
  "environment": "live",
  "token": "pf_live_9d8b7a1f2e3c3f9a2c8e7b1d4f0a6c5e"
}

Diagnóstico

Errores

Envoltura de error y catálogo de códigos.

Toda respuesta de error tiene la forma { "detail": "…", "code": "…" }. Usa el code (no el texto) para ramificar tu lógica.

codeHTTPCausaSolución
invalid_api_key
401
La API key no existe, está mal formada o el prefijo no corresponde al entorno.Revisa la cabecera Authorization y usa una clave válida (pf_live_ / pf_test_).
api_key_revoked
401
La clave fue revocada desde el panel o de forma automática tras acumular demasiados intentos fallidos de master key (auto-revoke por fuerza bruta).Crea una nueva API key en /developers/api-keys.
api_key_expired
401
La clave superó su fecha de expiración.Rota la clave o crea una nueva sin expiración.
ip_not_allowed
401
La petición proviene de una IP fuera del allowlist de la clave.Añade la IP de origen al allowlist de la clave o emite la petición desde una IP permitida.
insufficient_scope
403
La clave no tiene el scope que exige el endpoint.Crea o rota la clave incluyendo el scope necesario (ver tabla de scopes).
master_key_required
428
Operación sobre un valor sin la cabecera X-Master-Key.Reenvía la petición incluyendo X-Master-Key con la master key del usuario.
master_key_invalid
403
La master key enviada no valida contra el test_passwd del usuario.Comprueba que la master key es exactamente la del propietario de la bóveda.
master_key_locked
429
Demasiados intentos fallidos de master key (umbral por usuario, por defecto 10/hora); el endpoint queda bloqueado temporalmente con backoff exponencial.Espera el tiempo indicado en la cabecera Retry-After y reintenta con la master key correcta (una validación correcta resetea el contador).
not_authenticated
401
Falta toda credencial (ni API key ni JWT).Incluye la cabecera Authorization: Bearer <token>.
permission_denied
403
El recurso pertenece a otro usuario o la acción no está permitida.Usa el uuid de un recurso que pertenezca al titular de la clave.
not_found
404
El recurso no existe o no es visible para el titular.Verifica el uuid / public_id de la ruta.
validation_error
400
El cuerpo no cumple el esquema (campos faltantes o inválidos).Revisa el detalle por campo en la respuesta y corrige el payload.
invalid_rate_tier
400
Se intentó crear o rotar una clave con el scope values:read y el tier unlimited, combinación no permitida.Elige el tier default o high para claves que puedan leer valores descifrados.
weak_master_key
400
La nueva master key (en registro o cambio de master key) no cumple la política mínima: ≥8 caracteres, no toda idéntica, sin secuencias triviales ni claves comunes.Elige una master key más fuerte. Las master keys existentes mantienen su validez hasta que las cambies.
throttled
429
Se superó el límite de peticiones de la clave.Respeta la cabecera Retry-After y reintenta tras la ventana; sube de tier si lo necesitas.

428 vs 401

Recuerda: master_key_required es un 428, no un 401. Trátalo como "falta la master key", no como "sesión caducada": reintenta añadiendo la cabecera X-Master-Key, no renueves la API key.

Buenas prácticas

Seguridad

Recomendaciones para integrar PassFortress de forma segura.

Mínimo privilegio

Concede a cada clave solo los scopes que necesita. Evita apikeys:write salvo en aprovisionamiento.

Rotación periódica

Rota las claves con regularidad y tras cualquier sospecha de filtración. La rotación invalida el token anterior al instante.

Allowlist de IPs

Restringe cada clave a las IPs de tu backend. Las peticiones desde otras IPs reciben ip_not_allowed.

Nunca en el navegador

No incrustes claves ni la master key en JavaScript de cliente, apps móviles ni repositorios.

Separa entornos

Usa claves pf_test_ en desarrollo y CI, y pf_live_ solo en producción. Así una filtración en un entorno de pruebas no compromete los datos reales.