Saltar al contenido principal

Eliminar Clave de API

Expira (desactiva) una clave de API, evitando que sea usada para futuros requests de API. Esto también se llama "revocar" una clave.

Endpoint

DELETE /api/v1/api-keys/{api_key_id}

Autenticación

Requiere token JWT (autenticación Bearer).

Parámetros de Path

ParámetroTipoRequeridoDescripción
api_key_idUUIDEl ID de la clave de API a expirar

Response

Response Exitoso (200 OK)

{
"message": "Clave de API expirada exitosamente",
"success": true
}

Ejemplos

curl -X DELETE https://api.callcov.com/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Qué Sucede Cuando una Clave Expira

  1. Marcada como inactiva: is_active se establece en false
  2. Los requests de API fallan: La clave ya no puede autenticar
  3. No puede reactivarse: La expiración es permanente
  4. Se mantiene en historial: La clave permanece en la base de datos para propósitos de auditoría
  5. Libera un espacio: Permite crear nuevas claves (máx 10 activas)

Casos de Uso

Revocar Clave Comprometida

def revoke_compromised_key(access_token, key_id):
"""Revocar inmediatamente una clave de API comprometida"""
url = f"https://api.callcov.com/api/v1/api-keys/{key_id}"
headers = {"Authorization": f"Bearer {access_token}"}

response = requests.delete(url, headers=headers)

if response.status_code == 200:
print("⚠️ ¡Clave comprometida revocada!")
print("Crea una nueva clave y actualiza tus aplicaciones inmediatamente.")
return True
return False

Rotación de Claves

def rotate_api_key(access_token, old_key_id, description):
"""Rotar una clave de API: crear nueva, expirar vieja"""
# Crear nueva clave
create_url = "https://api.callcov.com/api/v1/api-keys/"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}

new_key_response = requests.post(
create_url,
headers=headers,
json={"description": description}
)

if new_key_response.status_code == 201:
new_key = new_key_response.json()['api_key']
print(f"✅ Nueva clave creada: {new_key}")
print("⚠️ ¡GUARDA ESTA CLAVE AHORA!")

# Expirar clave vieja
delete_url = f"https://api.callcov.com/api/v1/api-keys/{old_key_id}"
requests.delete(delete_url, headers=headers)

print("✅ Clave vieja expirada")
return new_key

return None

Limpiar Claves Sin Usar

def cleanup_unused_keys(access_token):
"""Expirar todas las claves de API que nunca se han usado"""
headers = {"Authorization": f"Bearer {access_token}"}

# Obtener todas las claves
list_url = "https://api.callcov.com/api/v1/api-keys/"
response = requests.get(list_url, headers=headers)

keys = response.json()['api_keys']
unused_keys = [k for k in keys if k['last_used_at'] is None]

print(f"Se encontraron {len(unused_keys)} claves sin usar")

for key in unused_keys:
# Pedir confirmación
confirm = input(f"¿Expirar {key['key_prefix']} ({key['description']})? (s/n): ")

if confirm.lower() == 's':
delete_url = f"https://api.callcov.com/api/v1/api-keys/{key['id']}"
requests.delete(delete_url, headers=headers)
print(f" ✅ Expirada {key['key_prefix']}")
else:
print(f" ⏭️ Omitida {key['key_prefix']}")

Errores

400 Bad Request

La clave ya está expirada:

{
"detail": "La clave de API ya está expirada"
}

404 Not Found

Clave de API no encontrada o no te pertenece:

{
"detail": "Clave de API no encontrada"
}

401 Unauthorized

Token JWT inválido o faltante:

{
"detail": "No se pudieron validar las credenciales"
}

Cuándo Expirar una Clave de API

✅ Expirar inmediatamente cuando:

  • Comprometida: La clave fue expuesta o filtrada
  • Ya no se necesita: La integración fue dada de baja
  • Rotación: Implementando política de rotación regular de claves
  • Brecha de seguridad: Medida de precaución
  • Offboarding de empleado: La clave fue usada por alguien que se fue

⏸️ Considerar antes de expirar:

  • En uso: Verificar last_used_at para ver si se usa activamente
  • Impacto: ¿Esto romperá alguna integración en ejecución?
  • Reemplazo: ¿Tienes una nueva clave lista?
  • Comunicación: ¿Notificaste a los miembros del equipo?

Mejores Prácticas

Rotación Regular de Claves

# Rotar claves cada 90 días
from datetime import datetime, timedelta

def should_rotate(key):
created = datetime.fromisoformat(key['created_at'].replace('Z', '+00:00'))
age = datetime.now(timezone.utc) - created
return age.days >= 90 # 90 días de antigüedad

# Verificar todas las claves
keys = get_api_keys(access_token)
for key in keys:
if should_rotate(key):
print(f"⚠️ La clave {key['key_prefix']} tiene {age.days} días - considerar rotar")

Proceso de Revocación de Emergencia

  1. Identificar clave comprometida (por prefijo o descripción)
  2. Revocar inmediatamente (no esperar)
  3. Crear nueva clave (si aún se necesita)
  4. Actualizar aplicaciones (deployar nueva clave)
  5. Verificar bloqueo de clave vieja (probar que falla)
  6. Documentar incidente (para auditoría de seguridad)

Antes de Expirar

# Verificar que la clave existe y está activa antes de expirar
def safe_expire_key(access_token, key_id):
"""Expirar una clave de forma segura con verificación"""
headers = {"Authorization": f"Bearer {access_token}"}

# Primero, verificar que la clave existe
list_url = "https://api.callcov.com/api/v1/api-keys/"
response = requests.get(list_url, headers=headers)

keys = response.json()['api_keys']
key_to_expire = next((k for k in keys if k['id'] == key_id), None)

if not key_to_expire:
print("❌ Clave no encontrada")
return False

if not key_to_expire['is_active']:
print("⚠️ La clave ya está expirada")
return False

print(f"A punto de expirar:")
print(f" Prefijo: {key_to_expire['key_prefix']}")
print(f" Descripción: {key_to_expire['description']}")
print(f" Creada: {key_to_expire['created_at']}")
print(f" Último uso: {key_to_expire['last_used_at'] or 'Nunca'}")

confirm = input("¿Proceder? (si/no): ")

if confirm.lower() == 'si':
delete_url = f"https://api.callcov.com/api/v1/api-keys/{key_id}"
response = requests.delete(delete_url, headers=headers)

if response.status_code == 200:
print("✅ Clave expirada exitosamente")
return True

print("❌ Expiración cancelada")
return False

Seguridad

  • Aislamiento de usuarios: Solo puedes expirar tus propias claves
  • Acción permanente: No se puede deshacer
  • Efecto inmediato: La clave deja de funcionar instantáneamente
  • Registro de auditoría: Las claves expiradas permanecen en el historial

Diferencia Entre Expirar y Eliminar

Este endpoint expira (elimina de forma lógica) la clave:

  • ✅ La clave se desactiva
  • ✅ La clave permanece en la base de datos
  • ✅ Se muestra en historial con include_expired=true
  • ✅ Se preserva el registro de auditoría

La clave NO se elimina permanentemente:

  • ❌ La clave no se elimina de la base de datos
  • ❌ No se puede reutilizar la misma clave

Relacionado