Referencia de la API
Cada endpoint de la API de SpaceKru v1 (15 operaciones) con parámetros, respuestas, errores y ejemplos en curl, Node.js y Python, seguido de los objetos y los 16 eventos de webhook. Generada a partir del documento OpenAPI 3.1.
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.
URL base y autenticación
https://app.spacekru.com/api/public/v1
Authorization: Bearer $SPACEKRU_API_KEYTodas las solicitudes y respuestas son JSON (UTF-8). Las fechas son ISO 8601 en UTC. Las escrituras necesitan Content-Type: application/json. Mira Autenticación, Errores y Límites de uso. Las descripciones técnicas de cada campo vienen del documento OpenAPI y están en inglés.
Cuenta
Test a key.
Prueba la conexión
GET /ping · Scope: read
Returns the organization the key belongs to, its plan and the key's scopes. Use it to test a new key.
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— Authenticated. DevuelvePing.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
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){
"object": "ping",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"name": "Acme Corp",
"plan": "Business",
"scopes": [
"read",
"write"
]
}Leads
Create, update, read and annotate leads.
Lista leads (del más reciente)
GET /leads · Scope: read
Newest first (by created_at, then id). Filters combine with AND. Follow next_cursor until has_more is false. Internal records SpaceKru keeps for its own team are never listed.
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
limit | query | integer | Page size, 1 to 100. Default 25. 1 to 100. Default 25. |
cursor | query | string | The next_cursor of the previous page. Opaque: do not build or edit it (an invalid cursor answers 400). Max 500 characters. |
updated_since | query | string (date-time) | Only records whose updated_at is at or after this time. ISO 8601 with a time zone, e.g. 2026-10-19T00:00:00Z. |
status | query | string | Exact match on status. Common values: new, contacted, qualified, proposal, won. Max 50 characters. |
type | query | string | Only people or only businesses. One of: person, business. |
external_id | query | string | Exact match on the external_id of the lead (the id in your own system). Max 200 characters. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— A page of leads.400—INVALID_REQUEST— a query parameter is invalid (unknown parameter, badlimit,updated_sinceorcursor).401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/leads" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/leads', {
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/leads",
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){
"data": [
{
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Operations Manager",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}
],
"has_more": true,
"next_cursor": "eyJ0IjoiMjAyNi0xMC0xOVQxNDozMjoxMC4wMDBaIiwiaSI6IjNmMmI4YzFlLTlhNGQtNGU2Zi1iN2E4LTFjMmQzZTRmNWE2YiJ9"
}Crea un lead
POST /leads · Scope: write
At least one contact channel is required: work_email, personal_email, work_phone, personal_phone, instagram_profile, linkedin_profile_url.
The lead is created with source = "api" (always) and type = "person" unless you send type. Blank strings and null are treated as "not sent" (no-code tools send "" for unmapped fields). Unknown fields are rejected with 400.
Duplicates: if an email or phone you send matches an existing lead, the API answers 409 DUPLICATE_RESOURCE with existing_id and creates nothing. When you send no email and no phone, the match is made on the social profile instead. An Instagram or LinkedIn URL sent in website is also stored in its own profile field.
initial_note is added to the lead's timeline, signed by the user who created the API key. If the note cannot be saved, the lead is still created.
Idempotency: send Idempotency-Key (up to 255 characters) to make retries safe. Same key and same body within 24 h: you get the original 201 again with Idempotent-Replayed: true. Same key, different body: 409 IDEMPOTENCY_CONFLICT. Only successful responses are stored: after an error you can retry with the same key.
Creating a lead does not make an AI Employee contact it: add the lead to a campaign in SpaceKru for that.
Scope: write (a read-only key answers 403 INSUFFICIENT_SCOPE).
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
Idempotency-Key | header | string | Any unique string per logical request, e.g. a UUID or your form response id. Max 255 characters. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Body de la solicitud (LeadCreate) — Requires at least one of: work_email, personal_email, work_phone, personal_phone, instagram_profile, linkedin_profile_url. Unknown fields are rejected. Los campos desconocidos se rechazan.
| Campo | Tipo | Descripción |
|---|---|---|
full_name | string | Max 200 characters. |
business_name | string | Max 200 characters. |
job_title | string | Max 200 characters. |
work_email | string (email) | Max 320 characters. |
personal_email | string (email) | Max 320 characters. |
work_phone | string | Max 40 characters. |
personal_phone | string | Max 40 characters. |
company_name | string | Max 200 characters. |
city | string | Max 120 characters. |
country | string | Max 120 characters. |
website | string | Max 500 characters. |
instagram_profile | string | Max 300 characters. |
linkedin_profile_url | string | Max 500 characters. |
type | string | One of: person, business. |
tags | array of string | Up to 20 tags of up to 50 characters. A comma-separated string is also accepted (e.g. "typeform, vip"). Up to 20 items. |
initial_note | string | Added to the lead's timeline. Max 5000 characters. |
external_id | string | Your id for this lead (unique per organization). Max 200 characters. |
campaign_id | string (uuid) | Add the new lead to this prospecting or recontact campaign. |
Respuestas
201— Created. On a replay, the original response. Withcampaign_id, it also hasenrollment. Devuelve Lead.400—INVALID_REQUEST— validation failed (details[]lists up to 20 fields), the body is not valid JSON, orContent-Typeis notapplication/json.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.409—DUPLICATE_RESOURCE(a matching lead exists: seeexisting_id) orIDEMPOTENCY_CONFLICT.413—PAYLOAD_TOO_LARGE— the body is over 1 MB.422—UNPROCESSABLE— valid JSON that could not be applied (e.g. too many active subscriptions, a value the database rejected).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl -X POST "https://app.spacekru.com/api/public/v1/leads" \
-H "Authorization: Bearer $SPACEKRU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: typeform-resp-8f2a1c" \
-d '{
"full_name": "Maria Lopez",
"work_email": "maria.lopez@example.com",
"work_phone": "+13055550142",
"company_name": "Acme Corp",
"job_title": "Operations Manager",
"city": "Miami",
"country": "United States",
"tags": [
"typeform",
"spring-promo"
],
"external_id": "crm-88412",
"initial_note": "Asked for a demo from the spring promo form."
}'const res = await fetch('https://app.spacekru.com/api/public/v1/leads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SPACEKRU_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'typeform-resp-8f2a1c',
},
body: JSON.stringify({
"full_name": "Maria Lopez",
"work_email": "maria.lopez@example.com",
"work_phone": "+13055550142",
"company_name": "Acme Corp",
"job_title": "Operations Manager",
"city": "Miami",
"country": "United States",
"tags": [
"typeform",
"spring-promo"
],
"external_id": "crm-88412",
"initial_note": "Asked for a demo from the spring promo form."
}),
});
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.post(
"https://app.spacekru.com/api/public/v1/leads",
headers={
"Authorization": f"Bearer {os.environ['SPACEKRU_API_KEY']}",
"Idempotency-Key": "typeform-resp-8f2a1c",
},
json={
"full_name": "Maria Lopez",
"work_email": "maria.lopez@example.com",
"work_phone": "+13055550142",
"company_name": "Acme Corp",
"job_title": "Operations Manager",
"city": "Miami",
"country": "United States",
"tags": ["typeform", "spring-promo"],
"external_id": "crm-88412",
"initial_note": "Asked for a demo from the spring promo form.",
},
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){
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Operations Manager",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}Obtén un lead
GET /leads/{id} · Scope: read
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The lead id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— The lead. Devuelve Lead.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b', {
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/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
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){
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Operations Manager",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}Actualiza un lead
PATCH /leads/{id} · Scope: write
Partial update: send only the fields to change (at least one). Send null or "" to clear a field.
tags: the array you send replaces the current tags; null removes them all; "" is ignored.
status and source are managed by SpaceKru and cannot be written (they are rejected as unknown fields).
Updates do NOT check for duplicates by email or phone. external_id must stay unique: another lead with the same one answers 409 with existing_id.
Scope: write (a read-only key answers 403 INSUFFICIENT_SCOPE).
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The lead id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Body de la solicitud (LeadUpdate) — Only the fields you send change. null or "" clears a field. status and source are not writable. Los campos desconocidos se rechazan.
| Campo | Tipo | Descripción |
|---|---|---|
full_name | string | Max 200 characters. |
business_name | string | Max 200 characters. |
job_title | string | Max 200 characters. |
work_email | string (email) | Max 320 characters. |
personal_email | string (email) | Max 320 characters. |
work_phone | string | Max 40 characters. |
personal_phone | string | Max 40 characters. |
company_name | string | Max 200 characters. |
city | string | Max 120 characters. |
country | string | Max 120 characters. |
website | string | Max 500 characters. |
instagram_profile | string | Max 300 characters. |
linkedin_profile_url | string | Max 500 characters. |
type | string | One of: person, business. |
tags | array of string | Up to 20 tags of up to 50 characters. A comma-separated string is also accepted (e.g. "typeform, vip"). Up to 20 items. |
external_id | string or null | Your id for this lead. Another lead with the same one answers 409. Max 200 characters. |
Respuestas
200— Updated. Devuelve Lead.400—INVALID_REQUEST— validation failed (details[]lists up to 20 fields), the body is not valid JSON, orContent-Typeis notapplication/json.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).409—DUPLICATE_RESOURCE— another lead already uses thisexternal_id(seeexisting_id).413—PAYLOAD_TOO_LARGE— the body is over 1 MB.422—UNPROCESSABLE— valid JSON that could not be applied (e.g. too many active subscriptions, a value the database rejected).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl -X PATCH "https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b" \
-H "Authorization: Bearer $SPACEKRU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"job_title": "Head of Operations",
"tags": [
"typeform",
"spring-promo",
"vip"
]
}'const res = await fetch('https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.SPACEKRU_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"job_title": "Head of Operations",
"tags": [
"typeform",
"spring-promo",
"vip"
]
}),
});
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.patch(
"https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
headers={
"Authorization": f"Bearer {os.environ['SPACEKRU_API_KEY']}",
},
json={
"job_title": "Head of Operations",
"tags": ["typeform", "spring-promo", "vip"],
},
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){
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Head of Operations",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo",
"vip"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T15:02:44.000Z"
}Añade una nota a un lead
POST /leads/{id}/notes · Scope: write
Adds a note to the lead's timeline in SpaceKru, signed by the user who created the API key (or the account owner if that user no longer exists).
Scope: write (a read-only key answers 403 INSUFFICIENT_SCOPE).
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The lead id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Body de la solicitud Los campos desconocidos se rechazan.
| Campo | Tipo | Descripción |
|---|---|---|
content (required) | string | Max 5000 characters. |
Respuestas
201— Created. Devuelve Note.400—INVALID_REQUEST— validation failed (details[]lists up to 20 fields), the body is not valid JSON, orContent-Typeis notapplication/json.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).413—PAYLOAD_TOO_LARGE— the body is over 1 MB.422—UNPROCESSABLE— valid JSON that could not be applied (e.g. too many active subscriptions, a value the database rejected).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl -X POST "https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b/notes" \
-H "Authorization: Bearer $SPACEKRU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Called back: wants pricing for 3 locations."
}'const res = await fetch('https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b/notes', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SPACEKRU_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"content": "Called back: wants pricing for 3 locations."
}),
});
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.post(
"https://app.spacekru.com/api/public/v1/leads/3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b/notes",
headers={
"Authorization": f"Bearer {os.environ['SPACEKRU_API_KEY']}",
},
json={
"content": "Called back: wants pricing for 3 locations.",
},
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){
"object": "note",
"id": "5d6e7f80-91a2-4b3c-8d4e-5f6a7b8c9d0e",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"content": "Called back: wants pricing for 3 locations.",
"created_at": "2026-10-19T15:10:03.000Z"
}Deals
Read-only in v1.
Lista deals
GET /deals · Scope: read
Newest first (by created_at, then id). Filters combine with AND. Follow next_cursor until has_more is false.
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
limit | query | integer | Page size, 1 to 100. Default 25. 1 to 100. Default 25. |
cursor | query | string | The next_cursor of the previous page. Opaque: do not build or edit it (an invalid cursor answers 400). Max 500 characters. |
updated_since | query | string (date-time) | Only records whose updated_at is at or after this time. ISO 8601 with a time zone, e.g. 2026-10-19T00:00:00Z. |
status | query | string | Exact match on status. Common values: open, won, lost. Max 50 characters. |
external_id | query | string | Exact match on the external_id of the deal (the id in your own system). Max 200 characters. |
lead_id | query | string (uuid) | Only the deals of this lead. |
stage_id | query | string (uuid) | Only the deals in this pipeline stage (see GET /pipeline-stages). |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— A page of deals.400—INVALID_REQUEST— a query parameter is invalid (unknown parameter, badlimit,updated_sinceorcursor).401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/deals" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/deals', {
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/deals",
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){
"data": [
{
"object": "deal",
"id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"title": "Acme Corp — 3 locations",
"value": 4800,
"stage": "Proposal",
"status": "open",
"close_date": "2026-11-30",
"lost_reason": null,
"stage_id": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
"agent_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5e",
"external_id": "opp-5521",
"next_step_action": "Follow-up scheduled (WhatsApp)",
"next_step_date": "2026-10-24T15:00:00.000Z",
"created_at": "2026-10-20T09:15:00.000Z",
"updated_at": "2026-10-22T16:40:12.000Z"
}
],
"has_more": false,
"next_cursor": null
}Obtén un deal
GET /deals/{id} · Scope: read
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The deal id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— The deal. Devuelve Deal.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/deals/8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/deals/8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f', {
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/deals/8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
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){
"object": "deal",
"id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"title": "Acme Corp — 3 locations",
"value": 4800,
"stage": "Proposal",
"status": "open",
"close_date": "2026-11-30",
"lost_reason": null,
"stage_id": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
"agent_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5e",
"external_id": "opp-5521",
"next_step_action": "Follow-up scheduled (WhatsApp)",
"next_step_date": "2026-10-24T15:00:00.000Z",
"created_at": "2026-10-20T09:15:00.000Z",
"updated_at": "2026-10-22T16:40:12.000Z"
}Cotizaciones
Read-only in v1. Quotes include their line items.
Lista cotizaciones
GET /quotes · Scope: read
Newest first (by created_at, then id). Filters combine with AND. Follow next_cursor until has_more is false.
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
limit | query | integer | Page size, 1 to 100. Default 25. 1 to 100. Default 25. |
cursor | query | string | The next_cursor of the previous page. Opaque: do not build or edit it (an invalid cursor answers 400). Max 500 characters. |
updated_since | query | string (date-time) | Only records whose updated_at is at or after this time. ISO 8601 with a time zone, e.g. 2026-10-19T00:00:00Z. |
status | query | string | Exact match on status. Common values: draft, sent, accepted, rejected. Max 50 characters. |
external_id | query | string | Exact match on the external_id of the quote (the id in your own system). Max 200 characters. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— A page of quotes.400—INVALID_REQUEST— a query parameter is invalid (unknown parameter, badlimit,updated_sinceorcursor).401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/quotes" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/quotes', {
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/quotes",
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){
"data": [
{
"object": "quote",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"number": "Q-1042",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "sent",
"subtotal": 1200,
"tax_total": 84,
"shipping_cost": 0,
"discount_total": 0,
"total": 1284,
"valid_until": "2026-11-15",
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "qt-7781",
"notes": "Prices valid for the 2026 rollout.",
"payment_terms": "Net 15",
"created_at": "2026-10-22T16:45:00.000Z",
"updated_at": "2026-10-22T16:47:31.000Z"
}
],
"has_more": false,
"next_cursor": null
}Obtén una cotización
GET /quotes/{id} · Scope: read
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The quote id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— The quote. Devuelve Quote.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/quotes/1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/quotes/1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d', {
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/quotes/1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
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){
"object": "quote",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"number": "Q-1042",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "sent",
"subtotal": 1200,
"tax_total": 84,
"shipping_cost": 0,
"discount_total": 0,
"total": 1284,
"valid_until": "2026-11-15",
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "qt-7781",
"notes": "Prices valid for the 2026 rollout.",
"payment_terms": "Net 15",
"created_at": "2026-10-22T16:45:00.000Z",
"updated_at": "2026-10-22T16:47:31.000Z"
}Órdenes
Read-only in v1. Orders include their line items and payment status.
Lista órdenes
GET /orders · Scope: read
Newest first (by created_at, then id). Filters combine with AND. Follow next_cursor until has_more is false. Archived orders are NOT listed (you can still retrieve them by id, and webhooks report them).
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
limit | query | integer | Page size, 1 to 100. Default 25. 1 to 100. Default 25. |
cursor | query | string | The next_cursor of the previous page. Opaque: do not build or edit it (an invalid cursor answers 400). Max 500 characters. |
updated_since | query | string (date-time) | Only records whose updated_at is at or after this time. ISO 8601 with a time zone, e.g. 2026-10-19T00:00:00Z. |
status | query | string | Exact match on status. Common values: draft, pending, processing, shipped, delivered, cancelled. Max 50 characters. |
external_id | query | string | Exact match on the external_id of the order (the id in your own system). Max 200 characters. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— A page of orders.400—INVALID_REQUEST— a query parameter is invalid (unknown parameter, badlimit,updated_sinceorcursor).401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/orders" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/orders', {
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/orders",
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){
"data": [
{
"object": "order",
"id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
"number": "ORD-2088",
"quote_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "processing",
"payment_status": "paid",
"subtotal": 1200,
"tax": 84,
"shipping_cost": 0,
"total": 1284,
"tracking_number": null,
"carrier": null,
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "erp-so-3310",
"delivery_type": "pickup",
"shipping_address": null,
"billing_address": null,
"notes": null,
"amount_paid": 1284,
"created_at": "2026-10-24T10:05:00.000Z",
"updated_at": "2026-10-24T18:20:09.000Z"
}
],
"has_more": false,
"next_cursor": null
}Obtén una orden
GET /orders/{id} · Scope: read
Archived orders are returned too.
Scope: read.
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The order id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— The order. Devuelve Order.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/orders/9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/orders/9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a', {
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/orders/9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
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){
"object": "order",
"id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
"number": "ORD-2088",
"quote_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "processing",
"payment_status": "paid",
"subtotal": 1200,
"tax": 84,
"shipping_cost": 0,
"total": 1284,
"tracking_number": null,
"carrier": null,
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "erp-so-3310",
"delivery_type": "pickup",
"shipping_address": null,
"billing_address": null,
"notes": null,
"amount_paid": 1284,
"created_at": "2026-10-24T10:05:00.000Z",
"updated_at": "2026-10-24T18:20:09.000Z"
}Suscripciones de webhook
Subscribe a URL to real-time events (REST Hooks). Every operation requires the write scope.
Lista las suscripciones de webhook
GET /subscriptions · Scope: write
Every webhook subscription of the organization — active and deactivated, created by any key or tool — newest first, up to 200. has_more is always false. Never includes the secret.
Scope: write (a read-only key answers 403 INSUFFICIENT_SCOPE).
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— Subscriptions.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl "https://app.spacekru.com/api/public/v1/subscriptions" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/subscriptions', {
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/subscriptions",
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){
"data": [
{
"object": "subscription",
"id": "0a1b2c3d-4e5f-4a6b-9c7d-8e9f0a1b2c3d",
"event_type": "lead.created",
"target_url": "https://hooks.zapier.com/hooks/catch/1234567/abcdefg/",
"is_active": true,
"failure_count": 0,
"disabled_reason": null,
"last_success_at": null,
"created_at": "2026-10-19T14:40:00.000Z",
"exclude_own_changes": false
}
],
"has_more": false,
"next_cursor": null
}Suscribe una URL a un evento (REST Hook)
POST /subscriptions · Scope: write
The secret used to sign deliveries is returned ONLY in this response. Store it now.
target_url rules: https:// only, default port 443 only, no user or password in the URL, a public host (no localhost, .local, .internal or private/reserved IP addresses — checked again on every delivery). Redirects are never followed. Your endpoint must answer within 8 seconds.
At most 50 active subscriptions per organization (422 beyond that). One subscription = one event and one URL: subscribe the same URL several times for several events.
No backfill: only events that happen after you subscribe are sent. To load history, list the resource once.
If the key that created a subscription is revoked, the subscription is deactivated (disabled_reason: "key_revoked").
Scope: write (a read-only key answers 403 INSUFFICIENT_SCOPE).
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Body de la solicitud (SubscriptionCreate) Los campos desconocidos se rechazan.
| Campo | Tipo | Descripción |
|---|---|---|
event_type (required) | string | One of: lead.created, lead.updated, deal.created, deal.stage_changed, deal.updated, quote.created, quote.status_changed, order.created, order.status_changed, campaign_lead.replied, campaign_lead.form_submitted, product.created, product.updated, booking.created, booking.updated, conversation.message_created. |
target_url (required) | string (uri) | Public https:// URL on port 443, without credentials. Internal hosts and private IP addresses are rejected. Redirects are not followed. Max 2000 characters. |
exclude_own_changes | boolean | Do not deliver changes made with THIS API key. Turn it on when your platform both writes through the API and listens to the same events. Default false. |
Respuestas
201— Created. The only response that includessecret. DevuelveSubscriptionCreated.400—INVALID_REQUEST— validation failed (details[]lists up to 20 fields), the body is not valid JSON, orContent-Typeis notapplication/json.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.413—PAYLOAD_TOO_LARGE— the body is over 1 MB.422—UNPROCESSABLE— valid JSON that could not be applied (e.g. too many active subscriptions, a value the database rejected).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl -X POST "https://app.spacekru.com/api/public/v1/subscriptions" \
-H "Authorization: Bearer $SPACEKRU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_type": "lead.created",
"target_url": "https://hooks.zapier.com/hooks/catch/1234567/abcdefg/"
}'const res = await fetch('https://app.spacekru.com/api/public/v1/subscriptions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SPACEKRU_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"event_type": "lead.created",
"target_url": "https://hooks.zapier.com/hooks/catch/1234567/abcdefg/"
}),
});
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.post(
"https://app.spacekru.com/api/public/v1/subscriptions",
headers={
"Authorization": f"Bearer {os.environ['SPACEKRU_API_KEY']}",
},
json={
"event_type": "lead.created",
"target_url": "https://hooks.zapier.com/hooks/catch/1234567/abcdefg/",
},
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){
"object": "subscription",
"id": "0a1b2c3d-4e5f-4a6b-9c7d-8e9f0a1b2c3d",
"event_type": "lead.created",
"target_url": "https://hooks.zapier.com/hooks/catch/1234567/abcdefg/",
"is_active": true,
"failure_count": 0,
"disabled_reason": null,
"last_success_at": null,
"created_at": "2026-10-19T14:40:00.000Z",
"exclude_own_changes": false,
"secret": "whsec_EXAMPLE0nlyN0tARealSecretDoNotUseIt00000"
}Desuscribe
DELETE /subscriptions/{id} · Scope: write
Deletes the subscription permanently (any subscription of the organization, active or deactivated). Retries still pending for it are cancelled.
Scope: write (a read-only key answers 403 INSUFFICIENT_SCOPE).
| Parámetro | En | Tipo | Descripción |
|---|---|---|---|
id (required) | path | string (uuid) | The subscription id. An id that is not a UUID, or that belongs to another organization, answers 404. |
Accept-Language | header | string | Language of error.message: en or es. Without it, the language of the profile of the person who created the key. Codes and field names never change. |
Respuestas
200— Deleted.401—INVALID_API_KEY— the key is missing, malformed, unknown or revoked.402—PLAN_UPGRADE_REQUIRED— the organization is not on an active paid plan (free plan, cancelled or unpaid subscription, closed account). No grace period.403—INSUFFICIENT_SCOPE— the key lacks the scope this operation needs.404—NOT_FOUND— no such record in this organization (also for ids that are not UUIDs).429—RATE_LIMITED— over 120 requests per minute or 10,000 per day for this key (defaults). WaitRetry-Afterseconds.500—INTERNAL_ERROR— unexpected error on our side. Safe to retry with backoff; quoterequest_idto support if it persists.503—SERVICE_BUSY(the server is at capacity) orELIGIBILITY_UNAVAILABLE(your plan could not be verified). Retry afterRetry-Afterseconds (5 or 10).
Ejemplo de solicitud
curl -X DELETE "https://app.spacekru.com/api/public/v1/subscriptions/0a1b2c3d-4e5f-4a6b-9c7d-8e9f0a1b2c3d" \
-H "Authorization: Bearer $SPACEKRU_API_KEY"const res = await fetch('https://app.spacekru.com/api/public/v1/subscriptions/0a1b2c3d-4e5f-4a6b-9c7d-8e9f0a1b2c3d', {
method: 'DELETE',
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.delete(
"https://app.spacekru.com/api/public/v1/subscriptions/0a1b2c3d-4e5f-4a6b-9c7d-8e9f0a1b2c3d",
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){
"object": "subscription",
"id": "0a1b2c3d-4e5f-4a6b-9c7d-8e9f0a1b2c3d",
"deleted": true
}Objetos
Lead
| Campo | Tipo | Descripción |
|---|---|---|
object | "lead" | — |
id | string (uuid) | — |
type | string | One of: person, business. |
full_name | string or null | — |
business_name | string or null | — |
job_title | string or null | — |
work_email | string or null | — |
personal_email | string or null | — |
work_phone | string or null | — |
personal_phone | string or null | — |
company_name | string or null | — |
city | string or null | — |
country | string or null | — |
status | string or null | Managed by SpaceKru (read-only). Free text; common values: new, contacted, qualified, proposal, won. |
source | string or null | Where the lead came from (read-only). api for leads created through the API. |
tags | array of string | — |
instagram_profile | string or null | — |
linkedin_profile_url | string or null | — |
website | string or null | — |
external_id | string or null | The id of this lead in YOUR system (unique per organization). Use it to sync without storing SpaceKru ids. |
created_at | string (date-time) or null | When the lead was created (UTC). |
updated_at | string (date-time) or null | Last change (UTC). Used by updated_since. |
Deal
| Campo | Tipo | Descripción |
|---|---|---|
object | "deal" | — |
id | string (uuid) | — |
lead_id | string or null | — |
title | string or null | — |
value | number or null | — |
stage | string or null | Name of the pipeline stage. |
status | string or null | Free text; common values: open, won, lost. |
close_date | string or null | Calendar date YYYY-MM-DD or a date-time. |
lost_reason | string or null | — |
stage_id | string or null | Id of the pipeline stage (see GET /pipeline-stages). The reliable way to know and change the stage. |
agent_id | string or null | The AI Employee in charge of the deal (it does the follow-ups). |
external_id | string or null | The id of this deal in YOUR system. |
next_step_action | string or null | Next scheduled step, e.g. "Follow-up scheduled (WhatsApp)". |
next_step_date | string (date-time) or null | When the next step is scheduled (UTC). |
created_at | string (date-time) or null | When the deal was created (UTC). |
updated_at | string (date-time) or null | Last change (UTC). |
Cotización
| Campo | Tipo | Descripción |
|---|---|---|
object | "quote" | — |
id | string (uuid) | — |
number | string or null | — |
lead_id | string or null | — |
deal_id | string or null | — |
status | string or null | Free text; common values: draft, sent, accepted, rejected. |
subtotal | number or null | — |
tax_total | number or null | — |
shipping_cost | number or null | — |
discount_total | number or null | — |
total | number or null | — |
valid_until | string or null | Calendar date, YYYY-MM-DD. |
items | array of LineItem | — |
external_id | string or null | The id of this quote in YOUR system. |
notes | string or null | — |
payment_terms | string or null | — |
created_at | string (date-time) or null | When the quote was created (UTC). |
updated_at | string (date-time) or null | Last change (UTC). |
Orden
| Campo | Tipo | Descripción |
|---|---|---|
object | "order" | — |
id | string (uuid) | — |
number | string or null | — |
quote_id | string or null | — |
lead_id | string or null | — |
deal_id | string or null | — |
status | string or null | Free text; common values: draft, pending, processing, shipped, delivered, cancelled. |
payment_status | string or null | Free text; common values: unpaid, partial, paid, refunded. |
subtotal | number or null | — |
tax | number or null | — |
shipping_cost | number or null | — |
total | number or null | — |
tracking_number | string or null | — |
carrier | string or null | — |
items | array of LineItem | — |
external_id | string or null | The id of this order in YOUR system. |
delivery_type | string or null | pickup or delivery. |
shipping_address | object or null | — |
billing_address | object or null | — |
notes | string or null | — |
amount_paid | number | Sum of the recorded payments. |
created_at | string (date-time) or null | When the order was created (UTC). |
updated_at | string (date-time) or null | Last change (UTC). |
Ítem
| Campo | Tipo | Descripción |
|---|---|---|
product_id | string or null | — |
name | string or null | — |
sku | string or null | — |
description | string or null | — |
quantity | number or null | — |
unit_price | number or null | — |
total | number or null | — |
variant_id | string or null | — |
tax_rate | number or null | Percent. |
discount | number or null | Percent. |
Pago
| Campo | Tipo | Descripción |
|---|---|---|
object | "payment" | — |
id | string (uuid) | — |
order_id | string (uuid) | — |
amount | number | — |
payment_method | string or null | — |
reference_code | string or null | — |
payment_date | string (date-time) or null | When it was paid. |
created_at | string (date-time) or null | When it was recorded. |
order_payment_status | string or null | unpaid, partial or paid after this payment. |
Producto
| Campo | Tipo | Descripción |
|---|---|---|
object | "product" | — |
id | string (uuid) | — |
name | string | — |
sku | string or null | — |
reference_code | string or null | — |
category | string or null | — |
description | string or null | — |
brand | string or null | — |
price | number or null | — |
sale_price | number or null | — |
currency | string or null | ISO 4217, e.g. USD, COP, MXN. |
stock_quantity | integer or null | — |
track_stock | boolean | — |
status | string | One of: active, draft, archived. |
images | array of string | — |
url | string or null | — |
min_order_quantity | integer or null | — |
has_variants | boolean | — |
variants | array of ProductVariant | — |
external_id | string or null | — |
created_at | string (date-time) or null | Created (UTC). |
updated_at | string (date-time) or null | Last change (UTC). |
Variante de producto
| Campo | Tipo | Descripción |
|---|---|---|
object | "product_variant" | — |
id | string (uuid) | — |
sku | string or null | — |
title | string or null | — |
price | number or null | — |
stock_quantity | integer or null | — |
attributes | object or null | — |
Empleado de IA
| Campo | Tipo | Descripción |
|---|---|---|
object | "agent" | — |
id | string (uuid) | — |
name | string | — |
role | string | customer_service, prospector, community_manager… |
status | string or null | — |
language | string or null | — |
channels | array of string | Channels turned on: whatsapp, email, web_chat, instagram… |
Etapa del pipeline
| Campo | Tipo | Descripción |
|---|---|---|
object | "pipeline_stage" | — |
id | string (uuid) | — |
name | string | — |
type | string | One of: OPEN, WON, LOST. |
position | integer | — |
Campaña
| Campo | Tipo | Descripción |
|---|---|---|
object | "campaign" | — |
id | string (uuid) | — |
name | string or null | — |
type | string | prospecting, recontact, inbound, email_marketing, whatsapp_marketing… |
status | string | — |
agent_id | string or null | — |
channels | array of string | — |
enrollable | boolean | true = leads can be added with POST /campaigns/{id}/leads. |
created_at | string (date-time) or null | When the campaign was created (UTC). |
Inscripción
| Campo | Tipo | Descripción |
|---|---|---|
object | "enrollment" | — |
campaign_id | string (uuid) | — |
dry_run | boolean | — |
requested | integer | — |
enrolled | integer | Added (or that would be added, on a dry run). |
already_in_campaign | integer | — |
excluded | integer | — |
excluded_lead_ids | array of string (uuid) | — |
reasons | object | — |
Lead de campaña
A lead inside one of your campaigns. Only delivered by the campaign_lead.* webhooks.
| Campo | Tipo | Descripción |
|---|---|---|
object | "campaign_lead" | — |
id | string (uuid) | — |
campaign_id | string (uuid) | — |
lead_id | string or null | — |
status | string or null | Status inside the campaign. Free text; common values: new, contacted, replied, awaiting_supervisor, converted, not_interested, closed_unresponsive. |
form_submitted_at | string (date-time) or null | When the lead submitted the campaign form, if it did. |
lead | Lead or any or null | The lead itself. |
Seguimiento
| Campo | Tipo | Descripción |
|---|---|---|
object | "follow_up" | — |
deal_id | string (uuid) | — |
scheduled_message_id | string (uuid) | — |
scheduled_at | string (date-time) | Adjusted to the working hours of the AI Employee. |
channel | string | One of: whatsapp, email. |
Recurso de reserva
| Campo | Tipo | Descripción |
|---|---|---|
object | "booking_resource" | — |
id | string (uuid) | — |
name | string | — |
capacity | integer or null | — |
category | string or null | — |
zone | string or null | — |
is_active | boolean | — |
min_slot_duration | integer or null | Minutes. |
Horario disponible
| Campo | Tipo | Descripción |
|---|---|---|
object | "availability_slot" | — |
start_time | string (date-time) | — |
end_time | string (date-time) | — |
resource | object | — |
Reserva
| Campo | Tipo | Descripción |
|---|---|---|
object | "booking" | — |
id | string (uuid) | — |
resource_id | string or null | — |
lead_id | string or null | — |
start_time | string (date-time) | — |
end_time | string (date-time) | — |
status | string | One of: confirmed, cancelled, seated, completed. |
party_size | integer or null | — |
notes | string or null | — |
meeting_url | string or null | — |
external_id | string or null | — |
created_at | string (date-time) or null | Created (UTC). |
updated_at | string (date-time) or null | Last change (UTC). |
Conversación
| Campo | Tipo | Descripción |
|---|---|---|
object | "conversation" | — |
id | string (uuid) | — |
lead_id | string or null | — |
status | string | One of: open, closed. |
external_conversation_id | string or null | — |
locale | string or null | — |
message_count | integer | — |
last_activity_at | string (date-time) or null | Last message (UTC). |
created_at | string (date-time) or null | Opened (UTC). |
Mensaje de conversación
| Campo | Tipo | Descripción |
|---|---|---|
object | "conversation_message" | — |
id | string (uuid) | — |
conversation_id | string (uuid) | — |
role | string | contact = your customer, agent = the AI Employee, human = a person replying from the SpaceKru Inbox. One of: contact, agent, human. |
text | string or null | — |
created_at | string (date-time) or null | When it was written (UTC). |
Turno de conversación
| Campo | Tipo | Descripción |
|---|---|---|
object | "conversation_turn" | — |
conversation_id | string (uuid) | — |
messages | array of ConversationMessage | The AI Employee's replies to this message (it can send more than one). |
deferred | boolean | true = no reply now. |
deferred_reason | string or null | One of: no_inbound_campaign, agent_not_ready, replies_disabled, message_filtered, campaign_inactive, no_reply. |
Suscripción
| Campo | Tipo | Descripción |
|---|---|---|
object | "subscription" | — |
id | string (uuid) | — |
event_type | string | One of: lead.created, lead.updated, deal.created, deal.stage_changed, deal.updated, quote.created, quote.status_changed, order.created, order.status_changed, campaign_lead.replied, campaign_lead.form_submitted, product.created, product.updated, booking.created, booking.updated, conversation.message_created. |
target_url | string | — |
is_active | boolean | false once deactivated (see disabled_reason). A deactivated subscription is never reactivated: delete it and create a new one. |
failure_count | integer | Consecutive failed delivery attempts. Any success resets it to 0. |
disabled_reason | string or null | Why it was deactivated: target_gone_410 — Your endpoint answered 410 Gone. disabled_after_20_consecutive_failures — Too many consecutive failed delivery attempts (the number is in the value, e.g. disabled_after_20_consecutive_failures). key_revoked — The API key that created the subscription was revoked. |
last_success_at | string (date-time) or null | Last successful delivery. |
created_at | string (date-time) | — |
exclude_own_changes | boolean | true = changes made with the API key that created this subscription are not delivered to it (no echo in two-way syncs). |
Nota
| Campo | Tipo | Descripción |
|---|---|---|
object | "note" | — |
id | string | — |
lead_id | string | — |
content | string | — |
created_at | string (date-time) or null | When the note was added (UTC). |
Error
The only error shape of the API.
| Campo | Tipo | Descripción |
|---|---|---|
error (required) | object | — |
Eventos de webhook
Cada evento se envía por POST a la target_url de sus suscripciones. Firmas, reintentos y garantías: Webhooks.
lead.created
Se dispara cuando: Se crea un lead desde cualquier origen: la app, una importación CSV, un formulario, el Lead Finder, un mensaje entrante o la API.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "lead.created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "api",
"api_key_id": "0b6c1d2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e"
},
"data": {
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Operations Manager",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}
}lead.updated
Se dispara cuando: Cambia uno de estos campos de un lead: type, full_name, business_name, job_title, work_email, personal_email, work_phone, personal_phone, company_name, status, tags, city, country, instagram_profile, linkedin_profile_url, website, external_id. Los cambios en cualquier otro campo (por ejemplo, contadores internos de seguimiento) nunca lo disparan.
`changes`: { "campo": [antes, después] } por cada campo que cambió. Los valores son los guardados tal cual (por ejemplo tags como se almacena), y solo aparecen esos 17 campos.
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "lead.updated",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"status": [
"new",
"contacted"
],
"job_title": [
"Operations Manager",
"Head of Operations"
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Head of Operations",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "contacted",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}
}deal.created
Se dispara cuando: Se crea un deal: en la app, por un Empleado de IA, desde una cotización o por la API.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "deal.created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "api",
"api_key_id": "0b6c1d2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e"
},
"data": {
"object": "deal",
"id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"title": "Acme Corp — 3 locations",
"value": 4800,
"stage": "Proposal",
"status": "open",
"close_date": "2026-11-30",
"lost_reason": null,
"stage_id": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
"agent_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5e",
"external_id": "opp-5521",
"next_step_action": "Follow-up scheduled (WhatsApp)",
"next_step_date": "2026-10-24T15:00:00.000Z",
"created_at": "2026-10-20T09:15:00.000Z",
"updated_at": "2026-10-22T16:40:12.000Z"
}
}deal.stage_changed
Se dispara cuando: Cambia la etapa del pipeline de un deal, o su estado (open, won, lost). Úsalo para "deal ganado" y "deal perdido".
`changes`: stage, stage_id y/o status, cada uno como [antes, después].
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "deal.stage_changed",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"stage": [
"Proposal",
"Won"
],
"stage_id": [
"c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
"d4e5f6a7-b8c9-4d0e-9f1a-3b4c5d6e7f80"
],
"status": [
"open",
"won"
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "deal",
"id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"title": "Acme Corp — 3 locations",
"value": 5200,
"stage": "Won",
"status": "won",
"close_date": "2026-10-28T17:02:00.000Z",
"lost_reason": null,
"stage_id": "d4e5f6a7-b8c9-4d0e-9f1a-3b4c5d6e7f80",
"agent_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5e",
"external_id": "opp-5521",
"next_step_action": null,
"next_step_date": null,
"created_at": "2026-10-20T09:15:00.000Z",
"updated_at": "2026-10-28T17:02:00.000Z"
}
}deal.updated
Se dispara cuando: Cambia uno de estos campos guardados de un deal SIN cambio de etapa ni de estado: deal_title, deal_value, agent_id, close_date, lost_reason, next_step_action, next_step_date, external_id. (deal_title y deal_value son title y value en el objeto Deal.)
`changes`: { "campo": [antes, después] } con los nombres de campo guardados de la lista anterior.
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "deal.updated",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"deal_value": [
4800,
5200
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "deal",
"id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"title": "Acme Corp — 3 locations",
"value": 5200,
"stage": "Proposal",
"status": "open",
"close_date": "2026-11-30",
"lost_reason": null,
"stage_id": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
"agent_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5e",
"external_id": "opp-5521",
"next_step_action": "Follow-up scheduled (WhatsApp)",
"next_step_date": "2026-10-24T15:00:00.000Z",
"created_at": "2026-10-20T09:15:00.000Z",
"updated_at": "2026-10-22T16:40:12.000Z"
}
}quote.created
Se dispara cuando: Se crea una cotización.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "quote.created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "quote",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"number": "Q-1042",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "sent",
"subtotal": 1200,
"tax_total": 84,
"shipping_cost": 0,
"discount_total": 0,
"total": 1284,
"valid_until": "2026-11-15",
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "qt-7781",
"notes": "Prices valid for the 2026 rollout.",
"payment_terms": "Net 15",
"created_at": "2026-10-22T16:45:00.000Z",
"updated_at": "2026-10-22T16:47:31.000Z"
}
}quote.status_changed
Se dispara cuando: Cambia el estado de una cotización (por ejemplo de sent a accepted).
`changes`: status como [antes, después].
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "quote.status_changed",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"status": [
"sent",
"accepted"
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "quote",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"number": "Q-1042",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "accepted",
"subtotal": 1200,
"tax_total": 84,
"shipping_cost": 0,
"discount_total": 0,
"total": 1284,
"valid_until": "2026-11-15",
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "qt-7781",
"notes": "Prices valid for the 2026 rollout.",
"payment_terms": "Net 15",
"created_at": "2026-10-22T16:45:00.000Z",
"updated_at": "2026-10-22T16:47:31.000Z"
}
}order.created
Se dispara cuando: Se crea una orden.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "order.created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "order",
"id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
"number": "ORD-2088",
"quote_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "pending",
"payment_status": "unpaid",
"subtotal": 1200,
"tax": 84,
"shipping_cost": 0,
"total": 1284,
"tracking_number": null,
"carrier": null,
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "erp-so-3310",
"delivery_type": "pickup",
"shipping_address": null,
"billing_address": null,
"notes": null,
"amount_paid": 0,
"created_at": "2026-10-24T10:05:00.000Z",
"updated_at": "2026-10-24T18:20:09.000Z"
}
}order.status_changed
Se dispara cuando: Cambia el estado de una orden, o su estado de pago (por ejemplo a paid).
`changes`: status y/o payment_status, cada uno como [antes, después].
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "order.status_changed",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"payment_status": [
"unpaid",
"paid"
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "order",
"id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
"number": "ORD-2088",
"quote_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"deal_id": "8d1e2f3a-4b5c-4d6e-8f70-9a1b2c3d4e5f",
"status": "processing",
"payment_status": "paid",
"subtotal": 1200,
"tax": 84,
"shipping_cost": 0,
"total": 1284,
"tracking_number": null,
"carrier": null,
"items": [
{
"product_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Annual plan — per location",
"sku": "PLAN-LOC-12",
"description": null,
"quantity": 3,
"unit_price": 400,
"total": 1284,
"variant_id": null,
"tax_rate": 7,
"discount": 0
}
],
"external_id": "erp-so-3310",
"delivery_type": "pickup",
"shipping_address": null,
"billing_address": null,
"notes": null,
"amount_paid": 1284,
"created_at": "2026-10-24T10:05:00.000Z",
"updated_at": "2026-10-24T18:20:09.000Z"
}
}campaign_lead.replied
Se dispara cuando: Un lead de una de tus campañas responde (su estado en la campaña pasa a replied).
`changes`: status como [antes, "replied"].
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "campaign_lead.replied",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"status": [
"contacted",
"replied"
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "campaign_lead",
"id": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e",
"campaign_id": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"status": "replied",
"form_submitted_at": null,
"lead": {
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Operations Manager",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}
}
}campaign_lead.form_submitted
Se dispara cuando: Un lead de una de tus campañas envía el formulario de la campaña.
`changes`: {} (siempre vacío). data.form_submitted_at trae la hora.
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "campaign_lead.form_submitted",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "campaign_lead",
"id": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e",
"campaign_id": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"status": "contacted",
"form_submitted_at": "2026-10-19T14:31:58.000Z",
"lead": {
"object": "lead",
"id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"type": "person",
"full_name": "Maria Lopez",
"business_name": null,
"job_title": "Operations Manager",
"work_email": "maria.lopez@example.com",
"personal_email": null,
"work_phone": "+13055550142",
"personal_phone": null,
"company_name": "Acme Corp",
"city": "Miami",
"country": "United States",
"status": "new",
"source": "api",
"tags": [
"typeform",
"spring-promo"
],
"instagram_profile": null,
"linkedin_profile_url": null,
"website": null,
"external_id": "crm-88412",
"created_at": "2026-10-19T14:32:10.000Z",
"updated_at": "2026-10-19T14:32:10.000Z"
}
}
}product.created
Se dispara cuando: Se crea un producto: en la app, por importación CSV, por un Empleado de IA o por la API.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "product.created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "product",
"id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Dining chair",
"sku": "CHAIR-OAK",
"reference_code": null,
"category": "Furniture",
"description": "Solid oak dining chair.",
"brand": "Acme Home",
"price": 199,
"sale_price": 179,
"currency": "USD",
"stock_quantity": 40,
"track_stock": true,
"status": "active",
"images": [
"https://cdn.example.com/chair.jpg"
],
"url": "https://shop.example.com/chair",
"min_order_quantity": 1,
"has_variants": true,
"variants": [
{
"object": "product_variant",
"id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
"sku": "CHAIR-OAK-BLK",
"title": "Oak / Black",
"price": 189,
"stock_quantity": 12,
"attributes": {
"wood": "Oak",
"color": "Black"
}
}
],
"external_id": "sku-88731",
"created_at": "2026-09-02T12:00:00.000Z",
"updated_at": "2026-10-19T09:30:00.000Z"
}
}product.updated
Se dispara cuando: Cambia uno de estos campos de un producto: name, sku, reference_code, category, description, brand, price, sale_price, currency, stock_quantity, track_stock, status, images, url, min_order_quantity, external_id. Los cambios de variantes no lo disparan.
`changes`: { "campo": [antes, después] } por cada campo que cambió.
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "product.updated",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"stock_quantity": [
40,
35
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "product",
"id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
"name": "Dining chair",
"sku": "CHAIR-OAK",
"reference_code": null,
"category": "Furniture",
"description": "Solid oak dining chair.",
"brand": "Acme Home",
"price": 199,
"sale_price": 179,
"currency": "USD",
"stock_quantity": 35,
"track_stock": true,
"status": "active",
"images": [
"https://cdn.example.com/chair.jpg"
],
"url": "https://shop.example.com/chair",
"min_order_quantity": 1,
"has_variants": true,
"variants": [
{
"object": "product_variant",
"id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
"sku": "CHAIR-OAK-BLK",
"title": "Oak / Black",
"price": 189,
"stock_quantity": 12,
"attributes": {
"wood": "Oak",
"color": "Black"
}
}
],
"external_id": "sku-88731",
"created_at": "2026-09-02T12:00:00.000Z",
"updated_at": "2026-10-19T09:30:00.000Z"
}
}booking.created
Se dispara cuando: Se crea una reserva: en la app, por un Empleado de IA o por la API.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "booking.created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "api",
"api_key_id": "0b6c1d2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e"
},
"data": {
"object": "booking",
"id": "f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f809102",
"resource_id": "e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8091",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"start_time": "2026-10-25T00:00:00.000Z",
"end_time": "2026-10-25T01:30:00.000Z",
"status": "confirmed",
"party_size": 4,
"notes": "Birthday",
"meeting_url": null,
"external_id": "pos-res-1042",
"created_at": "2026-10-20T14:00:00.000Z",
"updated_at": "2026-10-20T14:00:00.000Z"
}
}booking.updated
Se dispara cuando: Cambia uno de estos campos de una reserva: resource_id, lead_id, start_time, end_time, status, party_size, notes, meeting_url, external_id.
`changes`: { "campo": [antes, después] } por cada campo que cambió.
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "booking.updated",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {
"status": [
"confirmed",
"seated"
]
},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "booking",
"id": "f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f809102",
"resource_id": "e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8091",
"lead_id": "3f2b8c1e-9a4d-4e6f-b7a8-1c2d3e4f5a6b",
"start_time": "2026-10-25T00:00:00.000Z",
"end_time": "2026-10-25T01:30:00.000Z",
"status": "seated",
"party_size": 4,
"notes": "Birthday",
"meeting_url": null,
"external_id": "pos-res-1042",
"created_at": "2026-10-20T14:00:00.000Z",
"updated_at": "2026-10-20T14:00:00.000Z"
}
}conversation.message_created
Se dispara cuando: El Empleado de IA, o una persona que responde desde el Inbox, escribe en una conversación abierta por la API.
`changes`: {} (siempre vacío).
{
"id": "e4d3c2b1-a098-4765-8432-10fedcba9876",
"event": "conversation.message_created",
"occurred_at": "2026-10-19T14:32:11.000Z",
"organization_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"changes": {},
"triggered_by": {
"type": "app",
"api_key_id": null
},
"data": {
"object": "conversation_message",
"id": "29304b5c-6d7e-4f80-91a2-b3c4d5e6f708",
"conversation_id": "18293a4b-5c6d-4e7f-8091-a2b3c4d5e6f7",
"role": "human",
"text": "Hi Maria, this is Ana from the team. Your table is ready.",
"created_at": "2026-10-20T15:00:06.000Z"
}
}