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:

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 or false) 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 to true 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 be data:{{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 be data: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 and name, 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

  1. Identify the file: You need the unique identifier (fileId) of the file you wish to share.

  2. 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 the fileId of the file. You can also provide a name 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"
    }
  3. 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 or attributes=... 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:

  1. Open your browser's Developer Tools (F12 or Ctrl+Shift+I).
  2. Navigate to the Network tab.
  3. Perform the desired action within the UI.
  4. Observe which HTTP request(s) are triggered by the frontend.
  5. 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.