> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pretectum.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Managing Data Objects

> Learn how to create, read, update, and delete data objects using the Pretectum API

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:

<Steps>
  <Step title="Create an API Key">
    In the Pretectum app, go to **Configuration → API Keys** and create a key. It is shown once, at
    creation, so copy it then. See [API Keys](/api-reference/authentication/api-keys).
  </Step>

  <Step title="Identify Your Target Dataset">
    Know the business area, schema, and dataset where you want to manage data. Use the [List Business Areas](/api-reference/business-areas/list), [List Schemas](/api-reference/schemas/list), and [List Datasets](/api-reference/datasets/list) endpoints to discover available options.
  </Step>

  <Step title="Understand the Schema">
    Familiarize yourself with the schema field definitions to ensure your data conforms to the expected structure. Use [Get Schema Details](/api-reference/schemas/get) to view field definitions.
  </Step>
</Steps>

## Authentication

All data object operations require an API key. Send it in the `Authorization` header of every
request; there is no token to obtain first.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const API_BASE = 'https://api.pretectum.io';
  const apiKey = process.env.PRETECTUM_API_KEY;

  // Every request below sends the key like this
  // headers: { 'Authorization': apiKey }
  ```

  ```python Python theme={null}
  import os
  import requests

  API_BASE = 'https://api.pretectum.io'
  api_key = os.environ['PRETECTUM_API_KEY']

  # Every request below sends the key like this
  # headers={'Authorization': api_key}
  ```
</CodeGroup>

## Creating Data Objects

To create a new record, send a POST request with the field values:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function createDataObject(apiKey, businessAreaId, schemaId, datasetId, data) {
    const response = await fetch(
      `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects`,
      {
        method: 'POST',
        headers: {
          'Authorization': apiKey,
          '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(
    apiKey,
    '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}`);
    });
  }
  ```

  ```python Python theme={null}
  def create_data_object(api_key, business_area_id, schema_id, dataset_id, data):
      response = requests.post(
          f'{API_BASE}/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects',
          json=data,
          headers={
              'Authorization': api_key,
              'Content-Type': 'application/json'
          }
      )
      response.raise_for_status()
      return response.json()

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

  created = create_data_object(
      api_key,
      '20240115103000123a1b2c3d4e5f6789012345678901234',
      '20240115103000456d1e2f3a4b5c6789012345678901234',
      '20240925152201042a1b2c3d4e5f6789012345678901234',
      new_customer
  )

  print(f"Created: {created['_dataObjectId']}")

  # Check for validation errors
  if created['_errors']:
      print('Validation errors:')
      for err in created['_errors']:
          print(f"  - {err['name']}: {err['errors']}")
  ```
</CodeGroup>

### Handling Validation Errors

Data objects are created even if they have validation errors. This allows you to import data and fix issues later:

```javascript theme={null}
const created = await createDataObject(apiKey, 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:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function listDataObjects(apiKey, 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': apiKey }
    });

    return response.json();
  }

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

    do {
      const result = await listDataObjects(apiKey, 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(
    apiKey,
    businessAreaId,
    schemaId,
    datasetId
  );
  console.log(`Total customers: ${allCustomers.length}`);
  ```

  ```python Python theme={null}
  def list_data_objects(api_key, business_area_id, schema_id, dataset_id, page_key=None):
      params = {}
      if page_key:
          params['pageKey'] = page_key

      response = requests.get(
          f'{API_BASE}/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects',
          params=params,
          headers={'Authorization': api_key}
      )
      response.raise_for_status()
      return response.json()

  # Get all data objects with pagination
  def get_all_data_objects(api_key, business_area_id, schema_id, dataset_id):
      all_objects = []
      page_key = None

      while True:
          result = list_data_objects(api_key, business_area_id, schema_id, dataset_id, page_key)
          all_objects.extend(result['items'])
          page_key = result.get('nextPageKey')
          print(f"Loaded {len(all_objects)} records...")

          if not page_key:
              break

      return all_objects

  all_customers = get_all_data_objects(
      api_key,
      business_area_id,
      schema_id,
      dataset_id
  )
  print(f"Total customers: {len(all_customers)}")
  ```
</CodeGroup>

## Updating Data Objects

Update existing records using the PUT method. You must include the `_version` field to prevent overwriting concurrent changes:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function updateDataObject(apiKey, businessAreaId, schemaId, datasetId, dataObjectId, updates) {
    const response = await fetch(
      `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects/${dataObjectId}`,
      {
        method: 'PUT',
        headers: {
          'Authorization': apiKey,
          '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(
    apiKey,
    businessAreaId,
    schemaId,
    datasetId,
    dataObjectId,
    {
      '_version': currentVersion,
      'Email': 'john.smith.new@example.com',
      'Status': 'Premium'
    }
  );

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

  ```python Python theme={null}
  def update_data_object(api_key, business_area_id, schema_id, dataset_id, data_object_id, updates):
      response = requests.put(
          f'{API_BASE}/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects/{data_object_id}',
          json=updates,
          headers={
              'Authorization': api_key,
              'Content-Type': 'application/json'
          }
      )
      response.raise_for_status()
      return response.status_code == 204

  # Example: Update a customer's email
  data_object_id = '20240601120000123f1a2b3c4d5e6789012345678901234'
  current_version = 1  # Get this from the list response

  success = update_data_object(
      api_key,
      business_area_id,
      schema_id,
      dataset_id,
      data_object_id,
      {
          '_version': current_version,
          'Email': 'john.smith.new@example.com',
          'Status': 'Premium'
      }
  )

  if success:
      print('Customer updated successfully')
  ```
</CodeGroup>

### Handling Version Conflicts

When multiple users or processes update the same record, version conflicts can occur. Implement retry logic:

```javascript theme={null}
async function updateWithRetry(apiKey, businessAreaId, schemaId, datasetId, dataObjectId, fieldUpdates, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    // Get current version
    const list = await listDataObjects(apiKey, 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(
        apiKey,
        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:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function deleteDataObject(apiKey, businessAreaId, schemaId, datasetId, dataObjectId) {
    const response = await fetch(
      `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}/datasets/${datasetId}/dataobjects/${dataObjectId}`,
      {
        method: 'DELETE',
        headers: { 'Authorization': apiKey }
      }
    );

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

    return response.status === 204;
  }

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

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

  ```python Python theme={null}
  def delete_data_object(api_key, business_area_id, schema_id, dataset_id, data_object_id):
      response = requests.delete(
          f'{API_BASE}/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects/{data_object_id}',
          headers={'Authorization': api_key}
      )
      response.raise_for_status()
      return response.status_code == 204

  # Example: Delete a customer
  success = delete_data_object(
      api_key,
      business_area_id,
      schema_id,
      dataset_id,
      '20240601120000123f1a2b3c4d5e6789012345678901234'
  )

  if success:
      print('Customer deleted successfully')
  ```
</CodeGroup>

## Complete Client Example

Here's a complete client class that handles all CRUD operations:

<CodeGroup>
  ```javascript JavaScript theme={null}
  class PretectumDataClient {
    constructor(apiKey) {
      this.apiKey = apiKey;
      this.baseUrl = 'https://api.pretectum.io';
    }

    async request(method, path, body = null) {
      const options = {
        method,
        headers: {
          'Authorization': this.apiKey,
          '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(process.env.PRETECTUM_API_KEY);

  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);
  ```

  ```python Python theme={null}
  import os

  class PretectumDataClient:
      def __init__(self, api_key):
          self.api_key = api_key
          self.base_url = 'https://api.pretectum.io'

      def request(self, method, path, body=None):
          headers = {
              'Authorization': self.api_key,
              'Accept': 'application/json'
          }

          kwargs = {'headers': headers}
          if body:
              headers['Content-Type'] = 'application/json'
              kwargs['json'] = body

          response = requests.request(method, f'{self.base_url}{path}', **kwargs)

          if response.status_code == 204:
              return {'success': True}

          response.raise_for_status()
          return response.json()

      # Data Object methods
      def list_data_objects(self, business_area_id, schema_id, dataset_id, page_key=None):
          path = f'/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects'
          if page_key:
              path += f'?pageKey={page_key}'
          return self.request('GET', path)

      def create_data_object(self, business_area_id, schema_id, dataset_id, data):
          return self.request(
              'POST',
              f'/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects',
              data
          )

      def update_data_object(self, business_area_id, schema_id, dataset_id, data_object_id, updates):
          return self.request(
              'PUT',
              f'/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects/{data_object_id}',
              updates
          )

      def delete_data_object(self, business_area_id, schema_id, dataset_id, data_object_id):
          return self.request(
              'DELETE',
              f'/v1/businessareas/{business_area_id}/schemas/{schema_id}/datasets/{dataset_id}/dataobjects/{data_object_id}'
          )

      # Helper: Get all objects with pagination
      def get_all_data_objects(self, business_area_id, schema_id, dataset_id):
          all_objects = []
          page_key = None

          while True:
              result = self.list_data_objects(business_area_id, schema_id, dataset_id, page_key)
              all_objects.extend(result['items'])
              page_key = result.get('nextPageKey')
              if not page_key:
                  break

          return all_objects


  # Usage
  client = PretectumDataClient(os.environ['PRETECTUM_API_KEY'])

  business_area_id = '20240115103000123a1b2c3d4e5f6789012345678901234'
  schema_id = '20240115103000456d1e2f3a4b5c6789012345678901234'
  dataset_id = '20240925152201042a1b2c3d4e5f6789012345678901234'

  # Create
  created = client.create_data_object(business_area_id, schema_id, dataset_id, {
      'First Name': 'John',
      'Last Name': 'Smith',
      'Email': 'john@example.com'
  })

  # List
  all_objects = client.get_all_data_objects(business_area_id, schema_id, dataset_id)

  # Update
  client.update_data_object(business_area_id, schema_id, dataset_id, created['_dataObjectId'], {
      '_version': created['_version'],
      'Status': 'Active'
  })

  # Delete
  client.delete_data_object(business_area_id, schema_id, dataset_id, created['_dataObjectId'])
  ```
</CodeGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Error Handling">
    * Always check response status codes
    * Treat a 401 as a rejected key, not something a retry will fix
    * Implement retry logic for transient failures
    * Log errors with context for debugging
  </Accordion>

  <Accordion title="Performance">
    * 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
  </Accordion>

  <Accordion title="Data Integrity">
    * 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
  </Accordion>

  <Accordion title="Security">
    * Store credentials securely (environment variables, secrets manager)
    * Never log API keys
    * Refresh tokens before they expire
    * Use HTTPS for all API calls
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Search Data Objects" icon="magnifying-glass" href="/guides/dataobjects/search">
    Learn advanced search techniques
  </Card>

  <Card title="Working with Schemas" icon="file-code" href="/guides/schemas/overview">
    Understand schema definitions
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/dataobjects/list">
    View complete API documentation
  </Card>

  <Card title="Working with Datasets" icon="database" href="/guides/datasets/overview">
    Learn about dataset management
  </Card>
</CardGroup>
