Documentation
Getting Started
Data Grid
Modeling
Business Rules
Approvals
Users, Roles & Security
Administration
Installation
Migrating from MDS
Architecture

REST API

The REST API lets external applications read and write entity records over HTTP. Access is controlled with API keys. Each key belongs to a service account — an API user — that takes part in the same role and permission system as a human user.

The API Users tab under Access Management, listing each service account with its key prefix, roles, status and call count
The API Users tab under Access Management, listing each service account with its key prefix, roles, status and call count(click to enlarge)

The API is for server-to-server use only. No CORS headers are set, so browser calls from another origin are blocked. Call it from a backend service, a script, or an integration platform.

Enabling and disabling the API

The global kill switch is in Settings → General Settings → API.

  • On (default) — valid API key requests are processed normally
  • Off — every request using X-API-Key returns 503 Service Unavailable, whatever the key

The toggle saves immediately and takes effect at once. Use it to suspend all external access without deleting a key.

Managing API users

Go to Settings → Access Management → API Users.

Each API user is a dedicated identity for one integration. It is subject to the same roles and permissions as a regular user, so assign only the roles the integration needs.

Create an API user:

  1. Click New API user.
  2. Enter a display name. This field is required.
  3. Optionally name the key. A label helps when you manage several.
  4. Assign one or more roles.
  5. Optionally set a key expiry date. Leave it blank for a key that never expires.
  6. Click Save.

The key is shown once, immediately after creation. Copy it now — it cannot be retrieved again. Only a SHA-256 hash is stored.

Edit — click a row to change the display name, roles, active status or expiry. The key is not affected.

Regenerate key — the old key is invalidated at once, and any integration using it gets 401 Unauthorized on its next request. The new key is shown once.

Delete — removes the service account. Calls using its key return 401 immediately.

The list shows the key prefix, roles, status (Active, Inactive, Expired or No key), call count, last use and expiry. Every column header sorts.

Authenticating requests

Send the key in the X-API-Key header on every request:

X-API-Key: prm_<your-key>

The format is prm_ followed by a random string. Never send the key as a query parameter or in the body.

Rate limiting

The API accepts 300 requests per minute per key. Requests over the limit return 429 with error code RATE_LIMITED. Callers without a key are limited by client address instead.

Response envelope

Every endpoint is under /api/v1/ and returns the same shape:

{
  "success": true,
  "data": { },
  "pagination": { "page": 1, "pageSize": 100, "total": 542, "totalPages": 6 }
}

Errors return:

{ "success": false, "error": { "code": "PERMISSION_DENIED", "message": "..." } }

Endpoints

Entities

MethodPathDescription
GET/api/v1/entitiesList the entities the API user can read
GET/api/v1/entities/:entityIdEntity metadata and attribute definitions

Records

MethodPathDescription
GET/api/v1/entities/:entityId/recordsList records, paginated
GET/api/v1/entities/:entityId/records/:recordIdOne record
POST/api/v1/entities/:entityId/recordsCreate a record
PUT/api/v1/entities/:entityId/records/:recordIdUpdate a record
DELETE/api/v1/entities/:entityId/records/:recordIdDelete a record
ParameterDefaultMaxDescription
page1Page number
pageSize1001000Records per page
searchFree-text search across code and name

Examples

1. Discover your entities. This gives you the entityId values the other calls need.

curl https://your-primentra-server/api/v1/entities \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34"
{
  "success": true,
  "data": [
    { "id": 3, "name": "Customer", "modelId": 1, "modelName": "CRM" },
    { "id": 7, "name": "Product",  "modelId": 2, "modelName": "Products" }
  ]
}

2. Inspect an entity's attributes. Check the attribute ids and data types before you write.

curl https://your-primentra-server/api/v1/entities/3 \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34"
{
  "success": true,
  "data": {
    "id": 3,
    "name": "Customer",
    "attributes": [
      { "id": 12, "name": "Region", "dataType": "Domain", "isRequired": true },
      { "id": 13, "name": "Tier", "dataType": "Text", "isRequired": false },
      { "id": 14, "name": "IsActive", "dataType": "Boolean", "isRequired": false }
    ]
  }
}

3. Read records.

curl "https://your-primentra-server/api/v1/entities/3/records?pageSize=50" \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34"

curl "https://your-primentra-server/api/v1/entities/3/records?search=Acme" \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34"

Records come back as flat objects. A domain attribute carries its row id, plus a _<FieldName>_display property with the readable label:

{
  "id": 101,
  "code": "CUST-001",
  "name": "Acme Corp",
  "createdAt": "2026-02-01T09:14:00",
  "modifiedAt": "2026-08-10T11:02:00",
  "Region": 5,
  "_Region_display": "{EU} Europe",
  "Tier": "Gold",
  "IsActive": 1
}

4. Create a record. The body needs code or name, or both. Pass attribute values in the values array.

curl -X POST https://your-primentra-server/api/v1/entities/3/records \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "CUST-050",
    "name": "New Customer Ltd",
    "values": [
      { "attributeId": 12, "intValue": 5 },
      { "attributeId": 13, "textValue": "Silver" },
      { "attributeId": 14, "intValue": 1 }
    ]
  }'

5. Update a record. Same body shape. Only the attributes present in values are changed.

curl -X PUT https://your-primentra-server/api/v1/entities/3/records/102 \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34" \
  -H "Content-Type: application/json" \
  -d '{ "code": "CUST-050", "name": "New Customer Ltd",
        "values": [ { "attributeId": 13, "textValue": "Gold" } ] }'

6. Delete a record.

curl -X DELETE https://your-primentra-server/api/v1/entities/3/records/102 \
  -H "X-API-Key: prm_Ab12XyZwQrstUv34"

Values array — field names by data type

Data typeField in valuesExample
TexttextValue"textValue": "Gold"
IntintValue"intValue": 42
DecimaldecimalValue"decimalValue": 9.99
BooleanintValue"intValue": 1 (1 = true, 0 = false)
DateTimedateTimeValue"dateTimeValue": "2026-01-15T00:00:00"
DomainintValue"intValue": 5 — the row id of the referenced record

Reading every page

Follow pagination.totalPages until you have them all:

import requests

BASE = "https://your-primentra-server/api/v1"
HEADERS = {"X-API-Key": "prm_Ab12XyZwQrstUv34"}

page, records = 1, []
while True:
    r = requests.get(f"{BASE}/entities/3/records", headers=HEADERS,
                     params={"page": page, "pageSize": 500}).json()
    records.extend(r["data"])
    if page >= r["pagination"]["totalPages"]:
        break
    page += 1

Error codes

CodeHTTPMeaning
VALIDATION_ERROR400Missing required field or invalid input
INVALID_KEY401Key not found or invalid
KEY_DISABLED401The key or its linked user is inactive
KEY_EXPIRED401The key is past its expiry date
PERMISSION_DENIED403The role lacks the permission for this operation
NOT_FOUND404Entity or record does not exist
RATE_LIMITED429More than 300 requests in one minute on this key
API_DISABLED503The global kill switch is off
INTERNAL_ERROR500Unexpected server error

Approval workflows and the API

Write operations through the REST API go straight to master data. They do not enter the approval workflow, even when the entity has Requires approval enabled.

This is intentional. An integration holding an API key is treated as a trusted automated system, on the assumption that the data was validated upstream. Audit log entries are written for every API write, so the changes stay traceable.

Staging bypasses approval for the same reason. If you need human review before data lands in production, build that gate into the source system.

Usage tracking

Every authenticated request is counted per key, with a breakdown by operation.

CounterIncremented on
TotalEvery authenticated request
ReadGET
CreatePOST
UpdatePUT
DeleteDELETE

The counters appear as R/C/U/D badges in the API Users table, in the dashboard's API keys widget, and as a total API calls card in the dashboard KPIs. Tracking is fire-and-forget and never slows a response.

─── Technical ───

Database tables

ColumnTableDescription
KeyHashApiKeysSHA-256 hash of the raw key. The raw key is never stored
KeyPrefixApiKeysFirst 12 characters of the key, shown in the UI
CallCountApiKeysIncremented on every authenticated request
LastUsedAtApiKeysTimestamp of the most recent request
ExpiresAtApiKeysNULL means the key never expires
IsDeletedApiKeysSoft delete. The row is kept for audit; the key is rejected
UserTypeUsers'api' for service accounts, 'standard' for people

Authentication flow

  1. The client sends X-API-Key: prm_<key>.
  2. The server hashes the key with SHA-256 and looks the hash up in ApiKeys.
  3. If the key is valid — not deleted, not inactive, not expired, and the API is not globally disabled — the request proceeds as the linked API user.
  4. CallCount is incremented and LastUsedAt updated.
  5. Role-based permission checks run exactly as they do for a human user.

Kill switch caching. The apiEnabled setting is cached in memory for 30 seconds. Toggling it in General Settings clears the cache at once, so the change is immediate. The 30-second window only matters when the setting is changed outside the application.

Ready to get started?

Start managing your master data with Primentra today.

View Pricing
REST API | Integration & API | Docs | Primentra