Quick Reference

2 Response Structures

📦

APIResponse (73 Endpoints)

Structure: { result, message, recordCount, data }

Fields:

  • result (bool) - Success indicator
  • message (string) - Error details
  • recordCount (int) - Record count
  • data (dynamic) - Response payload

Used for: All GET, POST, PUT, DELETE operations

🔐

TokenResponse (2 Endpoints Only)

Structure: { accessToken, refreshToken }

Fields:

  • accessToken - JWT for API requests
  • refreshToken - 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.

json
// 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 exp claim 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 tokens
  • GET /User/Status - API health check
  • GET /Common/Lookup - Retrieve lookup categories
  • GET /Common/HealthCheck - Health monitoring

All other 71 endpoints require authentication (valid access token in Authorization header).

json
// 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 requests

HTTP 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

1

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 result field
  • Implement manual retry logic for 503 errors with exponential backoff
  • Track token expiry and refresh proactively (5 minutes before expiry)
json
// 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}");
}
2

.NET SDK Approach

Layered error handling with exceptions for HTTP errors and result checking for business logic.

Strategy

  • Level 1: Catch HttpRequestException for HTTP-level errors (401, 500, network failures)
  • Level 2: Check response.Result for business logic errors (validation, not found, conflicts)
  • SDK handles token refresh automatically (30 seconds before expiry)
  • Built-in retry logic for token expiry scenarios
json
// 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}");
}
3

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.message for 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:

json
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 /BizEntitydata: [...]
POST (create) Single object POST /BizEntitydata: {...}
PUT (update) Single object PUT /BizEntitydata: {...}
DELETE Empty object or confirmation DELETE /BizEntitydata: {}

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.Result before processing data (even if HTTP 200)
  • Log exact error messages from response.Message for debugging
  • Implement exponential backoff for 503 errors (1s, 2s, 4s, 8s)
  • Handle both HTTP-level errors (exceptions) and business logic errors (result: false)
  • Check recordCount to 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 result field)
  • Ignoring the message field when debugging errors
  • Processing data without checking result first
  • 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)

What's Next?

Continue your journey with these related concepts: