AtroCore is an API-centric application where the frontend communicates with the backend exclusively via REST API requests. Our REST API is built on OpenAPI (Swagger), allowing you to automatically generate a client in the programming language of your choice.
Every action you perform in the UI can be replicated through an API request. A good way to learn how the API works is to monitor the network tab in your browser's developer console (F12
).
All API requests must include the header Content-Type: application/json
. The base path for all API requests is /api/v1/
.
Best Practice: We recommend creating a separate API user with a specific role and limited permissions for all API calls.
Video Tutorials
For those who prefer video content, these tutorials provide a quick overview of key API concepts:
- How to Authorize: https://youtu.be/GWfNRvCswXg
- How to Select Specific Fields: https://youtu.be/i7o0aENuyuY
- How to Filter Data Records: https://youtu.be/irgWkN4wlkM
API Documentation
The REST API documentation is automatically generated for each AtroCore project based on its configurations and installed modules. You can access it at https://ATROCORE_INSTANCE_URL/apidocs/
.
For example, the documentation for our public demo instance is available at: Demo REST API. You can use the login admin
and password admin
in the basic authentication of the Swagger to explore the routes.
Authentication
To use the AtroCore API, you must first obtain an access token. Use the /api/v1/App/user
endpoint with Basic Authentication to get your token.
Step 1: Encode Basic Token
First, create a Base64-encoded string of your username and password in the format {username}:{password}
.
For example in Javascript
:
let basicToken = btoa('admin:admin');
// result => YWRtaW46YWRtaW4=
Step 2: Get the Authorization Token
Use the encoded token to make a GET request to the /App/user
endpoint with the Authorization-Token-Only
header set to true
.
GET /api/v1/App/user HTTP/1.1
Host: demo.atropim.com
Authorization: Basic YWRtaW46YWRtaW4=
Accept: application/json
Authorization-Token-Only: true
The response will contain your authorization token:
{
"authorizationToken": "*******************"
}
Now, include the header Authorization-Token: *******************
in all subsequent requests to authorize your calls.
For example, to get the instance generated metadata:
GET /api/v1/Metadata HTTP/1.1
Host: demo.atropim.com
Authorization-Token: *******************
Accept: application/json
Retrieving records list
When retrieving a list of records from the API, you can control the data returned using several query parameters. These parameters are combined in the URL to build complex queries for selecting, paginating, and filtering data.
Selecting Specific Fields
To retrieve only the fields you need, use the select
parameter with a comma-separated list of field names. This helps reduce the payload size and improves performance.
GET /api/v1/Product?select=id,name,isActive HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Note: The API may still return some additional fields, even if they aren't included in your
select
list. These are typically mandatory fields required by the backend or automatically included virtual fields.
Pagination
You can paginate through large record sets using the offset
and maxSize
parameters.
offset
: Specifies the number of records to skip from the beginning of the list.maxSize
: Defines the maximum number of records to return in a single response.
GET /api/v1/Product?select=id,name,isActive&offset=0&maxSize=2 HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Ordering
To sort the records, use the sortBy
and asc
parameters.
sortBy
: The name of the field to sort by.asc
: A boolean value (true
orfalse
) to specify the sort order.true
for ascending,false
for descending.
GET /api/v1/Product?select=id,name,isActive&offset=0&maxSize=2&sortBy=name&asc=false HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Filtering with the where
Parameter
To filter records, use the where
query parameter. This parameter uses an array notation to define a set of conditions. Each condition is an object with the following keys:
attribute
: The ID of the field or attribute you want to query.type
: The operator to use for the comparison (e.g.,equals
,like
,isNull
).value
: The value to compare against. This is omitted for operators that do not require a value (e.g.,isNull
).isAttribute
: (Optional) Set totrue
if you are querying an attribute instead of a standard field.
Example Request:
GET /api/v1/Product?where[0][attribute]=name&where[0][type]=like&where[0][value]=%test%&where[0][isAttribute]=false HTTP/1.1
Host: demo.atropim.com
Authorization-Token: ****************
This request filters the Product
records to find those where the name
field contains the string test
.
Backend Representation
This array notation is a web-friendly way of representing a structured backend array. For example, the query above corresponds to the following PHP array:
$where = [
[
"attribute" => 'name',
"type" => 'like',
"value" => '%test%',
"isAttribute" => false
]
];
For more complex queries and a full list of supported operators, consult the Advanced data querying section in the documentation. The array structures detailed there must be converted into the URL query string array notation for use in API requests.
Advanced API Features
This section provides details on non-standard API requests for common tasks like bulk operations and file uploads.
Bulk Create and Update
To perform bulk create and bulk update operations, use the upsert
action on the MassActions
endpoint. This action is idempotent: it attempts to find existing entities by their ID or unique fields. If an entity is found, it's updated; otherwise, a new one is created.
Endpoint:
POST https://ATROCORE_INSTANCE_URL/api/v1/MassActions/action/upsert
Payload Example:
The request body should be a JSON array of objects. Each object must specify the entity
type and the payload
containing the data to be processed.
[
{
"entity": "Product",
"payload": {
"name": "Apple iPhone 15",
"sku": "iphone15"
}
},
{
"entity": "Product",
"payload": {
"id": "2348924928743",
"name": "Apple iPhone 15 Pro Max"
}
}
]
You can make some tests in demo API here
File Upload
You can upload files to AtroCore through the File
entity using one of three methods, each requiring a different input format. In all cases, you must create a File
entity.
Upload by Base64 Content
This method is suitable for smaller files. The file's content is encoded in Base64 and sent directly in the request body.
Endpoint: POST /api/v1/File
Payload:
id
: (Optional) The unique file ID. If not provided, one will be generated.name
: (Required) The name of the file, including its extension (e.g.,"Test.txt"
).fileContents
: (Required) The Base64-encoded file data. The format must bedata:{{mimeType}};base64,{{base64EncodedContent}}
.
Example:
POST /api/v1/File HTTP/1.1
Host: demo.atropim.com
Authorization-Token: ****************************
Content-Type: application/json
:
{
"id": "a99060ec2fc0ddad2",
"name": "Test.txt",
"fileContents": "data:text/plain;base64,MTEx"
}
Upload by URL
This method is useful for uploading files that are already hosted elsewhere. The system will download the file from the provided URL.
Endpoint: POST /api/v1/File
Payload:
id
: (Optional) The unique file ID.name
: (Required) The file name.url
: (Required) The public URL of the file to be uploaded.
Example:
POST /api/v1/File HTTP/1.1
Host: demo.atropim.com
Authorization-Token: ****************************
Content-Type: application/json
:
{
"id": "a62860ec2fc0ddete",
"name": "picsum.txt",
"url": "https://picsum.photos/200/300"
}
Upload by Chunks (for Large Files)
For very large files, you must split the file into smaller pieces (chunks) and upload them individually. This process can be done asynchronously.
Endpoint: POST /api/v1/File
Payload:
id
: (Required) The file's unique ID. This ID must be consistent for all chunks of the same file.name
: (Required) The file name.fileUniqueHash
: (Required) A unique identifier for the entire file, distinct from the File entity ID.start
: (Required) The starting byte position of the current chunk.piece
: (Required) The Base64-encoded chunk data. The format must bedata:application/octet-stream;base64,{{base64EncodedContent}}
.piecesCount
: (Required) The total number of chunks for the file.
Example:
POST /api/v1/File HTTP/1.1
Host: demo.atropim.com
Authorization-Token: *******************
Content-Type: application/json
:
{
"id": "a99060ec2fc0dda33",
"name": "some.pdf",
"fileUniqueHash": "22551854",
"start": 0,
"piece": "data:application/octet-stream;base64,I4hYADzRed08...",
"piecesCount": 2
}
Response Handling:
- Partial Upload: If the response contains a
chunks
array, it means the chunk was successfully uploaded but the file is not yet complete.{ "chunks": [ "0", "2097152", "4194304", "6291456" ] }
- Complete Upload: When the last chunk is successfully uploaded, the response will include and
id
andname
, confirming the complete file has been created.{ "id": "acd7de2808e90041c", "name": "test.pdf", "chunks": [ "0", "2097152", "4194304", "6291456" ] }
JavaScript Example for Chunking:
The following JavaScript code demonstrates how to split a file into 2 MB chunks and prepare them for upload.
function slice(file, start, end) {
const sliceFn = file.slice || file.mozSlice || file.webkitSlice;
return sliceFn.call(file, start, end);
}
function createFilePieces(file, chunkSize, pieces) {
const piecesCount = Math.ceil(file.size / chunkSize);
let start = 0;
for (let i = 0; i < piecesCount; i++) {
const end = Math.min(start + chunkSize, file.size);
pieces.push({ start: start, piece: slice(file, start, end), piecesCount });
start = end;
}
}
function upload() {
const files = document.getElementById('fileInput').files;
if (!files[0]) {
alert('Please select a file.');
return;
}
const inputFile = files[0];
const chunkSize = 2 * 1024 * 1024; // 2 MB
const pieces = [];
createFilePieces(inputFile, chunkSize, pieces);
if (pieces.length < 2) {
alert('File is too small for chunking.');
return;
}
// Generate a unique ID for the file and a hash for all chunks
const fileId = "a99060ec2fc0dda33";
const fileHash = "22551854";
pieces.forEach(item => {
const reader = new FileReader();
reader.readAsDataURL(item.piece);
reader.onloadend = () => {
const uploadPayload = {
id: fileId,
name: inputFile.name,
fileUniqueHash: fileHash,
start: item.start,
piece: reader.result,
piecesCount: item.piecesCount
};
// Use fetch to send the payload to the API
fetch('https://demo.atropim.com/api/v1/File', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization-Token': '***************************' // Replace with your actual token
},
body: JSON.stringify(uploadPayload)
})
.then(response => response.json())
.then(data => {
console.log('Chunk uploaded successfully:', data);
})
.catch(error => {
console.error('Error uploading chunk:', error);
});
};
});
}
Creating Public URLs for Files
To create a publicly accessible URL for a file, you must create a Sharing entity. This entity acts as a key that links a specific file to a public download or view URL.
Step-by-Step Guide
-
Identify the file: You need the unique identifier (
fileId
) of the file you wish to share. -
Create a Sharing entity: Make a
POST
request to the/api/v1/Sharing
endpoint. The request body must be in JSON format and contain at least thefileId
of the file. You can also provide aname
to help identify the sharing record.Example Request:
POST /api/v1/Sharing HTTP/1.1 Host: your-instance.com Authorization-Token: *************** Content-Type: application/json Content-Length: 47 { "name": "My public version", "fileId": "a62860ec2fc0ddete" }
-
Process the response: If the request is successful, the API will return a JSON object containing details about the newly created Sharing entity. The publicly accessible URL is provided in the
link
property of this response.Example Response:
{ "id": "a01k5c597f5ef1s75edb6gkcbh0", "name": "My public version", "deleted": false, "active": true, "available": true, "link": "https://your-instance.com/sharings/a01k5c597f5ef1s75edb6gkcbh0.txt", "viewLink": "https://your-instance.com/sharings/a01k5c597f5ef1s75edb6gkcbh0.txt?view=1", "fileId": "a62860ec2fc0ddete", "fileName": "Test1.txt", "filePathsData": { "download": "https://your-instance.com/downloads/a62860ec2fc0ddete.", "thumbnails": { "small": null, "medium": null, "large": null } }, "createdAt": "2025-09-17 15:39:24", "modifiedAt": "2025-09-17 15:39:24", "createdById": "1", "modifiedById": "1", "modifiedByName": "Admin" }
The URL returned in the link
field is now public. You can share it for direct downloads or use it to embed files on external websites.
For more advanced options and parameters, such as controlling link expiry or setting a password, please refer to the Sharing API documentation.
Working with Attributes
In our system, attributes are treated as virtual fields that extend core entity fields. These fields are optional, meaning they may or may not appear in the API response depending on entity configuration and query parameters.
Attributes can be enabled or disabled per entity type. If an entity has attributes enabled (controlled by the Has Attributes
setting), then all behavior described below becomes available via the REST API.
Attribute Basics
Each attribute is stored with a unique internal ID and may optionally have a code. When a code is defined, it will be used as the key name for the virtual field in API responses. If no code is provided, the attribute ID is used instead.
Define a
code
for each attribute to produce more readable, developer-friendly API responses.
An attribute may generate multiple virtual fields, depending on its type. For example, an attribute representing a price with a unit might result in:
- a float field (e.g.,
priceAmount
), - a unit ID field (e.g.,
priceAmountUnitId
), - a unit name field,
- and additional supporting fields like
AllUnits
,UnitData
, etc.
attributesDefs
Field
When attributes are included in an API response, the system automatically adds the attributesDefs
field. This is a JSON object that describes each virtual field related to an attribute.
- Keys in
attributesDefs
correspond to virtual field names present in the entity. - Values provide metadata for the attribute such as
type
,attributeId
,label
, and more. - In collection responses, the metadata is minimized for performance.
- In single-record responses, full attribute configuration is returned.
This metadata allows clients to understand:
- what virtual fields are attribute-driven,
- how to render them,
- and which attribute each one is derived from.
Example: Fetching a Collection Without Attributes
GET /api/v1/Product?select=name&maxSize=1&offset=0&sortBy=name&asc=true HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Response:
{
"total": 60,
"list": [
{
"id": "a01...",
"name": "Example Product"
// no attributes included
}
]
}
Example: Include All Attributes
Use the allAttributes=true
query parameter to include all attributes for each item.
GET /api/v1/Product?select=name&maxSize=1&offset=0&sortBy=name&asc=true&allAttributes=true HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Response:
{
"list": [
{
"name": "Example Product",
"headlineText": "Some text",
"priceAttr": 100,
...
"attributesDefs": {
"headlineText": {
"attributeId": "a01...",
"type": "text"
},
"priceAttr": {
"attributeId": "a02...",
"type": "float"
},
"priceAttrUnit": {
"attributeId": "a02...",
"type": "link"
}
}
}
]
}
Example: Include Specific Attributes
Use the attributes
parameter to selectively load only specific attributes (by ID). This improves performance and allows sorting on the selected attributes.
GET /api/v1/Product?select=name&attributes=a01jzmpz4cze5da1gs5gq2shr4j,a01jz83qe81ebwsq2rfhpfvc2sp HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Example: Fetching a Single Record
Fetching a single record always includes all attribute values and full attribute metadata if attributes are enabled for that entity.
GET /api/v1/Product/a01jz56xg5xe09abkmfg4dr0kvj HTTP/1.1
Host: demo.atropim.com
Accept: application/json
Authorization-Token: ***************
Response (truncated):
{
"name": "Example Product",
"headlineText": "Some text",
"priceAttr": 100,
"priceAttrUnitId": "usd",
...
"attributesDefs": {
"headlineText": {
"attributeId": "a01...",
"label": "Main headline text",
"type": "text",
"readOnly": false,
"fullWidth": true
},
"priceAttr": {
"attributeId": "a02...",
"type": "float",
"measureId": "currency"
},
"priceAttrUnit": {
"attributeId": "a02...",
"type": "link",
"unitIdField": true
}
}
}
Summary
- Attributes are optional virtual fields and must be enabled per entity via the
Has Attributes
setting. - One attribute may produce multiple virtual fields (value, unit, etc.).
- Use
attributesDefs
in the API response to understand attribute-related virtual fields. - Use
allAttributes=true
to load all attributes orattributes=...
to request specific ones. - Use attribute codes for clean, readable keys in your data payloads.
Undocumented or Partially Documented Endpoints
While we strive to provide comprehensive and up-to-date documentation for all available REST API endpoints, there may still be actions or endpoints that are either undocumented or documented only partially. This is an active area of improvement, and we continuously work to close these gaps and ensure complete coverage.
Our application is fully API-centric—every interaction within the system is ultimately driven by API calls. Therefore, if you notice that a particular action available through the user interface is not clearly reflected in the API documentation, you may be able to identify the underlying request yourself.
To investigate:
- Open your browser's Developer Tools (
F12
orCtrl+Shift+I
). - Navigate to the Network tab.
- Perform the desired action within the UI.
- Observe which HTTP request(s) are triggered by the frontend.
- Analyze the endpoint, method (GET, POST, etc.), payload, and response to understand the API interaction.
The system sends a high volume of requests, including background polling and realtime communication. For example:
/data/publicData.json
is a regularly triggered request essential for frontend-backend synchronization./listening/entity/Product/{id}.json
facilitates realtime updates.
Because of this ongoing activity, the Network tab may include many unrelated requests. It’s important to correctly identify the one you're interested in.
Use the filtering tools in Developer Tools to narrow your focus:
- Filter by HTTP method (e.g., only POST requests)
- Specifically, we recommend filtering by
/api/v1/
to isolate requests that interact with the REST API—this helps exclude internal or non-REST traffic like configuration files and realtime listeners. - Sort by timestamp or response status to locate requests triggered by direct UI actions
This technique enables developers to reverse-engineer undocumented API interactions and gain a deeper understanding of system behavior. If you discover helpful endpoints through this process, we encourage you to share them—your feedback helps improve our documentation and benefits the entire developer community.
Thank you for supporting a transparent and developer-first ecosystem.