API Response Standards
FlowOn API follows standard HTTP conventions and OpenAPI 3.0 specifications for all responses.
HTTP Status Codes
| Status Code | Description | When Used |
|---|---|---|
| 200 | OK | Successful GET, POST, PUT, DELETE operations |
| 204 | No Content | Successful operation with no response body (Single/Multi queries with no results) |
| 400 | Bad Request | Validation errors, pre-condition failures, business rule violations |
| 401 | Unauthorized | Authentication failure or insufficient permissions |
| 404 | Not Found | Attachment not found (for download operations) |
| 500 | Internal Server Error | Unexpected server-side errors |
Success Responses
Create Action
Returns the ID of the created record:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Update Action
Returns an empty response body:
HTTP/1.1 200 OK
Content-Length: 0
Delete Action
Returns an empty response body:
HTTP/1.1 200 OK
Content-Length: 0
Read Action / Single Query
Returns the record directly:
{
"accountId": "a1b2c3d4-...",
"name": "Contoso Ltd",
"emailAddress1": "info@contoso.com"
}
Multi Query
Returns an array of records:
[
{ "contactId": "c1-guid", "fullName": "Alice Johnson" },
{ "contactId": "c2-guid", "fullName": "Bob Williams" }
]
Paginated Query
Returns a paginated result object:
{
"cursor": "eyJwYWdlIjoxfQ==",
"count": 20,
"totalRecordCount": 47,
"hasMoreRecords": true,
"data": [
{ "accountId": "a1-guid", "name": "Contoso Ltd" },
{ "accountId": "a2-guid", "name": "Fabrikam Inc" }
]
}
Process Actions
Returns an empty response body (or output parameters if defined):
{}
Error Response Structure
All errors follow a consistent structure:
{
"errorCode": "ERROR_CODE",
"errorMessage": "Human-readable description of the error"
}
| Field | Type | Description |
|---|---|---|
| errorCode | String | Machine-readable error identifier |
| errorMessage | String | Human-readable error description |
Common Error Codes
Validation Errors (400)
{
"errorCode": "VALIDATION_ERROR",
"errorMessage": "Name is required and cannot be empty"
}
Pre-condition Failures (400)
{
"errorCode": "PRECONDITION_FAILED",
"errorMessage": "Cannot update inactive accounts"
}
Authentication Errors (401)
{
"errorCode": "UNAUTHORIZED",
"errorMessage": "Invalid or expired authentication token"
}
Authorization Errors (401)
{
"errorCode": "UNAUTHORIZED",
"errorMessage": "You do not have permission to perform this action"
}
Not Found Errors (404)
{
"errorCode": "NOT_FOUND",
"errorMessage": "Account not found"
}
{
"errorCode": "ATTACHMENT_NOT_FOUND",
"errorMessage": "The requested attachment was not found"
}
Business Rule Violations (400)
{
"errorCode": "BUSINESS_RULE_VIOLATION",
"errorMessage": "Order total exceeds customer credit limit"
}
File Upload Errors (400)
{
"errorCode": "FILE_TOO_LARGE",
"errorMessage": "File too large. Maximum size: 10 MB"
}
{
"errorCode": "INVALID_FILE_TYPE",
"errorMessage": "File type not allowed. Allowed extensions: .pdf, .doc, .docx"
}
Internal Errors (500)
{
"errorCode": "INTERNAL_ERROR",
"errorMessage": "An unexpected error occurred while processing the request"
}
Best Practices for Handling Responses
Check Status Codes
Always check the HTTP status code before processing the response:
const response = await fetch('/api/sales/accounts', {
method: 'POST',
body: JSON.stringify(accountData)
});
if (response.ok) {
const { id } = await response.json();
console.log('Created account:', id);
} else {
const error = await response.json();
console.error('Error:', error.errorMessage);
}
Handle Empty Responses
Some operations return empty responses (204 No Content):
const response = await fetch('/api/sales/queries/account?AccountId=' + id);
if (response.status === 204) {
// No record found
return null;
}
return await response.json();
Parse Paginated Results
For paginated queries, use the cursor for navigation:
async function fetchAllAccounts() {
let cursor = null;
const allAccounts = [];
do {
const url = cursor
? `/api/sales/queries/accounts?cursor=${cursor}`
: '/api/sales/queries/accounts';
const response = await fetch(url);
const { data, cursor: nextCursor, hasMoreRecords } = await response.json();
allAccounts.push(...data);
cursor = hasMoreRecords ? nextCursor : null;
} while (cursor);
return allAccounts;
}