Errors
Every error of the SpaceKru API has the same JSON shape, a stable machine-readable code, a human-readable message in English or Spanish, and a request_id you can quote to support.
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.
Error format
{
"error": {
"code": "INVALID_REQUEST",
"message": "The request did not pass validation.",
"details": [
{
"field": "work_email",
"issue": "Invalid email"
}
],
"request_id": "5f2c9a1b7e3d"
}
}code— stable. Branch your code on it, never onmessage.message— for people, in English or Spanish (Accept-Language, or the language of whoever created the key). Wording may improve over time.details[]— validation errors: up to 20 items, each with thefieldand theissue(always in English).existing_id— only onDUPLICATE_RESOURCE: the id of the record that already exists.request_id— also in theX-Request-Idresponse header. Include it when you contact support.
Unknown routes under the API answer a JSON 404, never an HTML page.
Error codes
| HTTP | Code | Retry? |
|---|---|---|
400 | `INVALID_REQUEST` | After fixing the request |
401 | `INVALID_API_KEY` | After fixing the request |
402 | `PLAN_UPGRADE_REQUIRED` | After fixing the request |
402 | `AI_QUOTA_EXHAUSTED` | After fixing the request |
403 | `INSUFFICIENT_SCOPE` | After fixing the request |
403 | `MODULE_NOT_AVAILABLE` | After fixing the request |
404 | `NOT_FOUND` | No |
409 | `DUPLICATE_RESOURCE` | No |
409 | `IDEMPOTENCY_CONFLICT` | After fixing the request |
409 | `SLOT_UNAVAILABLE` | After fixing the request |
409 | `CONVERSATIONS_NOT_CONFIGURED` | After fixing the request |
413 | `PAYLOAD_TOO_LARGE` | After fixing the request |
422 | `UNPROCESSABLE` | After fixing the request |
429 | `RATE_LIMITED` | Yes, with backoff |
500 | `INTERNAL_ERROR` | Yes, with backoff |
503 | `SERVICE_BUSY` | Yes, with backoff |
503 | `ELIGIBILITY_UNAVAILABLE` | Yes, with backoff |
503 | `CONVERSATIONS_UNAVAILABLE` | Yes, with backoff |
Every code in detail
400 INVALID_REQUEST
- Default message: "The request is invalid."
- When: The body or a query parameter failed validation (
details[]lists each field), an unknown field was sent, the body is not valid JSON,Content-Typeis notapplication/jsonon a write, thecursoris invalid, or a requiredIdempotency-Keyis missing. - What to do: Read
details[]and correct the request. Unknown fields are rejected on purpose, so typos never pass silently.
401 INVALID_API_KEY
- Default message: "Missing, malformed, unknown or revoked API key."
- When: No
Authorization: Bearerheader, a key with the wrong shape, an unknown key, or a revoked key. - What to do: Check the key in your environment or tool. If it was revoked or lost, create a new one (Authentication).
402 PLAN_UPGRADE_REQUIRED
- Default message: "The SpaceKru API is available on paid plans only."
- When: The account is not on an active paid plan: free plan, cancelled or unpaid subscription, a plan without API access, or a closed account. The message says which.
- What to do: Upgrade or update the payment method in SpaceKru → Billing. The same keys work again right after.
402 AI_QUOTA_EXHAUSTED
- Default message: "Your organization has used all the AI tokens of its plan and its wallet balance."
- When: Your organization has used all the AI tokens of its plan and its wallet balance.
- What to do: Recharge the wallet or upgrade the plan in SpaceKru → Billing.
403 INSUFFICIENT_SCOPE
- Default message: "This API key does not have the scope required for this operation."
- When: The key is valid but lacks the scope of the operation (a read-only key on a write or on
/subscriptions, or a key withoutconversationson/conversations). - What to do: Use a key with the right access, or create one.
403 MODULE_NOT_AVAILABLE
- Default message: "Your plan does not include the module this endpoint uses."
- When: Your plan does not include the module this endpoint uses (
crm,sales_osorscheduler). - What to do: Upgrade your plan in SpaceKru → Billing.
404 NOT_FOUND
- Default message: "Resource not found."
- When: The id does not exist in your account, belongs to another account, or is not a UUID. Also for unknown routes.
- What to do: Check the id and the path. Ids of other accounts are reported as "not found" on purpose.
409 DUPLICATE_RESOURCE
- Default message: "A matching record already exists."
- When: A lead with the same email or phone already exists, another record already uses that
external_id, or a quote was already converted.existing_idis the record that exists. - What to do: Use
existing_id: update it if you need to (upsert).
409 IDEMPOTENCY_CONFLICT
- Default message: "This Idempotency-Key was already used with a different request."
- When: The
Idempotency-Keywas already used with a different body, or a request with the same key is still running. - What to do: Use a new key for a different request; if the first request is still running, retry in a few seconds.
409 SLOT_UNAVAILABLE
- Default message: "That time slot is not available."
- When: The booking slot is taken, the resource is closed at that time, or the party is larger than its capacity.
- What to do: Call
GET /availabilityand pick a free slot.
409 CONVERSATIONS_NOT_CONFIGURED
- Default message: "No Customer Service AI Employee is ready to chat. Turn on the Web Chat channel in an inbound campaign."
- When: No Customer Service AI Employee is ready to chat:
details[0].issuesays what is missing. - What to do: Complete the setup in SpaceKru (requirements).
413 PAYLOAD_TOO_LARGE
- Default message: "The request body exceeds the 1 MB limit."
- When: The request body is larger than 1 MB.
- What to do: Send less data per request.
422 UNPROCESSABLE
- Default message: "The request could not be processed."
- When: Valid JSON that could not be applied: a reference to a record that is not yours (
lead_id,product_id,stage_id…), a campaign that does not accept leads, a deal without AI Employee for a follow-up, a closed conversation, more than 50 active webhooks, or a value the database rejected. - What to do: Read the message: it names the field or the rule.
429 RATE_LIMITED
- Default message: "Rate limit exceeded. Retry after the time indicated in the Retry-After header."
- When: More than 120 requests in a minute or 10,000 in a UTC day for this key (defaults), or the daily AI conversation messages of the account.
- What to do: Wait
Retry-Afterseconds (Rate limits).
500 INTERNAL_ERROR
- Default message: "An unexpected error occurred."
- When: Something unexpected failed on our side. Internal details are never exposed.
- What to do: Retry with backoff. If it persists, contact us with the
request_id.
503 SERVICE_BUSY
- Default message: "The API is busy. Retry in a few seconds."
- When: The server is at capacity for a moment, or the AI model did not answer a conversation in time.
- What to do: Retry after
Retry-Afterseconds (5; 15 for AI conversations).
503 ELIGIBILITY_UNAVAILABLE
- Default message: "We could not verify your plan right now. Please retry shortly."
- When: We could not verify your plan for a moment. It fails closed: never a free pass.
- What to do: Retry after
Retry-Afterseconds (10). The request was not applied.
503 CONVERSATIONS_UNAVAILABLE
- Default message: "AI conversations through the API are not available right now."
- When: AI conversations through the API are switched off right now.
- What to do: Retry later.
Handling errors well
- Retry only
429,500and503, with backoff and jitter — and send anIdempotency-Keyon creates so retries are safe. - Never retry
400,401,402,403,404or422unchanged: the answer will be the same. - Log
request_idwith every failed call. With it we can find exactly what happened.