Skip to main content

Error Reference

All SpatialFlow API errors follow a consistent JSON format. This page documents every error code, HTTP status pattern, and common error scenarios.

Error Response Format​

Every error response includes a detail field with a human-readable message:

{
"detail": "Human-readable error message",
"error_code": "MACHINE_READABLE_CODE",
"details": {}
}
FieldTypeRequiredDescription
detailstring or arrayYesHuman-readable message (string) or validation errors (array)
error_codestringNoMachine-readable identifier for programmatic handling
detailsobjectNoAdditional context (field errors, limits, etc.)

Validation Errors​

When request body validation fails (HTTP 422), the detail field is an array of error objects instead of a string:

{
"detail": [
{
"type": "missing",
"loc": ["body", "data", "name"],
"msg": "Field required"
},
{
"type": "value_error",
"loc": ["body", "data", "geometry", "type"],
"msg": "Value error, unsupported geometry type"
}
]
}

Each validation error object contains:

FieldDescription
typeError type (e.g., missing, value_error, string_too_long)
locLocation path as an array (e.g., ["body", "data", "name"])
msgHuman-readable error message

HTTP Status Codes​

StatusMeaningWhen It Occurs
400Bad RequestInvalid input, malformed JSON, business rule violation
401UnauthorizedMissing or invalid authentication token or API key
403ForbiddenValid auth but insufficient permissions, or no event plan to count uploads against
404Not FoundResource does not exist or is not accessible to the caller
422Unprocessable EntityRequest body fails schema validation
429Too Many RequestsRequest rate limit exceeded
500Internal Server ErrorUnexpected server failure

Error Codes Reference​

The error_code field provides a machine-readable identifier for programmatic error handling. Not all errors include this field.

Error CodeHTTP StatusDescription
RATE_LIMIT_EXCEEDED429Global rate limit exceeded (requests per hour)
QUOTA_UNAVAILABLE403Location upload refused: the workspace has no event plan to count it against
WORKFLOW_LIMIT_REACHED403Workspace is at its plan's workflow limit
NOT_FOUND404Generic resource-not-found. Most endpoints emit domain-specific codes instead; see the note below.
Domain-specific 404 codes

Most endpoints emit domain-specific error_code values for 404 responses rather than the generic NOT_FOUND. Examples include DEVICE_NOT_FOUND and INVITE_NOT_FOUND. Inspect the error_code field on a 404 response to identify exactly which resource was missing. The generic NOT_FOUND code only surfaces from a small number of endpoints (e.g., billing) where the missing resource is not domain-specific.

Always Check the detail Field First

Not all errors include an error_code. The detail field is always present and provides a human-readable description. Use error_code for programmatic branching when available, and fall back to detail for display.

Common Error Scenarios​

Authentication Failure (401)​

curl -i https://api.spatialflow.io/api/v1/geofences \
-H "Authorization: Bearer expired-or-invalid-token"
{
"detail": "Unauthorized"
}

Solution: Refresh your JWT token or verify your API key is correct and not revoked.

Rate Limit Exceeded (429)​

curl -i https://api.spatialflow.io/api/v1/geofences \
-H "X-API-KEY: YOUR_API_KEY"
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Content-Type: application/json
{
"detail": "Rate limit exceeded",
"error_code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 1000,
"reset_at": "2025-11-10T11:00:00Z",
"retry_after": 3600
}
}

Solution: Wait for the number of seconds specified in the Retry-After header before retrying. See Rate Limiting for retry patterns.

Validation Error (422)​

curl -i -X POST https://api.spatialflow.io/api/v1/geofences \
-H "X-API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"geometry": {"type": "InvalidType", "coordinates": []}}'
{
"detail": [
{
"type": "missing",
"loc": ["body", "data", "name"],
"msg": "Field required"
},
{
"type": "value_error",
"loc": ["body", "data", "geometry", "type"],
"msg": "Value error, unsupported geometry type"
}
]
}

Solution: Check the loc field in each error to identify which request fields need correction.

Common Errors by Feature​

Authentication​

An expired or invalid access token, and an unrecognized or revoked API key, both return a plain 401 with no error_code:

{ "detail": "Unauthorized" }

Solution: Refresh your JWT token, or check that your API key is correct and hasn't been revoked.

Geofences​

Invalid geometry, for example a self-intersecting polygon:

{ "detail": "This shape crosses itself. Redraw it so the outline never crosses over. (<GEOS validity reason>)" }

The parenthesized part is the raw reason GEOS reported; the sentence before it is SpatialFlow's plain-language translation. Other shapes give a different sentence: too few points, a duplicated ring, or a hole that falls outside the outline.

Too many vertices:

{ "detail": "Geometry has 1023 vertices, exceeds maximum of 1000. Consider simplifying the geometry." }

The vertex limit is a flat 1,000 on every plan; see Geofences.

Workflows​

Invalid step type:

{
"detail": "Step 0: Invalid step type: sms_v2",
"details": {
"problems": [
{
"message": "Step 0: Invalid step type: sms_v2",
"phase": "activation",
"error_code": null
}
]
}
}

Saving or activating a workflow checks its trigger and steps after the request body parses, so a definition problem is a plain 400 with a string detail, not a 422 validation array. detail is the first problem; details.problems lists every problem that blocked the request. A step's problems start with its zero-based position (Step 0:). phase is save for a rule about what may be written and activation for something a running workflow needs, and error_code is set where one applies, such as INVALID_GEOFENCE_TARGET for a missing or deleted geofence target. See Workflows for the accepted step types.

Webhooks​

Invalid signature, on SpatialFlow's inbound webhook receiver (POST /webhooks/receive/{webhook_id}, verified via the X-Webhook-Signature header):

{
"success": false,
"error": "Unauthorized",
"message": "Invalid webhook signature"
}

Solution: Verify you're using the correct signing secret. This is a different header and contract from the X-SF-Signature on outbound deliveries; see Webhooks for both.

Handling Errors in Code​

Python​

import requests

response = requests.post(
"https://api.spatialflow.io/api/v1/geofences",
headers={"X-API-KEY": "YOUR_API_KEY"},
json={"name": "Test"},
)

if not response.ok:
error = response.json()
detail = error.get("detail")

# Validation errors return detail as an array
if isinstance(detail, list):
for err in detail:
print(f" {'.'.join(err['loc'])}: {err['msg']}")
else:
error_code = error.get("error_code")
if error_code == "RATE_LIMIT_EXCEEDED":
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Retry in {retry_after}s")
else:
print(f"Error: {detail}")

TypeScript​

async function handleApiError(response: Response): Promise<never> {
const body = await response.json();

if (Array.isArray(body.detail)) {
// Validation errors
const fields = body.detail.map(
(e: { loc: string[]; msg: string }) => `${e.loc.join(".")}: ${e.msg}`,
);
throw new Error(`Validation failed:\n${fields.join("\n")}`);
}

if (body.error_code === "RATE_LIMIT_EXCEEDED") {
const retryAfter = response.headers.get("Retry-After") ?? "60";
throw new Error(`Rate limited. Retry in ${retryAfter}s`);
}

throw new Error(body.detail ?? "Unknown error");
}

See Best Practices for retry patterns and production error handling strategies.