Custom Data Layers API
Create, manage, version, and query proprietary geospatial vector layers (points, polylines, polygons) directly hosted on the Sovereign Cloud GIS infrastructure.
Overview & Solution
Enterprises possess unique spatial assets - such as private warehouse footprints, fiber cable conduits, internal dealership polygons, and security zones. The Custom Data Layers API allows creating and updating dedicated spatial tables with full CRUD capabilities.
Before a layer can be used through this API, its schema - the set of fields it holds and their types - is defined through the Intelomatic User interface.
Industry Use Cases
1. Enterprise Asset Management
- Infrastructure Inventories: Maintain live maps of cell towers, solar inverters, electric vehicle charging bays, and utility pipelines.
2. Custom Business Geographies
- Dynamic Sales Territories: Update distributor territory boundary polygons without waiting for IT engineering releases.
3. Supply Chain Restricted Corridors
- No-Entry & Hazardous Zones: Publish dynamic no-go polygons for autonomous drones and transport fleets.
API reference
Custom Integrations · Requires Intelomatic tenant
Field definitions: Field data types
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /api/app-base/data-layer/{layerName}/layer-detail | Get a layer's field definitions (schema) |
POST | /api/app-base/data-layer/{layerName} | Create a record |
POST | /api/app-base/data-layer/{layerName}/search | Search records |
PUT | /api/app-base/data-layer/{layerName}/{id} | Update a record |
DELETE | /api/app-base/data-layer/{layerName}/{id} | Delete a record |
GET | /api/app-base/data-layer/{layerName}/{id}/detail | Get a single record with all its values |
All requests require a bearer token and the api-key query parameter:
Authorization: Bearer <access_token>
?api-key=<apiKey>
Get a layer's field definitions (schema)
POST /api/app-base/data-layer/{layerName}/layer-detail
Request Parameters
| Parameter | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
layerName | path | string | Yes | Data layer name | SAMPLE_DATA, CUSTOMER, PRODUCT |
properties | body | object | No | ||
properties.layerSettings | body | boolean | No | ||
properties.isTakeAll | body | boolean | No |
Request body
{
"properties": {
"layerSettings": true,
"isTakeAll": true
}
}
Response Schema
200 OK
{
"layerName": "SAMPLE_DATA",
"version": 8,
"properties": [
{
"propertyName": "sample_data_invoice_number",
"displayName": "Invoice number",
"dataType": 3,
"required": false,
"isSystem": false,
"isAdditional": true
},
{
"propertyName": "sample_data_option",
"displayName": "Option",
"dataType": 10,
"required": false,
"isSystem": false,
"isAdditional": true
}
],
"layerSettings": {}
}
| Field | Type | Description |
|---|---|---|
layerName | string | Layer the schema belongs to. |
version | integer | Schema version. It increases every time the layer definition is changed - compare it to detect that your integration is reading an outdated field list. |
properties | array | One entry per field on the layer. This is the list of keys a create or update body may use. |
properties[] | object | |
properties[].propertyName | string | |
properties[].displayName | string | |
properties[].dataType | integer | |
properties[].required | boolean | |
properties[].isSystem | boolean | |
properties[].isAdditional | boolean | |
layerSettings | object | Layer-level configuration, returned when layerSettings: true is requested. |
Read this before writing to the layer. properties[] is the authoritative field list - a create or update body may only use these propertyName keys.
| Property attribute | Meaning |
|---|---|
propertyName | The key to send in a request body |
displayName | Human label shown in the platform UI - never sent to the API |
dataType | Numeric type code. See Field data types for the codes and the JSON value each expects |
required | true means the field must be present on create |
isSystem | Set by the platform. Do not send these, except Title and Description |
isAdditional | A custom field defined for this tenant |
Fields of dataType 10 (List) also carry their allowed values, or the layer they read from and any dependency on another field - see Field data types.
Create a record
POST /api/app-base/data-layer/{layerName}
Request Parameters
| Parameter | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
layerName | path | string | Yes | Layer the new record is created in. The body must use that layer's field names | SAMPLE_DATA, CUSTOMER, PRODUCT |
Title | body | string | No | ||
Description | body | string | No | ||
sample_data_date | body | string | No | ||
sample_data_invoice_number | body | string | No | ||
sample_data_customer_address | body | string | No | ||
sample_data_mobile_number | body | string | No | ||
sample_data_geocode | body | string | No | ||
sample_data_option | body | string | No | ||
sample_data_state | body | string | No | ||
sample_data_district | body | string | No | ||
sample_data_sub_district | body | string | No | ||
sample_data_remarks | body | string | No |
Request body
{
"Title": "INV-2026-0042",
"Description": "Record created through the API",
"sample_data_date": "2026-09-07T09:30:00Z",
"sample_data_invoice_number": "INV-2026-0042",
"sample_data_customer_address": "12 MG Road, Pune, MAHARASHTRA - 411001",
"sample_data_mobile_number": "919000000042",
"sample_data_geocode": "{\"type\":\"Point\",\"coordinates\":[73.8567,18.5204]}",
"sample_data_option": "1",
"sample_data_state": "13",
"sample_data_district": "267",
"sample_data_sub_district": "01823",
"sample_data_remarks": "Created from the API reference example"
}
More request examples
SAMPLE_DATA layer
{
"Title": "INV-2026-0042",
"Description": "Record created through the API",
"sample_data_date": "2026-09-07T09:30:00Z",
"sample_data_invoice_number": "INV-2026-0042",
"sample_data_customer_address": "12 MG Road, Pune, MAHARASHTRA - 411001",
"sample_data_mobile_number": "919000000042",
"sample_data_geocode": "{\"type\":\"Point\",\"coordinates\":[73.8567,18.5204]}",
"sample_data_option": "1",
"sample_data_state": "13",
"sample_data_district": "267",
"sample_data_sub_district": "01823",
"sample_data_remarks": "Created from the API reference example"
}
SAMPLE_DATA layer - minimum
{
"Title": "INV-2026-0043",
"sample_data_invoice_number": "INV-2026-0043"
}
CUSTOMER layer
{
"customer_full_name": "Sample Record",
"customer_phone_number": "919000000001",
"customer_category": "Default",
"customer_postal_code": "412108",
"customer_address": "Mulshi, Pune, MAHARASHTRA - 412108",
"customer_location": "{\"coordinates\":[73.55986487231496,18.462925230845542],\"type\":\"Point\"}",
"customer_is_active": "true"
}
Response Schema
200 OK
{
"Id": "00000000-0000-0000-0000-000000000001",
"Layer": "SAMPLE_DATA",
"Title": "INV-2026-0042",
"CreatedDate": "2026-09-07T09:30:12Z",
"CreatedUser": "<user>",
"sample_data_invoice_number": "INV-2026-0042",
"sample_data_created_from": 3
}
| Field | Type | Description |
|---|---|---|
Id | string | Identifier of the record just created. Keep it - update, detail and delete all address the record by this value. |
Layer | string | Layer the record was written to, echoing the layerName in the path. |
Title | string | Display label of the record. |
CreatedDate | string | Server timestamp of the creation, in UTC. |
CreatedUser | string | Username of the account whose token made the call. |
sample_data_invoice_number | string | Example of a custom field, returned with the value you sent. |
sample_data_created_from | integer | Channel the record originated from, set by the platform: 1 Mobile, 2 Web, 3 Others. API calls get 3. |
How to build the body for your own layer
The body is a flat JSON object keyed by propertyName. Call layer-detail on the layer first and send the properties it lists - anything else is ignored. Every value must respect the field's declared data type. The example above is the demo SAMPLE_DATA layer.
See Field data types for the dataType codes, the JSON value each one expects, and how list fields and their dependency chains work.
Do not send system fields. Id, CreatedDate, CreatedUser, ModifiedDate, ModifiedUser and the *_created_from / *_last_modified_from markers are set by the platform. Title and Description are system fields you may set - Title is the record's display label.
Validators declared on a field are enforced. On SAMPLE_DATA, sample_data_mobile_number must be 8-12 characters.
No field on SAMPLE_DATA is mandatory, so a create call with only Title succeeds - but a layer of your own may declare required fields, which layer-detail reports as required: true.
Search records
POST /api/app-base/data-layer/{layerName}/search
Request Parameters
| Parameter | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
layerName | path | string | Yes | Data layer name | SAMPLE_DATA, CUSTOMER, PRODUCT |
skip | body | integer | No | ||
take | body | integer | No |
Request body
{
"skip": 0,
"take": 20
}
skip and take are paging properties:
skip- number of records to skip before the page starts.0is the first page.take- maximum number of records returned in this page.-1returns every match, which is only safe on small datasets.
The response reports total, the number of records matching the query regardless of paging - keep requesting pages until skip + take reaches it.
Response Schema
200 OK
{
"data": [
{
"Id": "00000000-0000-0000-0000-000000000001",
"Layer": "CUSTOMER",
"Path": "/…",
"CreatedDate": "2026-07-15T11:15:45Z",
"CreatedUser": "admin_apidemo",
"Title": "Sample Record",
"customer_full_name": "Sample Record",
"customer_phone_number": "919000000001"
}
],
"total": 1
}
| Field | Type | Description |
|---|---|---|
data | array | The page of matching records. Each entry carries the layer's own fields plus the system fields every record has. |
data[] | object | |
data[].Id | string | |
data[].Layer | string | |
data[].Path | string | |
data[].CreatedDate | string | |
data[].CreatedUser | string | |
data[].Title | string | |
data[].customer_full_name | string | |
data[].customer_phone_number | string | |
total | integer | Total number of records matching the query, ignoring skip and take - use it to drive paging. |
Update a record
PUT /api/app-base/data-layer/{layerName}/{id}
Request Parameters
| Parameter | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
layerName | path | string | Yes | Layer holding the record to update | SAMPLE_DATA, CUSTOMER, PRODUCT |
id | path | string | Yes | Id of the record to update | cd8588a9-52ff-4817-881e-e6a801ff4eb5 |
sample_data_remarks | body | string | No |
Request body
{
"sample_data_remarks": "Updated remark"
}
Response Schema
200 OK
{
"Id": "cd8588a9-52ff-4817-881e-e6a801ff4eb5",
"id": "cd8588a9-52ff-4817-881e-e6a801ff4eb5",
"Layer": "ZFLCFX_SAMPLE_DATA",
"Name": "f7b9ed17-9206-4ded-a1b5-45e3bc8ffcf9",
"Title": null,
"Description": null,
"Created": "2026-09-08T03:45:47.479Z",
"Creator": "apidemo_sovereignsolutions",
"Modified": "2026-09-08T03:45:47.479Z",
"Modifier": "apidemo_sovereignsolutions",
"Path": "/<internal storage path>",
"ParentIdPath": "/2/6025/6053/99689425",
"NodeId": 99772164,
"ParentId": 99689425,
"LayerData": {
"sample_data_remarks": "Updated remark",
"sample_data_last_modified_from": "2"
},
"Properties": [
{
"Name": "sample_data_remarks",
"DisplayName": null,
"Value": "Updated remark",
"DataType": 3,
"Format": null
},
{
"Name": "sample_data_last_modified_from",
"DisplayName": null,
"Value": "2",
"DataType": 10,
"Format": null
}
],
"sample_data_last_modified_from": 2
}
| Field | Type | Description |
|---|---|---|
Id | string | The record you updated - same value you sent in the path. |
id | string | Duplicate of Id in lower case. Read either one; they always carry the same value. |
Layer | string | Internal layer name: your tenant's system code plus the layer name. Search and detail return the plain layer name instead. |
Name | string | Internal node name of the record. Not the Id, and not used by any other endpoint. |
Title | string | Display label. null unless the record has one - an update that does not touch Title leaves it as is. |
Description | string | |
Created | string | When the record was created, UTC. |
Creator | string | Account that created the record. |
Modified | string | When this update was applied, UTC. |
Modifier | string | Account whose token made this call. |
Path | string | Internal storage location. Read-only, and of no use to an integration. |
ParentIdPath | string | Internal hierarchy path of numeric node IDs. Read-only. |
NodeId | integer | Internal numeric node ID. Read-only. |
ParentId | integer | Internal numeric ID of the parent node. Read-only. |
LayerData | object | The fields this call actually wrote, keyed by propertyName, including the *_last_modified_from marker the platform sets itself. Values are returned as strings here, whatever the field type. |
LayerData.sample_data_remarks | string | |
LayerData.sample_data_last_modified_from | string | |
Properties | array | The same changed fields in expanded form, each with its DataType. See Field data types. |
Properties[] | object | |
Properties[].Name | string | |
Properties[].DisplayName | string | |
Properties[].Value | string | |
Properties[].DataType | integer | |
Properties[].Format | string | |
sample_data_last_modified_from | integer | Channel of the last change, set by the platform: 1 Mobile, 2 Web, 3 Others. |
The update response is not shaped like search or detail. It returns the internal node record, so it carries platform fields (NodeId, ParentId, Name, Path, ParentIdPath, MimeType) that no other endpoint uses and that you should ignore. Call {id}/detail afterwards if you need the record in its normal shape.
Only the fields you sent come back changed. LayerData and Properties list exactly what this call wrote; untouched fields are not echoed. To confirm the full record, read it back with {id}/detail.
Only keys defined on the layer are stored. A key outside the layer's schema is echoed at the top level of the response but not persisted - it will not appear in LayerData, Properties, or any later read. Check LayerData to confirm which fields the update actually wrote.
Modified reflects the record's creation, not this update - it can come back equal to Created. Use LayerData rather than Modified to confirm a write took effect.
Several system fields come back null (Title, Description, Type, Status, Template, Content) whenever the update did not set them.
Delete a record
DELETE /api/app-base/data-layer/{layerName}/{id}
Request Parameters
| Parameter | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
layerName | path | string | Yes | Layer holding the record to delete | SAMPLE_DATA, CUSTOMER, PRODUCT |
id | path | string | Yes | Id of the record to delete, as returned by search or create | 00000000-0000-0000-0000-000000000001 |
Response Schema
200 OK
{
"count": 1
}
| Field | Type | Description |
|---|---|---|
count | integer | Number of records actually deleted. 1 = the record was found and removed, 0 = nothing matched the ID. The call returns 200 OK either way, so check this value to confirm the delete. |
Get a single record with all its values
GET /api/app-base/data-layer/{layerName}/{id}/detail
Request Parameters
| Parameter | In | Type | Required | Description | Example |
|---|---|---|---|---|---|
layerName | path | string | Yes | Data layer name | SAMPLE_DATA, CUSTOMER, PRODUCT |
id | path | string | Yes | Record ID |
Response Schema
200 OK
{
"Id": "<uuid>",
"Layer": "PRODUCT",
"Path": "/…",
"CreatedDate": "2026-07-15T11:15:45Z",
"CreatedUser": "<user>",
"ModifiedDate": "2026-07-15T11:15:45Z",
"ModifiedUser": "<user>",
"Title": "<title>",
"product_name": "<value>"
}
| Field | Type | Description |
|---|---|---|
Id | string | Record identifier, the value to pass to update and delete. |
Layer | string | Layer the record belongs to. |
Path | string | Internal storage location. Read-only. |
CreatedDate | string | When the record was created, UTC. |
CreatedUser | string | Account that created it. |
ModifiedDate | string | When the record was last changed, UTC. |
ModifiedUser | string | Account that last changed it. |
Title | string | Display label of the record. |
product_name | string |
Example
curl -X POST '$BASE_URL/api/app-base/data-layer/SAMPLE_DATA/layer-detail?api-key=$API_KEY' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{"properties": {"layerSettings": true, "isTakeAll": true}}'
Notes
Conventions that hold for every layer:
- Search responses are always wrapped as
{ "data": [...], "total": N }. - Every record carries a fixed set of system fields alongside its typed properties:
| Field | Meaning |
|---|---|
Id | Record identifier (UUID) |
Layer | The layer this record belongs to |
Path / ParentPath | Internal storage location |
CreatedDate / CreatedUser | When and by whom it was created |
ModifiedDate / ModifiedUser | When and by whom it was last changed |
Title | Display label for the record |
These appear on every layer, including each entry inside the search envelope's data[].
Schema vs. detail - do not confuse these two read operations: layer-detail returns the layer's schema (field definitions, no record ID in the path); {id}/detail returns one record's data (record ID in the path).
Integration datasets, folded in: market centre, village and dealer-territory datasets (e.g. MC_TERRITORY_VILLAGES, DEALER_WISE_TEHSIL_WISE_IND, DEALER_LONGITUDE_LATITUDE, DEALER_MAPPING) are just data layers like any other, queried through this same interface - there is no separate integration endpoint. Layer names are tenant-specific by design.