What API do I use to create/update bulk assets?
1. Purpose
The /api/asset/bulk endpoint enables bulk creation or update of master data entities such as fields, stops, routes, wells, tanks, equipment, and meters in the JoynFSM system.
2. Highlights
Create or update assets such as Field, Route, Stop, Well, Tank, Equipment, and Meter in bulk or individually.
Supports both creation of new assets and updates to existing ones based on the provided
id.Allows mapping of assets to specific areas and teams.
Provides detailed success and error responses for processed and unprocessed items.
Fully secured with Cognito-based authorization.
Designed for seamless integration with Joyn FDG systems.
3. API EndPoint:
/api/asset/bulk
Method:
POST
5. Getting Started
User Pool Configuration: Ensure your tenant is configured with a valid Cognito User Pool
JWT Token: Obtain a valid JWT token from Cognito authentication
6. JSON structure
{
// === REQUIRED FIELDS ===
"type": "string", // Required: Asset type (see valid values below)
"name": "string", // Required: Asset name
// === IDENTIFIER FIELDS (At least one required) ===
"id": "string", // Internal UUID (auto-generated if not provided)
"xid": "string", // External identifier (recommended)
"fdgxid": "string", // FDG external identifier
// === BASIC PROPERTIES ===
"isDeleted": boolean, // Soft delete flag (default: false)
"latitude": number, // Geographical coordinate
"longitude": number, // Geographical coordinate
"subType": "string", // Asset subtype
"productType": "string", // Product classification
"sourceSystem": "string", // Source system identifier
"stopId": "string", // Associated stop ID
"producingMethodId": "string", // Production method reference
"tankStrappingID": "string", // Tank strapping identifier
"fieldId": "string", // Field ID (auto-populated based on references)
"correlationId": "string", // Request tracking identifier
// === CUSTOM DATA ===
"customAttributes": {}, // Object: Custom key-value pairs
// === RELATIONSHIP OBJECTS ===
"field": { // Field reference
"id": "string",
"xid": "string",
"name": "string"
},
"route": { // Route reference
"id": "string",
"xid": "string",
"name": "string"
},
"stop": { // Stop reference
"id": "string",
"xid": "string",
"name": "string"
},
"area": { // Area reference
"id": "string",
"xid": "string",
"name": "string"
},
// === TEAM ASSIGNMENTS ===
"teams": [
{
"teamId": "string",
"resources": [
{
"userId": "string",
"resourceLevel": "string",
"isDeleted": false
}
],
"isDeleted": false
}
],
// === AREA DEFINITION (For area type assets) ===
"areaType": "STATIC|DYNAMIC", // Required for type: "area"
"areaDefinition": { // Required for areaType: "DYNAMIC"
"criteria": [
{
"attributeName": "string",
"values": {} // Object: Criteria values
}
]
},
// === ATTACHMENTS ===
"attachments": [
{
"bucket": "string", // S3 bucket name
"key": "string", // S3 object key
"fileName": "string", // Original filename
"region": "string" // AWS region
}
],
// === WATCHERS & CONTACTS ===
"watcher": { // Single watcher reference
"id": "string",
"userName": "string"
},
"contact": { // Single contact reference
"id": "string",
"xid": "string"
},
"watchers": ["string"], // Array of watcher IDs
"contacts": ["string"] // Array of contact IDs
}
7. Mandatory Fields
# | Field | Guidance |
|---|---|---|
1 | id | Unique identifier for the asset. Required for updates; optional for creating new assets. |
2 | name | Name of the asset |
3 | type | type of the asset. Supported values include:
|
4 | tenantId | Tenant ID associated with the asset |
8. Optional Fields
# | Field | Guidance |
|---|---|---|
1 | areaId | Identifier for the area to which the asset belongs. |
2 | areaType | Specifies whether the area is |
3 | teamId | Identifier for the team responsible for the asset. |
4 | description | A brief description of the asset. |
9. Request Format
· List of json
· Maximum size should be less than 6 MB
10. Key Rules
Order Matters: Create parent assets (fields) before child assets (routes/stops).
References: Use xid for reliable cross-references
Validation: Each object is validated independently
Partial Success: Some assets may succeed while others fail
Upsert Logic: Existing assets are updated, new ones are created
11. Response Format
NOTE 1: If the response data size exceeds a certain threshold, the API will not return the full data inline.
Instead, it will provide a URL to an S3 object where the full response can be downloaded.
NOTE 2: The response always includes two arrays: processedItems and unProcessedItems.
Even if the request is partially successful, both arrays will be present, and you should check both for results and errors.
NOTE 3: If the response is offloaded to S3, the inline response will not contain the processedItems and unProcessedItems arrays directly—only the download URL.
NOTE 4: The S3 URL is temporary and may expire after a short period (typically 5–15 minutes). Download the file promptly.
12. Example Request
{
"message": "string",
"success": true,
"data": {
"processedItems": [
{
"message": "string",
"record": {
"id": "string",
"xid": "string",
"name": "string",
"fdgxid": "string",
"type": "string",
"isDeleted": true,
"latitude": 0,
"longitude": 0,
"subType": "string",
"productType": "string",
"sourceSystem": "string",
"stopId": "string",
"producingMethodId": "string",
"tankStrappingID": "string",
"customAttributes": {},
"teams": [
{
"teamId": "string",
"resources": [
{
"userId": "string",
"resourceLevel": "string",
"isDeleted": false
}
],
"isDeleted": false
}
],
"areas": [
{
"areaId": "string",
"isDeleted": false
}
],
"fdgObjectId": "string",
"routeIds": "array",
"fdgRouteIds": [
"string"
],
"areaType": "string",
"bulkStopGroupName": "string",
"fieldId": "string",
"correlationId": "string"
}
}
],
"unProcessedItems": [
{
"message": "string",
"record": {
"id": "string",
"xid": "string",
"name": "string",
"fdgxid": "string",
"type": "string",
"isDeleted": true,
"latitude": 0,
"longitude": 0,
"subType": "string",
"productType": "string",
"sourceSystem": "string",
"stopId": "string",
"producingMethodId": "string",
"tankStrappingID": "string",
"customAttributes": {},
"teams": [
{
"teamId": "string",
"resources": [
{
"userId": "string",
"resourceLevel": "string",
"isDeleted": false
}
],
"isDeleted": false
}
],
"areas": [
{
"areaId": "string",
"isDeleted": false
}
],
"fdgObjectId": "string",
"routeIds": "array",
"fdgRouteIds": [
"string"
],
"areaType": "string",
"bulkStopGroupName": "string",
"fieldId": "string",
"correlationId": "string"
}
}
]
}
}
Example Request for stop
[
{
"attachments": [],
"latitude": 100,
"description": "Godric gryffindor -testing1",
"areas": [
{
"areaId": "8cc5fac8-1598-4794-b7fb-8a2796d17311",
"isDeleted": false
},
{
"areaId": "78469401-fd18-497d-ae10-39cf653da070",
"isDeleted": false
}
],
"availability": [],
"createdBy": "shivar@sevenlakes.com",
"createdOn": "2023-07-05T10:07:54.862Z",
"customAttributes": {
"C_DC_FLAG": "Yes",
"Foreman": "harry, test"
},
"cxid": "Gryffindor Common Room",
"fieldId": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31",
"id": "35391454-7152-4f87-b17b-d7175552e225",
"longitude": 100,
"modifiedBy": "Joynfsmscript@wenergysoftware.com",
"modifiedOn": "2024-02-26T12:59:21.030Z",
"name": "Gryffindor Common Room",
"preferredLocations": {},
"stopId": "35391454-7152-4f87-b17b-d7175552e225",
"tenantId": "00e29a9d-6621-43a4-a6c2-a25c67939e5a",
"type": "stop",
"watchers": []
}
]
Example Request for Tank
[
{
"name": "tank123",
"description": "creating tank for field-BLR",
"type": "tank",
"attachments": [],
"availability": [],
"customAttributes": {},
"field": {
"id": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31"
},
"preferredLocations": {},
"stop": {
"id": "33f9d517-56af-4c27-8ab0-13a88dcbc780"
},
"watchers": [
"2f313acc-a0d9-44af-9789-32025e0cee87"
]
}
]
Example request for well
[
{
"name": "well123",
"description": "creatin well using field BLR",
"type": "well",
"attachments": [],
"availability": [],
"customAttributes": {},
"field": {
"id": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31"
},
"preferredLocations": {},
"stop": {
"id": "33f9d517-56af-4c27-8ab0-13a88dcbc780"
}
}
]
Example Request for field:
[
{
"type": "field",
"id": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31",
"name": "field 1234",
"latitude": 100,
"longitude": 100
}
]
Example request for Static area creation
{
"name": "static area test",
"type": "area",
"areaType": "STATIC",
"fieldId": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31",
"teams": [
{
"teamId": "a6f2e572-c978-4f31-9fea-b512f940791c"
},
{
"teamId": "df27e37c-04f7-45d0-99c9-ca330079adc6"
},
{
"teamId": "34fc1cc5-1c55-4d50-ae3e-a8b15ed6ba9c"
},
{
"teamId": "d43391d6-1c11-4910-91ec-9c0fb41de193"
}
]
}
Example request for Dynamic Area creation
[
{
"id": "e0a3d507-fb5a-4db0-b5e4-10d1a82e10c2",
"name": "testStopAttributeArea",
"type": "area",
"areaType": "DYNAMIC",
"fieldId": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31",
"additionalStopIds": [
"2c089ea4-fb0b-4621-901b-98b8984155d7"
],
"areaDefinition": {
"criteria": [
{
"attributeName": "productType",
"values": {
"oil": true
}
}
]
},
"teams": [
{
"teamId": "team-001",
"resources": [
{
"userId": "user-001",
"resourceLevel": "operator"
}
]
}
]
}
]
Example Response
{
"success": true,
"message": "Processed 1 items, Failed to process 0 items",
"output": {
"processedItems": [
{
"message": "Item got updated",
"record": {
"areas": [
{
"areaId": "8cc5fac8-1598-4794-b7fb-8a2796d17311",
"isDeleted": false
},
{
"areaId": "78469401-fd18-497d-ae10-39cf653da070",
"isDeleted": false
}
],
"stopId": "35391454-7152-4f87-b17b-d7175552e225",
"tenantId": "00e29a9d-6621-43a4-a6c2-a25c67939e5a",
"customAttributes": {
"C_DC_FLAG": "Yes",
"Foreman": "harry, test"
},
"attachments": [],
"createdBy": "shivar@sevenlakes.com",
"name": "Gryffindor Common Room",
"availability": [],
"preferredLocations": {},
"globalAttributes": {},
"createdOn": "2023-07-05T10:07:54.862Z",
"modifiedOn": "2025-08-04T04:16:11.520Z",
"longitude": 100,
"watchers": [],
"description": "Godric gryffindor -testing1",
"id": "35391454-7152-4f87-b17b-d7175552e225",
"latitude": 100,
"modifiedBy": "rajat.barman@sevenlakes.com",
"fieldId": "5be5b1a8-eda5-42cf-b931-3a8e97c61b31",
"type": "stop"
}
}
],
"unProcessedItems": []
}
}
13. Create vs Update
Create:
If the asset (based on its identifiers) does not exist in the system, a new asset is created.
Identifiers checked for existence:
id
xid
name
fdgxid
If none of these match an existing asset for the tenant, the API will create a new record.
Update:
If an asset with the same
id,xid,name, orfdgxidalready exists for the tenant, the API will update the existing asset with the new data provided in the request.The update is performed in-place, and only the fields provided in the request will be updated (others remain unchanged unless explicitly set to null or a new value).
14. How the API decides?
For each asset in the bulk array, the API tries to find an existing asset using the provided identifiers.
If a match is found, it’s an update; if not, it’s a create.
15. Dynamic Areas
A dynamic area is an area whose membership (e.g., which stops/assets belong to it) is determined automatically based on criteria, rather than being manually assigned.
This is achieved using the areaDefinition property, which specifies rules (criteria) for including assets in the area.
What makes an area “dynamic”?
Dynamic areas use the
areaType: "DYNAMIC"property.They include an
areaDefinitionobject, which contains one or morecriteria.
The system evaluates these criteria to automatically assign assets (like stops or wells) to the area, based on their attributes.
16. Area Definition
The areaDefinition property is required for dynamic areas and describes the rules for which assets should be included.
Structure:
"areaDefinition": {
"criteria": [
{
"attributeName": "productType",
"values": { "oil": true }
},
{
"attributeName": "region",
"values": { "north": true, "south": false }
}
]
}
criteria: An array of objects, each specifying an attribute and the values that qualify an asset for inclusion in the area.
attributeName: The name of the asset attribute to check (e.g.,"productType","region").
values: An object where the keys are possible values for the attribute, and the values are booleans indicating inclusion.
How it works:
The system will automatically include all assets (e.g., stops, wells) in this area if their attributes match the criteria.
17. Teams in Dynamic Areas
You can assign teams to a dynamic area just like with static areas, using the teams property, allowing for access control and task assignment based on the dynamic membership of the area.
Structure:
"teams": [
{
"teamId": "team-001",
"resources": [
{
"userId": "user-001",
"resourceLevel": "operator"
},
{
"userId": "user-002",
"resourceLevel": "supervisor"
}
]
}
]
teamId: The unique identifier for the team.
resources: The users assigned to the team for this area, with their roles.
18. Area Team Mapping
You can assign teams (and their users) to an area asset by including a teams array in your area object when using the /api/asset/bulk endpoint.
How it works?
When you include a
teamsarray in an area asset, the system links those teams and users to the area.
This mapping controls:
Access control: Only mapped teams/users can view or manage the area.
Task assignment: Tasks related to the area can be assigned to mapped teams/users.
Notifications: Only mapped teams/users receive area-related notifications.
Updating the
teamsarray will update the mapping (add, remove, or change users/roles).Notes:
1. AllteamIdanduserIdvalues must reference existing teams and users.
2. You can assign multiple teams to a single area.
3. If you omit theteamsproperty, no team mapping will be set or changed for that area.
19. Error Handling
Our API uses standard HTTP status codes to indicate the success or failure of a request. **Common Error Codes:** -
Code | Description |
|---|---|
400 Bad Request | The request was invalid. |
401 Unauthorized | Authentication failed: Verify Cognito User Pool configuration |
404 Not Found | The requested resource was not found. |
500 Internal Server Error | An error occurred on the server. |
Error Response:
{"success":false,"message":"Some error message"}
20. Common Reasons for API Failure
Potential Issue | Resolution |
|---|---|
improper date format
| format should be |
Missing identifier (id, xid, name, fdgxid) | Provide at least one unique identifier for each asset. |
Missing required field type | Ensure every asset object includes a valid |
Invalid type value | Use only allowed values: |
Missing required reference (e.g., field for route/stop/area) | For child assets, include the required parent reference object (e.g., |
Missing areaType for area asset | For |
Missing areaDefinition for dynamic area | For |
Reference object missing identifier | When providing references (e.g., |
Duplicate asset (same xid or name in same field) | Use unique |
Invalid or missing team/user IDs in teams | Ensure all |
Large request size | If the request is too large, split into smaller batches |
Invalid attachment structure | Each attachment must include at least |
Partial Success | Some records may fail while others succeed; always check |
21. Checks / Validations
# | Validation |
|---|---|
1 | All referenced fields, routes, stops, areas, and parent assets (by id/xid/name/fdgxid) are checked for existence. |
2 | All team IDs, user IDs, and user xids in the teams array are validated for existence. |
3 | All asset objects must have at least one valid identifier (id, xid, name, or fdgxid). |
4 | All entity names and xids must be unique within their parent context (e.g., field). |
5 | Custom attributes and metadata are validated for correct data types if schema is defined. |
6 | Authority/permission checks: Only users with the correct permissions can create/update assets.hecked, lower authority level cannot update higher authority level reading |
7 | Duplicate asset check: No two assets with the same xid or name in the same field. |
8 | Reference integrity: All references (e.g., field, route, stop, area) must point to existing, valid entities. |
9 | Area-specific checks: For type: "area", areaType and (if dynamic) areaDefinition are required and validated. |
10 | Area-specific checks: For type: "area", areaType and (if dynamic) areaDefinition are required and validated. |
11 | Partial upsert: Each asset is validated independently; failures in one do not block others. |
12 | Schema validation: All fields are checked against the asset schema for type and presence. |
22. Setup Requirements
# | Requirement |
|---|---|
1 | Use Cognito JWT tokens for authentication. |
2 | Ensure your tenant and users are set up and have the right permissions. |
3 | Create parent assets before referencing them in child assets. |
4 | If using attachments, make sure S3 references are correct. |