Skip to main content
This guide walks you through the complete lifecycle of data objects in Pretectum - from creating new records to updating and deleting them. Data objects are the core records in your master data repository.

Overview

Data objects represent individual records in your master data system. They:
  • Belong to a specific dataset within a schema and business area
  • Have dynamic fields defined by the schema structure
  • Support validation based on schema rules
  • Include version tracking for conflict resolution
  • Maintain an audit trail of all changes

Before You Begin

To manage data objects, you need:
1

Get API Credentials

Contact your Pretectum tenant administrator to obtain your client_id and client_secret.
2

Identify Your Target Dataset

Know the business area, schema, and dataset where you want to manage data. Use the List Business Areas, List Schemas, and List Datasets endpoints to discover available options.
3

Understand the Schema

Familiarize yourself with the schema field definitions to ensure your data conforms to the expected structure. Use Get Schema Details to view field definitions.

Authentication

All data object operations require authentication. First, obtain an access token:
const API_BASE = 'https://api.pretectum.io';

async function getAccessToken(clientId, clientSecret) {
  const response = await fetch(`${API_BASE}/oauth2/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: clientId,
      client_secret: clientSecret
    })
  });

  const data = await response.json();
  return data.access_token;
}

Creating Data Objects

To create a new record, send a POST request with the field values:
async function createDataObject(accessToken, businessAreaId, schemaId, datasetId, data) {
  const response = await fetch(
    `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects`,
    {
      method: 'POST',
      headers: {
        'Authorization': accessToken,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    }
  );

  if (!response.ok) {
    throw new Error(`Create failed: ${response.statusText}`);
  }

  return response.json();
}

// Example: Create a new customer
const newCustomer = {
  'First Name': 'John',
  'Last Name': 'Smith',
  'Email': 'john.smith@example.com',
  'Phone': '+1 555-123-4567',
  'Status': 'Active'
};

const created = await createDataObject(
  accessToken,
  '20240115103000123a1b2c3d4e5f6789012345678901234',
  '20240115103000456d1e2f3a4b5c6789012345678901234',
  '20240925152201042a1b2c3d4e5f6789012345678901234',
  newCustomer
);

console.log(`Created: ${created._dataObjectId}`);

// Check for validation errors
if (created._errors.length > 0) {
  console.log('Validation errors:');
  created._errors.forEach(err => {
    console.log(`  - ${err.name}: ${err.errors}`);
  });
}

Handling Validation Errors

Data objects are created even if they have validation errors. This allows you to import data and fix issues later:
const created = await createDataObject(accessToken, businessAreaId, schemaId, datasetId, {
  'First Name': 'John',
  'Email': 'invalid-email',  // Invalid format
  'Date of Birth': '1985/01/15'  // Wrong date format
});

if (created._errors.length > 0) {
  // Store the ID for later correction
  await storeForReview(created._dataObjectId, created._errors);
}

Listing Data Objects

Retrieve all records in a dataset with pagination support:
async function listDataObjects(accessToken, businessAreaId, schemaId, datasetId, pageKey = null) {
  const url = new URL(
    `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects`
  );
  if (pageKey) {
    url.searchParams.set('pageKey', pageKey);
  }

  const response = await fetch(url, {
    headers: { 'Authorization': accessToken }
  });

  return response.json();
}

// Get all data objects with pagination
async function getAllDataObjects(accessToken, businessAreaId, schemaId, datasetId) {
  const allObjects = [];
  let pageKey = null;

  do {
    const result = await listDataObjects(accessToken, businessAreaId, schemaId, datasetId, pageKey);
    allObjects.push(...result.items);
    pageKey = result.nextPageKey;
    console.log(`Loaded ${allObjects.length} records...`);
  } while (pageKey);

  return allObjects;
}

const allCustomers = await getAllDataObjects(
  accessToken,
  businessAreaId,
  schemaId,
  datasetId
);
console.log(`Total customers: ${allCustomers.length}`);

Updating Data Objects

Update existing records using the PUT method. You must include the _version field to prevent overwriting concurrent changes:
async function updateDataObject(accessToken, businessAreaId, schemaId, datasetId, dataObjectId, updates) {
  const response = await fetch(
    `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects/${dataObjectId}`,
    {
      method: 'PUT',
      headers: {
        'Authorization': accessToken,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(updates)
    }
  );

  if (!response.ok) {
    throw new Error(`Update failed: ${response.statusText}`);
  }

  return response.status === 204;
}

// Example: Update a customer's email
const dataObjectId = '20240601120000123f1a2b3c4d5e6789012345678901234';
const currentVersion = 1;  // Get this from the list response

const success = await updateDataObject(
  accessToken,
  businessAreaId,
  schemaId,
  datasetId,
  dataObjectId,
  {
    '_version': currentVersion,
    'Email': 'john.smith.new@example.com',
    'Status': 'Premium'
  }
);

if (success) {
  console.log('Customer updated successfully');
}

Handling Version Conflicts

When multiple users or processes update the same record, version conflicts can occur. Implement retry logic:
async function updateWithRetry(accessToken, businessAreaId, schemaId, datasetId, dataObjectId, fieldUpdates, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    // Get current version
    const list = await listDataObjects(accessToken, businessAreaId, schemaId, datasetId);
    const current = list.items.find(obj => obj._dataObjectId === dataObjectId);

    if (!current) {
      throw new Error('Data object not found');
    }

    try {
      const success = await updateDataObject(
        accessToken,
        businessAreaId,
        schemaId,
        datasetId,
        dataObjectId,
        { ...fieldUpdates, _version: current._version }
      );
      return success;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      console.log(`Version conflict, retrying (${attempt + 1}/${maxRetries})...`);
      await new Promise(r => setTimeout(r, 1000));
    }
  }
}

Deleting Data Objects

Remove records from a dataset using the DELETE method:
async function deleteDataObject(accessToken, businessAreaId, schemaId, datasetId, dataObjectId) {
  const response = await fetch(
    `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects/${dataObjectId}`,
    {
      method: 'DELETE',
      headers: { 'Authorization': accessToken }
    }
  );

  if (!response.ok) {
    throw new Error(`Delete failed: ${response.statusText}`);
  }

  return response.status === 204;
}

// Example: Delete a customer
const success = await deleteDataObject(
  accessToken,
  businessAreaId,
  schemaId,
  datasetId,
  '20240601120000123f1a2b3c4d5e6789012345678901234'
);

if (success) {
  console.log('Customer deleted successfully');
}

Complete Client Example

Here’s a complete client class that handles all CRUD operations:
class PretectumDataClient {
  constructor(clientId, clientSecret) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.accessToken = null;
    this.tokenExpiry = null;
    this.baseUrl = 'https://api.pretectum.io';
  }

  async authenticate() {
    const response = await fetch(`${this.baseUrl}/oauth2/token`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: this.clientId,
        client_secret: this.clientSecret
      })
    });

    const data = await response.json();
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + (data.expires_in * 1000);
  }

  async ensureAuth() {
    if (!this.accessToken || Date.now() >= this.tokenExpiry - 60000) {
      await this.authenticate();
    }
  }

  async request(method, path, body = null) {
    await this.ensureAuth();

    const options = {
      method,
      headers: {
        'Authorization': this.accessToken,
        'Accept': 'application/json'
      }
    };

    if (body) {
      options.headers['Content-Type'] = 'application/json';
      options.body = JSON.stringify(body);
    }

    const response = await fetch(`${this.baseUrl}${path}`, options);

    if (response.status === 204) {
      return { success: true };
    }

    if (!response.ok) {
      throw new Error(`Request failed: ${response.status} ${response.statusText}`);
    }

    return response.json();
  }

  // Data Object methods
  async listDataObjects(businessAreaId, schemaId, datasetId, pageKey = null) {
    let path = `/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects`;
    if (pageKey) {
      path += `?pageKey=${encodeURIComponent(pageKey)}`;
    }
    return this.request('GET', path);
  }

  async createDataObject(businessAreaId, schemaId, datasetId, data) {
    return this.request(
      'POST',
      `/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects`,
      data
    );
  }

  async updateDataObject(businessAreaId, schemaId, datasetId, dataObjectId, updates) {
    return this.request(
      'PUT',
      `/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects/${dataObjectId}`,
      updates
    );
  }

  async deleteDataObject(businessAreaId, schemaId, datasetId, dataObjectId) {
    return this.request(
      'DELETE',
      `/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects/${dataObjectId}`
    );
  }

  // Helper: Get all objects with pagination
  async getAllDataObjects(businessAreaId, schemaId, datasetId) {
    const all = [];
    let pageKey = null;

    do {
      const result = await this.listDataObjects(businessAreaId, schemaId, datasetId, pageKey);
      all.push(...result.items);
      pageKey = result.nextPageKey;
    } while (pageKey);

    return all;
  }
}

// Usage
const client = new PretectumDataClient('your_client_id', 'your_client_secret');

const businessAreaId = '20240115103000123a1b2c3d4e5f6789012345678901234';
const schemaId = '20240115103000456d1e2f3a4b5c6789012345678901234';
const datasetId = '20240925152201042a1b2c3d4e5f6789012345678901234';

// Create
const created = await client.createDataObject(businessAreaId, schemaId, datasetId, {
  'First Name': 'John',
  'Last Name': 'Smith',
  'Email': 'john@example.com'
});

// List
const all = await client.getAllDataObjects(businessAreaId, schemaId, datasetId);

// Update
await client.updateDataObject(businessAreaId, schemaId, datasetId, created._dataObjectId, {
  '_version': created._version,
  'Status': 'Active'
});

// Delete
await client.deleteDataObject(businessAreaId, schemaId, datasetId, created._dataObjectId);

Best Practices

  • Always check response status codes
  • Handle 401 errors by refreshing the access token
  • Implement retry logic for transient failures
  • Log errors with context for debugging
  • Use pagination for large datasets instead of loading everything at once
  • Cache schema information to reduce API calls
  • Run independent operations in parallel where possible
  • Implement connection pooling for high-volume applications
  • Always include _version when updating to prevent conflicts
  • Validate data on the client side before sending
  • Handle validation errors returned by the API
  • Implement idempotency for create operations in distributed systems
  • Store credentials securely (environment variables, secrets manager)
  • Never log access tokens
  • Refresh tokens before they expire
  • Use HTTPS for all API calls

Next Steps