Authentication & API keys
Every request carries an API key in the Authorization header. A key belongs to one SpaceKru account, has read, read & write or AI conversation access, and works only while the account is on a paid plan.
The SpaceKru API (v1.1) is a REST/JSON API available on every paid SpaceKru plan. It syncs leads both ways (upsert by external_id), adds leads to campaigns, creates and moves deals and asks AI Employees to follow up, manages products, quotes, orders, payments and bookings, lets your platform chat with your Customer Service AI Employee, and sends signed webhooks for 16 events that say who caused each change. Base URL: https://app.spacekru.com/api/public/v1. Authentication: Bearer API key (sk_live_…). Documentation: https://www.spacekru.com/developers (Spanish: https://www.spacekru.com/es/developers). OpenAPI 3.1: https://app.spacekru.com/api/public/v1/openapi.json.
API keys
Send the key as a Bearer token on every request:
Authorization: Bearer sk_live_…- Format:
sk_live_followed by 43 characters. Anything else answers401without even reaching our database. - Every key is live: there is no test mode. Tag test leads (for example
docs-test) so you can find and remove them. - The key is shown once, when it is created. We store only a keyed hash of it, so nobody — including SpaceKru — can show it to you again.
- Up to 5 active keys per account. Name each key after the tool that uses it ("Zapier", "Website form", "ERP sync") so you can revoke one without touching the rest.
Who can create keys
Only the account owner, in Integrations → API & Webhooks (key names up to 100 characters). The same panel shows each key's last use, the request count of the last 30 days, and every webhook subscription.
Scopes
- Read only (
read): everyGETexcept webhooks and conversations. - Read & write (
read+write): everything that creates or changes data, and every/subscriptionsoperation. - AI conversations (
conversations, optional, only with read & write): the/conversationsoperations. They spend AI tokens, so the owner grants it explicitly when creating the key.
A key without the scope an operation needs answers 403 INSUFFICIENT_SCOPE.
| Operation | What it does | Scope | Plan module |
|---|---|---|---|
GET /ping | Test the connection | read | — |
GET /leads | List leads (newest first) | read | crm |
POST /leads | Create a lead | write | crm |
GET /leads/{id} | Retrieve a lead | read | crm |
PATCH /leads/{id} | Update a lead | write | crm |
POST /leads/{id}/notes | Add a note to a lead | write | crm |
GET /deals | List deals | read | crm |
POST /deals | Create a deal | write | crm |
GET /deals/{id} | Retrieve a deal | read | crm |
PATCH /deals/{id} | Update or move a deal | write | crm |
GET /quotes | List quotes | read | sales_os |
POST /quotes | Create a quote | write | sales_os |
GET /quotes/{id} | Retrieve a quote | read | sales_os |
PATCH /quotes/{id} | Update a quote | write | sales_os |
GET /orders | List orders | read | sales_os |
POST /orders | Create an order | write | sales_os |
GET /orders/{id} | Retrieve an order | read | sales_os |
PATCH /orders/{id} | Update an order | write | sales_os |
GET /subscriptions | List webhook subscriptions | write | — |
POST /subscriptions | Subscribe a URL to an event (REST Hook) | write | — |
DELETE /subscriptions/{id} | Unsubscribe | write | — |
PUT /leads/external/{external_id} | Create or update a lead by your id (upsert) | write | crm |
GET /agents | List AI Employees | read | crm |
GET /pipeline-stages | List pipeline stages | read | crm |
GET /campaigns | List campaigns | read | crm |
GET /campaigns/{id} | Retrieve a campaign | read | crm |
POST /campaigns/{id}/leads | Add leads to a campaign | write | crm |
POST /deals/{id}/follow-ups | Ask the AI Employee to follow up | write | crm |
DELETE /deals/{id}/follow-ups | Cancel pending follow-ups | write | crm |
GET /products | List products | read | sales_os |
POST /products | Create a product | write | sales_os |
GET /products/{id} | Retrieve a product | read | sales_os |
PATCH /products/{id} | Update a product | write | sales_os |
PATCH /products/{id}/variants/{variant_id} | Update a variant (price, stock) | write | sales_os |
GET /bookings | List bookings | read | scheduler |
POST /bookings | Create a booking | write | scheduler |
GET /bookings/{id} | Retrieve a booking | read | scheduler |
PATCH /bookings/{id} | Reschedule or update a booking | write | scheduler |
GET /booking-resources | List bookable resources | read | scheduler |
GET /availability | Free slots of a day | read | scheduler |
GET /conversations/availability | Is an AI Employee ready to chat? | conversations | — |
POST /conversations | Open a conversation | conversations | — |
GET /conversations/{id} | Retrieve a conversation | conversations | — |
POST /conversations/{id}/messages | Send the customer's message | conversations | — |
GET /conversations/{id}/messages | List the messages of a conversation | conversations | — |
POST /conversations/{id}/close | Close a conversation | conversations | — |
POST /quotes/{id}/convert | Convert a quote into an order | write | sales_os |
POST /orders/{id}/payments | Record a payment | write | sales_os |
Plan modules (403)
Some operations need a module of your plan, the same one the app needs: crm (leads, deals, campaigns), sales_os (products, quotes, orders) and scheduler (bookings). Without it the answer is 403 MODULE_NOT_AVAILABLE. Every paid plan includes the three modules today.
Response language
error.message comes in English or Spanish. Send Accept-Language: es (or en) to choose; without it, the language of the profile of the person who created the key is used. The Content-Language header tells you which one you got. Error codes, field names, event names and details[].issue never change: branch on them, not on the message.
Plan requirements (402)
The API works only while the account is on an active paid plan (the 7-day free trial of a paid plan counts). Otherwise every request answers 402 PLAN_UPGRADE_REQUIRED with one of these messages:
| Situation | Message you receive |
|---|---|
| No paid plan | The SpaceKru API is available on paid plans only. Upgrade your plan to connect Zapier, Make or your own system. |
| Subscription cancelled, unpaid or past due | Your SpaceKru subscription is not active. Update your payment method to restore API access. |
| The plan does not include the API | Your current SpaceKru plan does not include API access. |
| Account closed | This SpaceKru account is closed. |
- There is no grace period: a payment that fails pauses the API right away (within about a minute).
- Keys are not deleted. When the account is back on a paid plan, the same keys and webhooks work again.
- If we can't verify the plan for a moment, you get
503 ELIGIBILITY_UNAVAILABLEwithRetry-After: 10. It fails closed: retry, it is never a free pass.
401 vs 402 vs 403
| Status | Code | Meaning |
|---|---|---|
401 | INVALID_API_KEY | No key, a malformed key, an unknown key or a revoked key. The response carries WWW-Authenticate: Bearer. |
402 | PLAN_UPGRADE_REQUIRED | The key is valid, but the account is not on an active paid plan. |
403 | INSUFFICIENT_SCOPE / MODULE_NOT_AVAILABLE | The key lacks the scope (e.g. a read-only key calling POST /leads), or the plan lacks the module. |
Revoke and rotate keys
- Revoke a key in Integrations → API & Webhooks. It stops working on the very next request — there is no cache.
- Revoking a key also deactivates the webhook subscriptions it created (
disabled_reason: "key_revoked"). Subscriptions created by other keys are not affected.
To rotate a key without downtime:
- Create a new key with the same access.
- Deploy it to your integration (environment variable, Zapier/Make/n8n connection).
- Check in the panel that the new key shows a recent "Last used".
- Revoke the old key. If the old key created webhooks, subscribe them again with the new key (and if they used
exclude_own_changes, they now skip the new key's changes).
Security & best practices
- Server-to-server only. The API sends no CORS headers, so browsers block calls from web pages — on purpose. A key in front-end code, a mobile app or a public repository can be stolen; keep it on a server or inside your automation tool.
- Store keys as secrets: environment variables, a secrets manager, or the credential store of Zapier, Make or n8n.
- Least privilege: a Read only key for reporting; AI conversations only for the key of your chat integration.
- One key per integration, so revoking one never breaks the others.
- Verify every webhook signature and reject old timestamps — see Verify signatures.
- Tenant isolation: a key can only ever see its own account. An id from another account answers
404, exactly like an id that does not exist — also inside a body (lead_id,product_id…). - Webhook targets are restricted to public
https://hosts on port 443, checked on every delivery; redirects are never followed.
Example: check a key
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)