Skip to content

Request & Response Envelope

Section: 15 — API
Last Updated: 2026-05-30
Scope: APIResponse<T>, error format, pagination, validation errors


APIResponse<T> — The Standard Envelope

Every API response in Microtec ERP is wrapped in the APIResponse<T> envelope defined in Microtec.Contracts (Microtec.Domain NuGet package).

Success Response

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "invoiceNumber": "INV-2026-00123",
    "totalAmount": 15000.0000,
    "status": "Draft"
  },
  "message": "Invoice retrieved successfully",
  "messageCode": null,
  "validationErrors": null,
  "meta": null
}

Paginated List Response

json
{
  "success": true,
  "data": [
    { "id": "...", "invoiceNumber": "INV-2026-00123" },
    { "id": "...", "invoiceNumber": "INV-2026-00124" }
  ],
  "message": null,
  "messageCode": null,
  "validationErrors": null,
  "meta": {
    "total": 250,
    "page": 1,
    "limit": 20,
    "totalPages": 13,
    "hasNextPage": true,
    "hasPreviousPage": false
  }
}

Create Response (201)

json
HTTP 201 Created
Location: /api/v1/accounting/invoices/550e8400-e29b-41d4-a716-446655440000

{
  "success": true,
  "data": "550e8400-e29b-41d4-a716-446655440000",
  "message": "Invoice created successfully",
  "messageCode": null,
  "validationErrors": null,
  "meta": null
}

NOTE

Create operations return the new resource's GUID in data, not the full object. The client uses the returned ID to fetch the full resource if needed.


Error Responses

Validation Error (400)

json
HTTP 400 Bad Request

{
  "success": false,
  "data": null,
  "message": "One or more validation errors occurred",
  "messageCode": null,
  "validationErrors": [
    {
      "field": "invoiceDate",
      "message": "Invoice date cannot be in the future"
    },
    {
      "field": "customerId",
      "message": "Customer is required"
    },
    {
      "field": "lines[0].quantity",
      "message": "Quantity must be greater than 0"
    }
  ],
  "meta": null
}

Business Rule Error (409 Conflict)

json
HTTP 409 Conflict

{
  "success": false,
  "data": null,
  "message": "Employee already has an active contract for this period",
  "messageCode": 9017,
  "validationErrors": null,
  "meta": null
}

Not Found (404)

json
HTTP 404 Not Found

{
  "success": false,
  "data": null,
  "message": "Invoice with ID '550e8400-...' was not found",
  "messageCode": 4040,
  "validationErrors": null,
  "meta": null
}

Unauthorized (401)

json
HTTP 401 Unauthorized

{
  "success": false,
  "data": null,
  "message": "Authentication required",
  "messageCode": 4010,
  "validationErrors": null,
  "meta": null
}

Forbidden (403)

json
HTTP 403 Forbidden

{
  "success": false,
  "data": null,
  "message": "You do not have permission to approve invoices",
  "messageCode": 4030,
  "validationErrors": null,
  "meta": null
}

Server Error (500)

json
HTTP 500 Internal Server Error

{
  "success": false,
  "data": null,
  "message": "An unexpected error occurred. Please contact support with reference: REF-2026-05-30-abc123",
  "messageCode": 5000,
  "validationErrors": null,
  "meta": null
}

IMPORTANT

500 responses include a reference code (logged in Seq) but never expose stack traces, exception messages, or internal details in the response body.


The APIResponse<T> Class

csharp
// From Microtec.Contracts (Microtec.Domain NuGet package)
public class APIResponse<T>
{
    public bool Success { get; set; }
    public T? Data { get; set; }
    public string? Message { get; set; }
    public int? MessageCode { get; set; }
    public IEnumerable<ValidationError>? ValidationErrors { get; set; }
    public PaginationMeta? Meta { get; set; }

    // Factory methods
    public static APIResponse<T> Ok(T data, string? message = null)
        => new() { Success = true, Data = data, Message = message };

    public static APIResponse<T> Fail(string message, int? code = null)
        => new() { Success = false, Message = message, MessageCode = code };

    public static APIResponse<T> ValidationFail(IEnumerable<ValidationError> errors)
        => new()
        {
            Success = false,
            Message = "One or more validation errors occurred",
            ValidationErrors = errors
        };
}

public record ValidationError(string Field, string Message);

public class PaginationMeta
{
    public int Total { get; set; }
    public int Page { get; set; }
    public int Limit { get; set; }
    public int TotalPages => (int)Math.Ceiling((double)Total / Limit);
    public bool HasNextPage => Page < TotalPages;
    public bool HasPreviousPage => Page > 1;
}

Pagination

Request Parameters

All list endpoints accept standard pagination query parameters:

ParameterTypeDefaultMaxDescription
pageint1Page number (1-based)
limitint20100Items per page
searchstringFull-text search term
sortBystringField name to sort by
sortDirstringascasc or desc
GET /api/v1/accounting/invoices?page=2&limit=50&search=INV-2026&sortBy=invoiceDate&sortDir=desc

PagedResult<T>

csharp
public class PagedResult<T>
{
    public IEnumerable<T> Items { get; set; } = [];
    public int Total { get; set; }
    public int Page { get; set; }
    public int Limit { get; set; }
}

Used with APIResponse<PagedResult<T>>:

json
{
  "success": true,
  "data": {
    "items": [...],
    "total": 250,
    "page": 2,
    "limit": 50
  },
  "meta": {
    "total": 250,
    "page": 2,
    "limit": 50,
    "totalPages": 5,
    "hasNextPage": true,
    "hasPreviousPage": true
  }
}

NOTE

meta at the top level mirrors the pagination info from data. Clients may use either. The meta field is present whenever data is a paginated list.


MessageCode Reference

messageCode is a numeric code for programmatic error handling (independent of locale):

RangeCategory
2xxSuccess codes (rarely used)
4010–4019Authentication errors
4030–4039Authorization / permission errors
4040–4049Not found errors
4090–4099Conflict / business rule violations
4290Rate limit exceeded
5000–5099Server errors
9000–9099HR domain errors
9100–9199Accounting domain errors
9200–9299Inventory domain errors
9300–9399Sales domain errors

Frontend applications use messageCode to show localized messages stored in the Angular translation files rather than relying on the server-sent message string.


Date and Number Formats

Dates

All dates in request/response bodies use ISO 8601 UTC:

json
{
  "invoiceDate": "2026-05-30T00:00:00Z",
  "createdOn":   "2026-05-30T14:23:45.123Z",
  "hireDate":    "2026-01-01"
}

NOTE

Date-only fields (like hireDate) use YYYY-MM-DD format without time. DateTime fields always include timezone (Z for UTC or +03:00 for AST).

Decimal Numbers

Decimal values are serialized as JSON numbers (not strings) with up to 4 decimal places:

json
{
  "unitPrice": 150.5000,
  "vatAmount": 22.5750,
  "totalAmount": 173.0750
}

Content Negotiation

HeaderAccepted Values
Content-Typeapplication/json (required for POST/PUT/PATCH)
Acceptapplication/json (default), application/pdf (export endpoints)
Accept-Languageen (default), ar

Request Headers

HeaderRequiredDescription
AuthorizationYesBearer {keycloak-jwt}
X-Access-ContextYes (ERP endpoints)ERP Access Context Token
Content-TypeYes (body requests)application/json
Accept-LanguageNoen or ar
X-Api-KeyInternal only3bb564df-0f24-4ea6-82c1-d99f368cac8a for direct service-to-service

Internal Documentation — Microtec Platform Team