Error Handling
Standard error envelope, error codes, HTTP status codes, and how to handle errors with the TypeScript SDK.
Error Response Format
All Simply360 API errors follow a consistent envelope. When a request fails, the response body contains an error object with a machine-readable code and a human-readable message, plus a meta object that includes a unique request identifier.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The field 'email' is required.",
"details": { "field": "email" }
},
"meta": {
"requestId": "req_abc123def456"
}
}
Envelope Fields
| Field | Type | Description |
|---|---|---|
error.code |
string | Stable, machine-readable error code. Use this for programmatic handling. |
error.message |
string | Human-readable description. May change over time — do not match against this string in code. |
error.details |
object | Optional, error-specific structured details (e.g., the offending field name). |
meta.requestId |
string | Unique identifier for the request. Include this when contacting support. |
Common Error Codes
| Error Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR |
400 | The request body or query parameters failed validation. Inspect error.details and error.message for which fields are invalid. |
AUTHENTICATION_REQUIRED |
401 | No valid credential was provided. Ensure your Authorization header includes a valid bearer token. |
INVALID_API_KEY |
401 | The API key was not found, is inactive, or failed verification. Related codes: API_KEY_EXPIRED and INVALID_SESSION_TOKEN (expired or revoked session token). |
PERMISSION_DENIED |
403 | The credential is valid but lacks access to this resource or operation. Related code: FEATURE_PERMISSION_DENIED (a required feature permission is missing). |
NOT_FOUND |
404 | The requested resource does not exist or is not accessible to your team. |
CONFLICT |
409 | The request conflicts with the current state of the resource (e.g., a duplicate of an existing item). |
RATE_LIMIT_EXCEEDED |
429 | You've exceeded a request quota. Wait until the window resets (see the X-RateLimit-Reset header) before retrying. |
INTERNAL_ERROR |
500 | An unexpected server error occurred. If it persists, contact support with the requestId. |
TIMEOUT |
408 | Returned by the SDK (not the server) when the configured request timeout elapses (default 30 seconds). |
UNKNOWN |
— | SDK fallback when a non-2xx response has no parseable error code (e.g., a gateway error page). |
HTTP Status Codes
Simply360 follows standard HTTP semantics:
| Status | Meaning |
|---|---|
200 | Success. |
201 | Created — a new resource was successfully created. |
204 | No Content — success with no response body. Rare; most delete endpoints return 200 with a confirmation body. |
400 | Bad Request — malformed input or failed validation. |
401 | Unauthorized — missing or invalid credentials. |
403 | Forbidden — authenticated but lacking permission. |
404 | Not Found. |
409 | Conflict — the request conflicts with existing state. |
429 | Too Many Requests — you've hit a rate limit. |
500 | Internal Server Error. |
Handling Errors in the TypeScript SDK
The SDK throws ApiError for any non-2xx response. ApiError extends Error and exposes:
statusCode— HTTP status (e.g.,404).code— Machine-readable error code (e.g.,"NOT_FOUND").message— Human-readable message.details— OptionalRecord<string, unknown>with structured context.
import { Simply360, ApiError } from '@simply360/sdk';
const s360 = new Simply360({ apiKey: process.env.S360_API_KEY! });
async function getRecord(recordId: string) {
try {
const response = await s360.dataRecords.get(recordId);
return response.data;
} catch (error) {
if (error instanceof ApiError) {
switch (error.code) {
case 'AUTHENTICATION_REQUIRED':
case 'INVALID_API_KEY':
console.error('Invalid credentials. Check your API key.');
break;
case 'NOT_FOUND':
console.warn(`Record ${recordId} not found.`);
return null;
case 'RATE_LIMIT_EXCEEDED': {
// Rate-limit headers are on the HTTP response, not on ApiError.
// Apply your own backoff before retrying.
await new Promise((resolve) => setTimeout(resolve, 5000));
return getRecord(recordId);
}
case 'VALIDATION_ERROR':
console.error('Validation failed:', error.message, error.details);
break;
default:
console.error(`API error [${error.statusCode}/${error.code}]: ${error.message}`);
}
}
throw error;
}
}
The requestId Field
Every API response includes a requestId in the meta object, and the same value is sent in the X-Request-Id response header. When contacting Simply360 support about an API issue, always include the requestId from the failed response — it lets the team locate the exact request in server logs and diagnose quickly.
If you are making raw HTTP requests without the SDK, parse the response body (or read the X-Request-Id header) to extract the requestId. Store it alongside any error report or log entry your application produces.
const response = await fetch('https://api.simply360.app/v1/data-records/WXYZ-5678-IJKL', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await response.json();
if (!response.ok) {
console.error(`Request failed: ${body.error.message} (requestId: ${body.meta.requestId})`);
}
Timeouts
The SDK has a default request timeout of 30 seconds. When the timeout fires, the SDK throws an ApiError with statusCode: 408 and code: "TIMEOUT". You can configure the timeout per client:
const s360 = new Simply360({
apiKey: process.env.S360_API_KEY!,
timeout: 60000, // 60 seconds
});