Skip to content

API Standards Overview

Section: 15 — API
Last Updated: 2026-05-30
Scope: Versioning, response envelope, validation, Swagger, gateway routing


Standards at a Glance

All Microtec ERP APIs follow a consistent contract enforced by the Microtec.Web.Core NuGet package.

StandardValue
VersioningURL path: /api/v1/, /api/v2/
Response formatAPIResponse<T> JSON envelope
ValidationFluentValidation (server-side)
DocumentationSwagger / OpenAPI 3.0 at /swagger
AuthJWT Bearer (Authorization: Bearer)
ContextERP Access Token (X-Access-Context)
Content typeapplication/json
Date formatISO 8601 UTC

URL Convention

/api/{version}/{domain}/{resource}[/{id}][/{sub-resource}]

Examples

GET  /api/v1/accounting/invoices
GET  /api/v1/accounting/invoices/{id}
POST /api/v1/accounting/invoices
PUT  /api/v1/accounting/invoices/{id}
DELETE /api/v1/accounting/invoices/{id}

GET  /api/v1/hr/employees
GET  /api/v1/hr/employees/{id}/contracts
POST /api/v1/hr/employees/{id}/contracts

GET  /api/v1/accounting/invoices?page=1&limit=20&search=INV-001

Naming Conventions

ActionHTTP MethodURL Pattern
List allGET/api/v1/{domain}/{resources}
Get by IDGET/api/v1/{domain}/{resources}/{id}
CreatePOST/api/v1/{domain}/{resources}
UpdatePUT/api/v1/{domain}/{resources}/{id}
Partial updatePATCH/api/v1/{domain}/{resources}/{id}
DeleteDELETE/api/v1/{domain}/{resources}/{id}
Dropdown / lookupGET/api/v1/{domain}/{resources}/dropdown
ExportGET/api/v1/{domain}/{resources}/export

Handler Naming Convention

Backend CQRS handlers follow a strict naming pattern:

OperationCommand/QueryHandler Name
CreateCommandAdd{Entity}Command
UpdateCommandEdit{Entity}Command
DeleteCommandDelete{Entity}Command
Get by IDQueryGetById{Entity}Query
Get all / listQueryGetAll{Entity}Query
Get dropdownQueryGetDropdown{Entity}Query
ExportQueryExport{Entity}Query

Response Envelope

See request-response.md for full envelope specification with examples.

All responses are wrapped in APIResponse<T>:

json
{
  "success": true,
  "data": { },
  "message": "Operation successful",
  "messageCode": null,
  "validationErrors": null,
  "meta": null
}

HTTP status codes are always meaningful:

  • 200 OK — successful read
  • 201 Created — resource created
  • 204 No Content — successful delete
  • 400 Bad Request — validation error
  • 401 Unauthorized — missing/invalid token
  • 403 Forbidden — insufficient permissions
  • 404 Not Found — resource not found
  • 409 Conflict — business rule violation
  • 422 Unprocessable Entity — semantic validation failure
  • 500 Internal Server Error — unexpected server error

FluentValidation

All command/query input DTOs have a corresponding AbstractValidator<T>:

csharp
public class AddEmployeeCommandValidator : AbstractValidator<AddEmployeeCommand>
{
    public AddEmployeeCommandValidator()
    {
        RuleFor(x => x.FirstName)
            .NotEmpty().WithMessage("First name is required")
            .MaximumLength(100);

        RuleFor(x => x.NationalId)
            .NotEmpty()
            .Length(10).WithMessage("National ID must be 10 digits")
            .Matches(@"^\d{10}$");

        RuleFor(x => x.HireDate)
            .NotEmpty()
            .LessThanOrEqualTo(DateTime.Today)
            .WithMessage("Hire date cannot be in the future");
    }
}

Validation errors are automatically mapped to APIResponse<T>.validationErrors by the MediatR validation pipeline behavior (in Microtec.Web.Core).


Controller Pattern

Every controller action must have:

csharp
[ApiController]
[Route("api/v1/[controller]")]
[Authorize]
public class InvoicesController : ControllerBase
{
    private readonly IMediator _mediator;

    [HttpGet]
    [ProducesResponseType(typeof(APIResponse<PagedResult<InvoiceDto>>), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(APIResponse<object>), StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> GetAll([FromQuery] GetAllInvoiceQuery query)
        => Ok(await _mediator.Send(query));

    [HttpGet("{id:guid}")]
    [ProducesResponseType(typeof(APIResponse<InvoiceDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(APIResponse<object>), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetById(Guid id)
        => Ok(await _mediator.Send(new GetByIdInvoiceQuery(id)));

    [HttpPost]
    [ProducesResponseType(typeof(APIResponse<Guid>), StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(APIResponse<object>), StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Create([FromBody] AddInvoiceCommand command)
    {
        var result = await _mediator.Send(command);
        return CreatedAtAction(nameof(GetById), new { id = result.Data }, result);
    }
}

IMPORTANT

[ProducesResponseType] attributes are mandatory on all controller actions. They are required for accurate Swagger documentation and are enforced in code review.


Swagger / OpenAPI

Each microservice exposes Swagger UI at:

https://{service-host}/swagger

Example (local dev):

http://localhost:5001/swagger    ← Accounting API
http://localhost:5002/swagger    ← HR API
http://localhost:5003/swagger    ← Inventory API

Swagger is disabled in production and enabled in dev/stage via environment configuration:

json
{
  "Features": {
    "EnableSwagger": true
  }
}

Swagger Groups

Large services (e.g., Accounting) group endpoints by domain area:

csharp
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1-invoices", new() { Title = "Accounting - Invoices", Version = "v1" });
    c.SwaggerDoc("v1-journals", new() { Title = "Accounting - Journals", Version = "v1" });
    c.SwaggerDoc("v1-reports", new() { Title = "Accounting - Reports", Version = "v1" });
});

Rate Limiting

All public endpoints are rate-limited via the API Gateway:

TierLimitWindow
Standard (authenticated)1000 req1 minute
Export endpoints10 req1 minute
Auth endpoints20 req1 minute
Unauthenticated10 req1 minute

Rate limit responses:

json
HTTP 429 Too Many Requests
Retry-After: 45

{
  "success": false,
  "data": null,
  "message": "Rate limit exceeded. Retry after 45 seconds.",
  "messageCode": 4290
}

Localization

API responses support Arabic and English:

Accept-Language: ar    ← Arabic error messages
Accept-Language: en    ← English (default)

Error messages in validationErrors are localized based on the Accept-Language header.


Internal Documentation — Microtec Platform Team