Responses & Error Handling
Master how the 7G API communicates success, failure, and errors across all endpoints using consistent response structures.
For HTTP/REST users: Parse JSON responses and check result field (boolean) plus HTTP status codes
For .NET SDK users: Check response.Result property and handle HttpRequestException for HTTP-level errors
Both approaches use the same response structures - APIResponse for all endpoints except authentication (which uses TokenResponse).
Quick Reference
2 Response Structures
APIResponse (73 Endpoints)
Structure: { result, message, recordCount, data }
Fields:
result(bool) - Success indicatormessage(string) - Error detailsrecordCount(int) - Record countdata(dynamic) - Response payload
Used for: All GET, POST, PUT, DELETE operations
TokenResponse (2 Endpoints Only)
Structure: { accessToken, refreshToken }
Fields:
accessToken- JWT for API requestsrefreshToken- Long-lived renewal token
Used for: /User/Login, /User/RefreshToken only
NOT wrapped in APIResponse
Critical Rule: HTTP 200 ≠ Success
Must check result: true to confirm success. Business logic errors (validation, not found, conflicts) return HTTP 200 with result: false.
Error handling happens at TWO levels - HTTP status codes AND the result field.
Why This Matters
Proper error handling prevents data corruption, enables graceful degradation through retry logic, and provides clear feedback when operations fail. Critical for maintaining data integrity in financial services operations.
APIResponse Structure
Standard response format for all operational endpoints
Used for all endpoints except authentication (/User/Login and /User/RefreshToken).
Field Descriptions
| Field | Type | Description | When Populated |
|---|---|---|---|
result |
boolean | Success indicator (true = success, false = error) |
Always present |
message |
string | Error message explaining what went wrong | Only when result: false, null/empty when success |
recordCount |
integer | Number of records in current page (GET) or records affected (POST/PUT/DELETE) | Always present |
data |
dynamic | Response payload (array for GET, object for POST/PUT, empty object for errors) | Always present (may be empty object {} on error) |
Critical: Always Check result Field
The API can return HTTP 200 with result: false for business logic errors. Never assume HTTP 200 means success - always check the result field before processing data.
// HTTP Response (200 OK)
{
"result": true,
"message": "",
"recordCount": 3,
"data": [
{
"bizEntityID": 12345,
"name": "Smith Super Fund",
"bizEntityTypeID": 4
},
{
"bizEntityID": 12346,
"name": "Jones Trust",
"bizEntityTypeID": 2
},
{
"bizEntityID": 12347,
"name": "Wilson Family",
"bizEntityTypeID": 1
}
]
}TokenResponse Structure
Special format for authentication endpoints only
Critical Difference: Authentication endpoints (/User/Login and /User/RefreshToken) return TokenResponse directly, NOT wrapped in APIResponse.
Field Descriptions
| Field | Type | Description |
|---|---|---|
accessToken |
string | JWT bearer token used in Authorization header for all authenticated requests. Expires based on environment configuration (typically 15-60 minutes). |
refreshToken |
string | Long-lived token used to obtain new access tokens without re-authentication. Each refresh returns a NEW refresh token (token rotation). |
No expiresIn Field
Unlike many APIs, TokenResponse does NOT include an expiresIn field. Token expiry is configured per environment:
- .NET SDK - Reads JWT
expclaim directly and auto-refreshes 30 seconds before expiry - HTTP/REST - Must track expiry manually using login timestamp + configured duration (consult your administrator)
Public Endpoints (No Authentication Required)
Only 4 endpoints can be called without an Authorization header:
POST /User/Login- Authenticate and receive tokensGET /User/Status- API health checkGET /Common/Lookup- Retrieve lookup categoriesGET /Common/HealthCheck- Health monitoring
All other 71 endpoints require authentication (valid access token in Authorization header).
// POST /User/Login - HTTP 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "9z8y7x6w5v4u3t2s1r0q9p8o7n6m5l4k3j2i..."
}
// NOT wrapped in APIResponse - TokenResponse is returned directly
// Use accessToken in Authorization header for subsequent requestsHTTP Status Codes
Understanding what each status code means and how to respond
Success Codes (2xx)
| Code | Name | When Returned | Response Body |
|---|---|---|---|
200 |
OK | Standard successful response for GET, PUT, DELETE | APIResponse with result: true (or TokenResponse for auth) |
201 |
Created | Resource successfully created via POST | APIResponse with result: true and created entity in data |
Client Errors (4xx)
| Code | Name | Common Causes | How to Fix |
|---|---|---|---|
400 |
Bad Request | Validation errors, missing required fields, invalid data format | Check request body against DTO requirements and validation rules |
401 |
Unauthorized | Missing token, expired token, invalid token | Include Authorization header or refresh/re-login |
403 |
Forbidden | Insufficient permissions for requested operation | Verify role-based access or contact administrator |
404 |
Not Found | Requested resource doesn't exist | Verify ID/external reference or check if resource was deleted |
409 |
Conflict | Duplicate key, unique constraint violation, relationship constraint | Use unique identifiers, remove dependencies before deletion |
429 |
Too Many Requests | Rate limit exceeded | Implement exponential backoff and reduce request frequency |
Server Errors (5xx)
| Code | Name | When Returned | Recommended Action |
|---|---|---|---|
500 |
Internal Server Error | Unhandled exception or unexpected error | Log details and contact support if error persists after retry |
503 |
Service Unavailable | Database busy (lock timeout), database capacity issues | Retry with exponential backoff (wait 1s, 2s, 4s, etc.) |
Error Messages Reference
Exact messages from the API and how to resolve them
Authentication Errors (401)
| Scenario | Exact Message | Solution |
|---|---|---|
| Missing Token | "Unauthorized: No authentication token was provided!" | Include Authorization header in request |
| Expired/Invalid Token | "Unauthorized: Expired or revoked Token was intercepted, Please login again!" | Use refresh token endpoint or re-login with credentials |
| Login Failed | "Login failed." | Verify username and password are correct |
Validation Errors (400)
| Scenario | Message Pattern | Solution |
|---|---|---|
| FluentValidation | Varies by field/rule (e.g., "'Name' must not be empty", "Invalid email format") | Check validation requirements for each DTO field |
| Invalid Payload | "Invalid payload detected, [specific requirement]" | Verify request structure matches endpoint requirements |
| Missing Required Field | "A required field is missing (NULL not allowed): [entities]" | Provide all required fields (non-nullable) |
| Invalid Data Format | "Invalid data format for one of the fields: [entities]" | Check data types (string, int, date formats) |
| Value Out of Range | "A numeric or date value is out of range: [entities]" | Verify value constraints (e.g., dates not in future, positive numbers) |
Conflict Errors (409)
| Scenario | Exact Message Pattern | Solution |
|---|---|---|
| Duplicate Key | "A record with the same key or unique value already exists: [entities]" | Use different external ID or check if record already exists |
| Relationship Constraint | "Cannot delete or update this record because it is linked in other entities: [entities]" | Remove dependent records first or use archive/soft delete |
| Database Deadlock | "The request could not be completed due to a database deadlock. Please retry: [entities]" | Retry request - deadlocks are transient and resolve automatically |
Server Errors (500/503)
| Status | Exact Message | Solution |
|---|---|---|
500 |
"An internal server error occurred while processing the request. Please try again later!" | Log error details and contact support if persists after retry |
503 |
"The database is busy (lock timeout). Try again later: [entities]" | Implement exponential backoff retry (1s, 2s, 4s, 8s) |
503 |
"The database is temporarily unavailable due to capacity issues: [entities]" | Wait and retry - typically resolves within seconds/minutes |
Error Response Format
All error responses include:
{
"result": false,
"message": "[error message from tables above]",
"recordCount": 0,
"data": {}
}Even for HTTP 4xx/5xx errors, the response body follows this structure with result: false and descriptive message.
Error Handling Strategies
Path-specific approaches for robust error recovery
HTTP/REST Approach
Manual two-level error handling with explicit status code checking and response parsing.
Strategy
- Level 1: Check HTTP status code (200, 400, 401, 500, 503, etc.)
- Level 2: Parse JSON response body and check
resultfield - Implement manual retry logic for 503 errors with exponential backoff
- Track token expiry and refresh proactively (5 minutes before expiry)
// Complete HTTP/REST error handling pattern
using System.Net.Http;
using System.Net.Http.Json;
try
{
var response = await httpClient.GetAsync("/BizEntity?PageSize=10");
// Level 1: Check HTTP status code
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
Console.WriteLine("Token expired - refreshing...");
await RefreshTokenAsync();
response = await httpClient.GetAsync("/BizEntity?PageSize=10");
}
else if (response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable)
{
Console.WriteLine("Service busy - retrying with backoff...");
await Task.Delay(1000); // 1 second
response = await httpClient.GetAsync("/BizEntity?PageSize=10");
}
// Level 2: Parse response body
var result = await response.Content.ReadFromJsonAsync<APIResponse>();
// Check business logic success (even if HTTP 200)
if (!result.Result)
{
Console.WriteLine($"Business error: {result.Message}");
return;
}
// Success - process data
Console.WriteLine($"Success! Retrieved {result.RecordCount} entities");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error: {ex.Message}");
}.NET SDK Approach
Layered error handling with exceptions for HTTP errors and result checking for business logic.
Strategy
- Level 1: Catch
HttpRequestExceptionfor HTTP-level errors (401, 500, network failures) - Level 2: Check
response.Resultfor business logic errors (validation, not found, conflicts) - SDK handles token refresh automatically (30 seconds before expiry)
- Built-in retry logic for token expiry scenarios
// Complete SDK error handling pattern
using SevenG.API.SDK;
using System.Net;
try
{
var filter = new BizEntityFilter { PageSize = 10 };
var response = await client.BizEntity.GetAsync(filter);
// Level 2: Check business logic success
if (!response.Result)
{
Console.WriteLine($"Business error: {response.Message}");
// Handle specific business errors
if (response.Message.Contains("not found"))
{
Console.WriteLine("Entity doesn't exist");
}
else if (response.Message.Contains("already exists"))
{
Console.WriteLine("Duplicate record detected");
}
return;
}
// Success - deserialize and process
var entities = JsonSerializer.Deserialize<List<BizEntityDTO>>(
JsonSerializer.Serialize(response.Data)
);
Console.WriteLine($"Success! Retrieved {entities.Count} entities");
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
Console.WriteLine("Authentication failed - token expired");
// SDK auto-refreshes, so this means refresh also failed
await client.Auth.LoginAsync(username, password);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable)
{
Console.WriteLine("Service temporarily unavailable - retry with backoff");
await Task.Delay(2000); // 2 seconds
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP error {ex.StatusCode}: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error: {ex.Message}");
}Universal Best Practices
Strategies that apply regardless of integration approach.
- Layered handling - HTTP errors → Business logic errors → Application errors
- Exponential backoff - For 503 errors, wait 1s, then 2s, 4s, 8s between retries
- Log exact messages - Capture
response.messagefor debugging and support - Never retry 400 errors - Validation failures won't succeed on retry
- Check recordCount - Handle empty result sets (
recordCount: 0) gracefully - Monitor token expiry - Refresh tokens before they expire to avoid 401s mid-operation
Error Recovery Decision Tree
| If You Receive | Then | Reason |
|---|---|---|
| HTTP 401 | Refresh token or re-login | Token missing, expired, or invalid |
| HTTP 400 | Fix request payload | Validation error or missing required field |
| HTTP 409 | Check for duplicates/dependencies | Unique constraint or relationship violation |
| HTTP 500 | Log and retry once, then contact support | Unexpected server error |
| HTTP 503 | Retry with exponential backoff | Database busy or capacity issue (transient) |
| HTTP 200 + result: false | Check message for details |
Business logic error (not found, validation, etc.) |
Response Processing Patterns
How to work with response.Data effectively
Pattern 1: Deserializing Collections (.NET)
The SDK returns response.Data as dynamic. Convert to strongly-typed collections:
var response = await client.BizEntity.GetAsync(filter);
if (response.Result)
{
// Deserialize to List<BizEntityDTO>
var entities = JsonSerializer.Deserialize<List<BizEntityDTO>>(
JsonSerializer.Serialize(response.Data)
);
foreach (var entity in entities)
{
Console.WriteLine($"{entity.BizEntityID}: {entity.Name}");
}
}
else
{
Console.WriteLine($"Error: {response.Message}");
}Pattern 2: Handling Empty Results
Check recordCount to detect empty result sets before processing:
var response = await client.BizEntity.GetAsync(filter);
if (!response.Result)
{
Console.WriteLine($"Error: {response.Message}");
return;
}
// Check for empty results
if (response.RecordCount == 0)
{
Console.WriteLine("No entities found matching your filter");
return;
}
// Process data knowing it's not empty
var entities = JsonSerializer.Deserialize<List<BizEntityDTO>>(
JsonSerializer.Serialize(response.Data)
);
Console.WriteLine($"Found {entities.Count} entities");Pattern 3: Single vs Multiple Records
Understand when data is an array vs single object:
| Endpoint Type | Data Format | Example |
|---|---|---|
| GET (collection) | Array of objects | GET /BizEntity → data: [...] |
| POST (create) | Single object | POST /BizEntity → data: {...} |
| PUT (update) | Single object | PUT /BizEntity → data: {...} |
| DELETE | Empty object or confirmation | DELETE /BizEntity → data: {} |
Detection Tip: Check recordCount - if it's 0 or 1, you likely have a single object. If > 1, it's an array.
Best Practices
Guidelines for robust error handling and response processing
Recommended
- Always check
response.Resultbefore processing data (even if HTTP 200) - Log exact error messages from
response.Messagefor debugging - Implement exponential backoff for 503 errors (1s, 2s, 4s, 8s)
- Handle both HTTP-level errors (exceptions) and business logic errors (
result: false) - Check
recordCountto handle empty result sets gracefully - Refresh tokens proactively before expiry (SDK: 30s, HTTP: 5min)
- Validate request payloads before sending to avoid 400 errors
Avoid
- Assuming HTTP 200 means success (always check
resultfield) - Ignoring the
messagefield when debugging errors - Processing
datawithout checkingresultfirst - Hardcoding error messages (they may evolve with API versions)
- Retrying 400 errors (validation failures won't succeed on retry)
- Letting tokens expire during operations (causes disruptive 401 errors)
- Treating all errors the same (different codes require different strategies)