Autenticación y llaves de API
Cada solicitud lleva una llave de API en el header Authorization. Una llave pertenece a una cuenta de SpaceKru, tiene acceso de lectura, de lectura y escritura o de conversaciones con IA, y solo funciona mientras la cuenta esté en un plan de pago.
La API de SpaceKru (v1.1) es una API REST/JSON disponible en todos los planes de pago de SpaceKru. Sincroniza leads en ambos sentidos (upsert por external_id), añade leads a campañas, crea y mueve deals y pide seguimientos a los Empleados de IA, gestiona productos, cotizaciones, órdenes, pagos y reservas, permite que tu plataforma converse con tu Empleado de IA de Customer Service y envía webhooks firmados para 16 eventos que dicen quién causó cada cambio. URL base: https://app.spacekru.com/api/public/v1. Autenticación: llave de API Bearer (sk_live_…). Documentación: https://www.spacekru.com/es/developers. OpenAPI 3.1: https://app.spacekru.com/api/public/v1/openapi.json.
Llaves de API
Envía la llave como token Bearer en cada solicitud:
Authorization: Bearer sk_live_…- Formato:
sk_live_seguido de 43 caracteres. Cualquier otra cosa responde401sin llegar siquiera a nuestra base de datos. - Cada llave es real: no hay modo de prueba. Etiqueta los leads de prueba (por ejemplo
docs-test) para encontrarlos y quitarlos. - La llave se muestra una sola vez, al crearla. Solo guardamos un hash con clave, así que nadie, ni siquiera SpaceKru, puede volver a mostrártela.
- Hasta 5 llaves activas por cuenta. Nombra cada llave por la herramienta que la usa ("Zapier", "Formulario web", "Sincronización ERP") para revocar una sin tocar las demás.
Quién puede crear llaves
Solo el dueño de la cuenta, en Integraciones → API y Webhooks (nombres de hasta 100 caracteres). El mismo panel muestra el último uso de cada llave, las solicitudes de los últimos 30 días y cada suscripción de webhook.
Permisos (scopes)
- Solo lectura (
read): cadaGETexcepto webhooks y conversaciones. - Lectura y escritura (
read+write): todo lo que crea o cambia datos, y cada operación de/subscriptions. - Conversaciones con IA (
conversations, opcional, solo con lectura y escritura): las operaciones de/conversations. Gastan tokens de IA, por eso el dueño lo concede de forma explícita al crear la llave.
Una llave sin el permiso que necesita una operación responde 403 INSUFFICIENT_SCOPE.
| Operación | Qué hace | Scope | Módulo del plan |
|---|---|---|---|
GET /ping | Prueba la conexión | read | — |
GET /leads | Lista leads (del más reciente) | read | crm |
POST /leads | Crea un lead | write | crm |
GET /leads/{id} | Obtén un lead | read | crm |
PATCH /leads/{id} | Actualiza un lead | write | crm |
POST /leads/{id}/notes | Añade una nota a un lead | write | crm |
GET /deals | Lista deals | read | crm |
POST /deals | Crea un deal | write | crm |
GET /deals/{id} | Obtén un deal | read | crm |
PATCH /deals/{id} | Actualiza o mueve un deal | write | crm |
GET /quotes | Lista cotizaciones | read | sales_os |
POST /quotes | Crea una cotización | write | sales_os |
GET /quotes/{id} | Obtén una cotización | read | sales_os |
PATCH /quotes/{id} | Actualiza una cotización | write | sales_os |
GET /orders | Lista órdenes | read | sales_os |
POST /orders | Crea una orden | write | sales_os |
GET /orders/{id} | Obtén una orden | read | sales_os |
PATCH /orders/{id} | Actualiza una orden | write | sales_os |
GET /subscriptions | Lista las suscripciones de webhook | write | — |
POST /subscriptions | Suscribe una URL a un evento (REST Hook) | write | — |
DELETE /subscriptions/{id} | Desuscribe | write | — |
PUT /leads/external/{external_id} | Crea o actualiza un lead por tu id (upsert) | write | crm |
GET /agents | Lista los Empleados de IA | read | crm |
GET /pipeline-stages | Lista las etapas del pipeline | read | crm |
GET /campaigns | Lista campañas | read | crm |
GET /campaigns/{id} | Obtén una campaña | read | crm |
POST /campaigns/{id}/leads | Añade leads a una campaña | write | crm |
POST /deals/{id}/follow-ups | Pide al Empleado de IA un seguimiento | write | crm |
DELETE /deals/{id}/follow-ups | Cancela los seguimientos pendientes | write | crm |
GET /products | Lista productos | read | sales_os |
POST /products | Crea un producto | write | sales_os |
GET /products/{id} | Obtén un producto | read | sales_os |
PATCH /products/{id} | Actualiza un producto | write | sales_os |
PATCH /products/{id}/variants/{variant_id} | Actualiza una variante (precio, stock) | write | sales_os |
GET /bookings | Lista reservas | read | scheduler |
POST /bookings | Crea una reserva | write | scheduler |
GET /bookings/{id} | Obtén una reserva | read | scheduler |
PATCH /bookings/{id} | Reprograma o actualiza una reserva | write | scheduler |
GET /booking-resources | Lista los recursos de reserva | read | scheduler |
GET /availability | Horarios libres de un día | read | scheduler |
GET /conversations/availability | ¿Hay un Empleado de IA listo para conversar? | conversations | — |
POST /conversations | Abre una conversación | conversations | — |
GET /conversations/{id} | Obtén una conversación | conversations | — |
POST /conversations/{id}/messages | Envía el mensaje del cliente | conversations | — |
GET /conversations/{id}/messages | Lista los mensajes de una conversación | conversations | — |
POST /conversations/{id}/close | Cierra una conversación | conversations | — |
POST /quotes/{id}/convert | Convierte una cotización en orden | write | sales_os |
POST /orders/{id}/payments | Registra un pago | write | sales_os |
Módulos del plan (403)
Algunas operaciones necesitan un módulo de tu plan, el mismo que necesita la app: crm (leads, deals, campañas), sales_os (productos, cotizaciones, órdenes) y scheduler (reservas). Sin él la respuesta es 403 MODULE_NOT_AVAILABLE. Hoy todos los planes de pago incluyen los tres módulos.
Idioma de las respuestas
error.message llega en inglés o en español. Envía Accept-Language: es (o en) para elegir; sin él, se usa el idioma del perfil de la persona que creó la llave. El header Content-Language te dice cuál recibiste. Los code de error, los nombres de campos, los nombres de eventos y details[].issue nunca cambian: decide con ellos, no con el mensaje.
Requisitos del plan (402)
La API solo funciona mientras la cuenta esté en un plan de pago activo (cuenta la prueba gratuita de 7 días). Si no, cada solicitud responde 402 PLAN_UPGRADE_REQUIRED con uno de estos mensajes:
| Situación | Mensaje que recibes |
|---|---|
| Sin plan de pago | La API de SpaceKru solo está disponible en planes de pago. Mejora tu plan para conectar Zapier, Make o tu propio sistema. |
| Suscripción cancelada, impaga o vencida | Tu suscripción de SpaceKru no está activa. Actualiza tu método de pago para recuperar el acceso a la API. |
| El plan no incluye la API | Tu plan actual de SpaceKru no incluye acceso a la API. |
| Cuenta cerrada | Esta cuenta de SpaceKru está cerrada. |
- No hay periodo de gracia: un pago que falla pausa la API de inmediato (en aproximadamente un minuto).
- Las llaves no se borran. Cuando la cuenta vuelve a un plan de pago, las mismas llaves y webhooks funcionan de nuevo.
- Si no podemos verificar el plan por un momento, recibes
503 ELIGIBILITY_UNAVAILABLEconRetry-After: 10. Falla cerrado: reintenta, nunca es un pase libre.
401 vs 402 vs 403
| Estado | Código | Significado |
|---|---|---|
401 | INVALID_API_KEY | Sin llave, una llave mal formada, desconocida o revocada. La respuesta lleva WWW-Authenticate: Bearer. |
402 | PLAN_UPGRADE_REQUIRED | La llave es válida, pero la cuenta no está en un plan de pago activo. |
403 | INSUFFICIENT_SCOPE / MODULE_NOT_AVAILABLE | A la llave le falta el permiso (por ejemplo, una llave de solo lectura llamando POST /leads), o al plan le falta el módulo. |
Revocar y rotar llaves
- Revoca una llave en Integraciones → API y Webhooks. Deja de funcionar en la siguiente solicitud: no hay caché.
- Revocar una llave también desactiva las suscripciones de webhook que creó (
disabled_reason: "key_revoked"). Las de otras llaves no se tocan.
Para rotar una llave sin interrupciones:
- Crea una llave nueva con el mismo acceso.
- Ponla en tu integración (variable de entorno, conexión de Zapier/Make/n8n).
- Revisa en el panel que la llave nueva tenga un "Último uso" reciente.
- Revoca la llave vieja. Si creó webhooks, suscríbelos de nuevo con la llave nueva (y si usaban
exclude_own_changes, ahora omiten los cambios de la llave nueva).
Seguridad y buenas prácticas
- Solo de servidor a servidor. La API no envía headers CORS, así que los navegadores bloquean llamadas desde páginas web, a propósito. Una llave en código de front-end, una app móvil o un repositorio público puede ser robada; mantenla en un servidor o en tu herramienta de automatización.
- Guarda las llaves como secretos: variables de entorno, un gestor de secretos o el almacén de credenciales de Zapier, Make o n8n.
- Mínimo privilegio: una llave de Solo lectura para reportes; Conversaciones con IA solo para la llave de tu integración de chat.
- Una llave por integración, para que revocar una nunca rompa las demás.
- Verifica la firma de cada webhook y rechaza marcas de tiempo viejas: mira Verificar firmas.
- Aislamiento por cuenta: una llave solo puede ver su propia cuenta. Un id de otra cuenta responde
404igual que uno que no existe, también dentro de un body (lead_id,product_id…). - Los destinos de los webhooks están restringidos a hosts públicos
https://en el puerto 443, verificados en cada entrega; nunca se siguen redirecciones.
Ejemplo: comprueba una llave
curl "https://app.spacekru.com/api/public/v1/ping" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/ping', {
headers: {
Authorization: `Bearer ${process.env.SPACEKRU_API_KEY}`,
},
});
const body = await res.json();
if (!res.ok) {
// Branch on body.error.code; quote body.error.request_id to support.
throw new Error(`${body.error.code}: ${body.error.message} (request ${body.error.request_id})`);
}
console.log(body);import os
import requests
r = requests.get(
"https://app.spacekru.com/api/public/v1/ping",
headers={
"Authorization": f"Bearer {os.environ['SPACEKRU_API_KEY']}",
},
timeout=30,
)
body = r.json()
if not r.ok:
# Branch on body["error"]["code"]; quote body["error"]["request_id"] to support.
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']} (request {body['error']['request_id']})")
print(body)