LoktaAPI

Conventions

Rules that hold across every endpoint in the Reference. Read once — the per-endpoint pages assume all of this.

Base URL & tenant

All paths hang off {host}/lokta-lms/api/v1. The platform is multi-tenant: every request names its tenant with the Tenant-Identifier header (this instance's default tenant is default). A missing or wrong tenant header fails the request before authentication is even considered.

Authentication

HTTP Basic with an application user's credentials, on every request — there is no session or token handshake to keep alive.

curl '{host}/lokta-lms/api/v1/loans' \
  -u '{username}:{password}' \
  -H 'Tenant-Identifier: default'

What a user may call is governed by their roles' permissions (see Roles & Permissions in the reference); a valid login without the right permission gets a 403 with the missing permission named in the error body.

Dates & locale

The write contract is explicit about how it should be read: every body that carries a date also carries the format that date is written in.

{
  "locale": "en",
  "dateFormat": "dd MMMM yyyy",
  "expectedDisbursementDate": "19 August 2026"
}

Commands

State changes are commands, not bare updates: POST to the resource with a ?command= query parameter. The same URL performs different transitions depending on the command.

POST /v1/loans/{loanId}?command=approve
POST /v1/loans/{loanId}?command=disburse
POST /v1/loans/{loanId}/transactions?command=repayment
POST /v1/loans/{loanId}/transactions/{transactionId}?command=undowriteoff

Command bodies follow the date convention above; the response echoes the affected ids (resourceId, sometimes subResourceId and a changes map).

Pagination & sorting

List endpoints take offset and limit (plus orderBy / sortOrder where the reference shows them). Paged responses wrap results as:

{ "totalFilteredRecords": 240, "pageItems": [ … ] }

Endpoints without those parameters return the full list. Field filtering with ?fields=a,b,c trims response payloads.

Errors

Failures return a consistent envelope. The outer message summarises; the errors array names each offending parameter with a stable globalisation code you can key translations or handling off.

{
  "developerMessage": "The request was invalid…",
  "httpStatusCode": "400",
  "defaultUserMessage": "Validation errors exist.",
  "userMessageGlobalisationCode": "validation.msg.validation.errors.exist",
  "errors": [
    {
      "developerMessage": "The parameter `dueDate` is mandatory.",
      "defaultUserMessage": "The parameter `dueDate` is mandatory.",
      "userMessageGlobalisationCode": "validation.msg.loancharge.dueDate.cannot.be.blank",
      "parameterName": "dueDate"
    }
  ]
}

Idempotency

Money-movement commands accept an Idempotency-Key header. Retrying the same command with the same key returns the original result instead of posting twice — use it on repayments and disbursements issued over unreliable links.