API Keys
Overview
The REST API lets external applications read and write entity records programmatically over HTTP. Access is controlled with API keys — each key is tied to a service account (API user) that participates in the same role and permission system as regular users.

The API is designed for server-to-server use only. No CORS headers are set — browser-based calls from a different origin will be blocked. Always call the API from a backend service, script, or integration platform.
Enabling / Disabling the API
There is a global kill switch in Admin → General Settings → API tab.
- On (default): all valid API key requests are processed normally
- Off: every request using
X-API-Keyreturns503 Service Unavailable, regardless of key validity
Use this to temporarily suspend all external access without deleting any keys.
Managing API Users
Go to Admin → Access Management → API Users to create and manage API users.
Each API user is a service account — a dedicated identity for one external integration. API users are subject to the same roles and permissions as regular users. Assign only the roles the integration actually needs.
Create an API user:
- Go to Admin → Access Management → API Users and click New API user
- Enter a display name (required)
- Assign one or more roles — the API user inherits all permissions from those roles
- Optionally set an expiry date (leave blank for no expiry)
- Click Save
The API key is shown once immediately after creation. Copy it now — it cannot be retrieved again. Only a SHA-256 hash is stored in the database.
Edit an API user: Click any row to open the edit form. You can change the display name, role assignments, active status, or expiry date. The key is not affected.
Regenerate a key: Click Regenerate key on the API user row. The old key is immediately invalidated — any integration using it will receive 401 Unauthorized on the next request. The new key is shown once.
Delete an API user: Removes the service account. All existing calls using that key immediately return 401 Unauthorized.
Authenticating Requests
Send the API key in every request as the X-API-Key header:
The key format is prm_ followed by a random string. Never send the key as a query parameter or in the request body.
REST API Reference
All endpoints are under /api/v1/. Every response uses the same envelope format:
Errors return:
Entities
| Method | Path | Description |
|---|---|---|
GET | /api/v1/entities | List all entities the API user can access |
GET | /api/v1/entities/:entityId | Get entity metadata and attribute definitions |
Records
| Method | Path | Description |
|---|---|---|
GET | /api/v1/entities/:entityId/records | List records (paginated) |
GET | /api/v1/entities/:entityId/records/:id | Get a single record |
POST | /api/v1/entities/:entityId/records | Create a record |
PUT | /api/v1/entities/:entityId/records/:id | Update a record |
DELETE | /api/v1/entities/:entityId/records/:id | Delete a record |
Pagination parameters (on GET /records):
| Parameter | Default | Max | Description |
|---|---|---|---|
page | 1 | — | Page number |
pageSize | 100 | 1000 | Records per page |
search | — | — | Free-text search across Code and Name |
Approval workflows and the API
Write operations through the REST API (POST, PUT, DELETE) write directly to the master data — they do not go through the approval workflow, even if the entity has Requires approval enabled.
This is intentional. API integrations are treated as trusted automated systems. If you give an integration write access via an API key, the assumption is that the data has already been validated upstream.
Audit log entries are still created for every API write, so all changes remain traceable.
Usage Tracking
Every API request is automatically counted per key. Primentra tracks both the total call count and a CRUD breakdown:
| Counter | Incremented on |
|---|---|
| Total | Every authenticated API request |
| Read | GET requests (list/get records) |
| Create | POST requests (new records) |
| Update | PUT requests (modify records) |
| Delete | DELETE requests (remove records) |
These counters are visible in:
- Admin → API Users table — per-key totals with R/C/U/D badges
- Dashboard → API Keys widget — aggregated across all keys
- Dashboard → Key Metrics — total API calls KPI card
Tracking is fire-and-forget — it never slows down API responses.
Practical Examples
1. Discover your entities
First, list all entities the API user has access to. This gives you the entityId values you need for subsequent calls.
Response:
2. Inspect an entity's attributes
Before creating or updating records, check what attributes the entity has and what data types they use.
Response:
3. Read records
Records are returned as flat objects. Domain attributes include a _FieldName_display property with the human-readable label:
4. Create a record
The request body requires code or name (or both). Pass additional attribute values in the values array, each with an attributeId and the appropriate value field.
Response:
5. Update a record
Same body shape as create. Only fields present in values are updated — omitted attributes are left unchanged.
6. Delete a record
Values array — field names by data type
When creating or updating records, use the correct field name in each values entry:
| Attribute data type | Field in values | Example |
|---|---|---|
| Text | textValue | "textValue": "Gold" |
| Int | intValue | "intValue": 42 |
| Decimal | decimalValue | "decimalValue": 9.99 |
| Boolean | intValue | "intValue": 1 (1 = true, 0 = false) |
| DateTime | dateTimeValue | "dateTimeValue": "2026-01-15T00:00:00" |
| Domain | intValue | "intValue": 5 (foreign key ID of the domain record) |
Python example
JavaScript / Node.js example
Error Codes
| Code | HTTP | Meaning |
|---|---|---|
API_DISABLED | 503 | Global API kill switch is off |
INVALID_KEY | 401 | Key not found or invalid |
KEY_DISABLED | 401 | Key or linked user is inactive |
KEY_EXPIRED | 401 | Key is past its expiry date |
PERMISSION_DENIED | 403 | Role lacks required permission for the operation |
NOT_FOUND | 404 | Entity or record does not exist |
VALIDATION_ERROR | 400 | Missing required field or invalid input |
INTERNAL_ERROR | 500 | Unexpected server error |
─── Technical ───
Database tables: `ApiKeys`, `Users` (UserType = 'api')
| Column | Table | Description |
|---|---|---|
KeyHash | ApiKeys | SHA-256 hash of the raw key — raw key is never stored |
KeyPrefix | ApiKeys | First 12 characters of the raw key, shown in the UI for identification |
CallCount | ApiKeys | Incremented on every successful authenticated request |
LastUsedAt | ApiKeys | Timestamp of the most recent authenticated request |
ExpiresAt | ApiKeys | NULL = never expires |
IsDeleted | ApiKeys | Soft-delete — row is retained for audit, key is rejected on auth |
UserType | Users | 'api' for service accounts, 'standard' for human users |
Authentication flow:
- Client sends
X-API-Key: prm_<key>in the request header - Server computes SHA-256 of the key and looks up the hash in
ApiKeys - If found and valid (not deleted, not inactive, not expired, API not globally disabled): request proceeds as the linked API user
- On success:
CallCountis incremented andLastUsedAtis updated - Role-based permission checks run identically to regular user requests
Kill switch caching: The global API enabled/disabled setting is cached in memory for up to 30 seconds to reduce database load. After toggling the kill switch, allow up to 30 seconds before the change takes effect on all requests.