Skip to content

API Standards Overview

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


Standards at a Glance

All Microtec ERP APIs follow a consistent contract enforced by shared Microtec packages, including Microtec.Web.Core, Microtec.Web.ApiVersioning, and Microtec.Web.Hosting.

StandardValue
VersioningURL path versioning: /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

See API Versioning for the canonical configuration, controller and minimal-endpoint patterns, Swagger documents, gateway setup, migration, and rollout checklist.


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
using Asp.Versioning;
using Microtec.Web.ApiVersioning;

[ApiController]
[ApiVersion(ApiVersions.V1)]
[Route(MicrotecApiVersioningDefaults.VersionedControllerRoute)]
[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 API version exposes its own OpenAPI document, such as /swagger/v1/swagger.json and /swagger/v2/swagger.json. Swagger UI is served at /swagger when the root EnableSwagger setting is enabled for the environment.

Do not register Swagger documents or version groups manually in a service. Shared API versioning creates the documents from endpoint metadata. See Swagger / OpenAPI for UI usage and API Versioning for document grouping and route substitution.


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