Skip to main content
This guide explains how to work with schemas in Pretectum using the API. You will learn how to retrieve schema definitions, understand field structures, and use schemas effectively for searching and organizing data.

Overview

Schemas define the structure of your data objects in Pretectum. Each schema specifies:
  • Fields: The attributes that data objects contain (e.g., First Name, Email, Phone)
  • Data Types: The type of data each field holds (string, number, date, etc.)
  • Validation Rules: Requirements like mandatory fields and unique constraints
  • Search Configuration: Which fields are indexed for searching
Schemas are organized within business areas, creating a hierarchy:
Business Area (e.g., Customer)
  └── Schema (e.g., Individual Customer)
       └── Fields (e.g., First Name, Email, Phone)
           └── Data Objects (actual customer records)

Why Schemas Matter

Understanding your schemas is essential for:
  1. Effective Searching: Know which fields are searchable and their exact names
  2. Data Validation: Understand required fields and data types
  3. Building Integrations: Create forms and interfaces that match your data structure
  4. Query Optimization: Target searches to specific fields for better results

Before You Begin

To use the Schema APIs, you need:
1

Get API Credentials

Obtain your client_id and client_secret from your Pretectum tenant administrator.
2

Obtain Access Token

Exchange your credentials for an access token using the authentication endpoint.
3

Get Business Area IDs

Retrieve business area IDs using the List Business Areas endpoint. Schema queries require a business area ID.

Listing Schemas in a Business Area

To get schemas, you first need the business area ID, then query for schemas within that area.

Step 1: Get Business Area ID

async function getBusinessAreaId(accessToken, businessAreaName) {
  const response = await fetch('https://api.pretectum.io/v1/my/businessareas', {
    headers: { 'Authorization': accessToken }
  });

  const areas = await response.json();
  const area = areas.find(a => a.name === businessAreaName);
  return area?.businessAreaId;
}

const businessAreaId = await getBusinessAreaId(accessToken, 'Customer');

Step 2: List Schemas

curl -X GET "https://api.pretectum.io/v1/businessareas/20240115103000123a1b2c3d4e5f6789012345678901234/schemas" \
  -H "Authorization: your_access_token"

Understanding the Response

{
  "items": [
    {
      "schemaId": "20240115103000456d1e2f3a4b5c6789012345678901234",
      "name": "Individual Customer",
      "description": "Schema for individual customer records",
      "businessAreaId": "20240115103000123a1b2c3d4e5f6789012345678901234",
      "businessAreaName": "Customer",
      "active": true,
      "state": "published",
      "fieldsCount": 12,
      "dataSetCount": 3,
      "version": 5,
      "createdByEmail": "admin@example.com",
      "updatedByEmail": "admin@example.com",
      "createdDate": "2024-01-15T10:30:00Z",
      "updatedDate": "2024-06-15T14:22:00Z"
    },
    {
      "schemaId": "20240120090000789e2f3a4b5c6d7890123456789012345",
      "name": "Business Customer",
      "description": "Schema for business customer records",
      "businessAreaId": "20240115103000123a1b2c3d4e5f6789012345678901234",
      "businessAreaName": "Customer",
      "active": true,
      "state": "published",
      "fieldsCount": 15,
      "dataSetCount": 2,
      "version": 3,
      "createdByEmail": "admin@example.com",
      "updatedByEmail": "admin@example.com",
      "createdDate": "2024-01-20T09:00:00Z",
      "updatedDate": "2024-05-10T11:30:00Z"
    }
  ],
  "nextPageKey": "eyJMYXN0RXZhbHVhdGVkS2V5Ijp7..."
}
FieldDescription
schemaIdUnique identifier for the schema
nameDisplay name used for filtering in search operations
descriptionExplanation of the schema’s purpose
businessAreaIdThe parent business area ID
businessAreaNameThe parent business area name
activeWhether the schema is currently active
stateCurrent state of the schema (e.g., draft, published)
fieldsCountNumber of fields defined in the schema
dataSetCountNumber of datasets using this schema
versionVersion number for tracking changes
nextPageKeyPagination token for fetching the next page (if present)

Getting Schema Details

Retrieve complete schema information including field definitions:
curl -X GET "https://api.pretectum.io/v1/businessareas/20240115103000123a1b2c3d4e5f6789012345678901234/schemas/20240115103000456d1e2f3a4b5c6789012345678901234" \
  -H "Authorization: your_access_token"

Example Response

{
  "schemaId": "20240115103000456d1e2f3a4b5c6789012345678901234",
  "name": "Individual Customer",
  "description": "Schema for individual customer records",
  "businessAreaId": "20240115103000123a1b2c3d4e5f6789012345678901234",
  "businessAreaName": "Customer",
  "version": 5,
  "createdBy": "admin@example.com",
  "updatedBy": "admin@example.com",
  "createdDate": "2024-01-15T10:30:00Z",
  "updatedDate": "2024-06-15T14:22:00Z",
  "fields": [
    {
      "fieldId": "20250115103500001a1b2c3d4e5f6789012345678901234",
      "name": "First Name",
      "description": "Customer's first name",
      "active": true,
      "dataType": "string",
      "isPrimaryKey": false,
      "isSortKey": false,
      "isRequired": true,
      "isMultivalue": false,
      "isPII": true,
      "min": 1,
      "max": 100
    },
    {
      "fieldId": "20250115103700002b2c3d4e5f6a7890123456789012345",
      "name": "Last Name",
      "description": "Customer's last name",
      "active": true,
      "dataType": "string",
      "isPrimaryKey": false,
      "isSortKey": true,
      "isRequired": true,
      "isMultivalue": false,
      "isPII": true,
      "min": 1,
      "max": 100
    },
    {
      "fieldId": "20250115104000003c3d4e5f6a7b8901234567890123456",
      "name": "Email",
      "description": "Customer's email address",
      "active": true,
      "dataType": "email",
      "isPrimaryKey": true,
      "isSortKey": false,
      "isRequired": true,
      "isMultivalue": false,
      "isPII": true
    },
    {
      "fieldId": "20250115104200004d4e5f6a7b8c9012345678901234567",
      "name": "Customer Type",
      "description": "Type of customer",
      "active": true,
      "dataType": "picklist",
      "isPrimaryKey": false,
      "isSortKey": false,
      "isRequired": true,
      "isMultivalue": false,
      "isPII": false,
      "picklistType": "static",
      "staticPicklist": [
        { "code": "individual", "description": "Individual customer" },
        { "code": "corporate", "description": "Corporate customer" }
      ]
    }
  ]
}
Use schema and field information to construct effective search queries.

Filtering by Schema Name

# Search within a specific schema
curl -X GET "https://api.pretectum.io/dataobjects/search?query=John&businessArea=Customer&schema=Individual%20Customer" \
  -H "Authorization: your_access_token"

Searching by Field Names

Use the field names from the schema to construct field-specific queries:
# Using field names from the schema
query=[First Name] eq "John"
query=[Email] contains "@example.com"
query=[Last Name] startswith "Sm"
Field names with spaces must be enclosed in brackets when using comparison operators: [First Name] eq "John"

Complete Client Implementation

Here is a complete implementation that handles business areas, schemas, and searching:
const API_BASE = 'https://api.pretectum.io';

class PretectumClient {
  constructor(clientId, clientSecret) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.accessToken = null;
    this.tokenExpiry = null;
    this._businessAreas = null;
    this._schemas = {};
  }

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

    if (!response.ok) throw new Error('Authentication failed');

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

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

  async getBusinessAreas() {
    await this.ensureAuthenticated();

    if (this._businessAreas) return this._businessAreas;

    const response = await fetch(`${API_BASE}/v1/my/businessareas`, {
      headers: { 'Authorization': this.accessToken }
    });

    this._businessAreas = await response.json();
    return this._businessAreas;
  }

  async getBusinessAreaId(name) {
    const areas = await this.getBusinessAreas();
    const area = areas.find(a => a.name === name);
    return area?.businessAreaId;
  }

  async getSchemas(businessAreaId) {
    await this.ensureAuthenticated();

    if (this._schemas[businessAreaId]) {
      return this._schemas[businessAreaId];
    }

    const allSchemas = [];
    let pageKey = null;

    do {
      const url = new URL(`${API_BASE}/v1/businessareas/${businessAreaId}/schemas`);
      if (pageKey) url.searchParams.set('pageKey', pageKey);

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

      const data = await response.json();
      allSchemas.push(...data.items);
      pageKey = data.nextPageKey;
    } while (pageKey);

    this._schemas[businessAreaId] = allSchemas;
    return allSchemas;
  }

  async getSchemasByBusinessAreaName(businessAreaName) {
    const businessAreaId = await this.getBusinessAreaId(businessAreaName);
    if (!businessAreaId) {
      throw new Error(`Business area not found: ${businessAreaName}`);
    }
    return this.getSchemas(businessAreaId);
  }

  async getSchemaDetails(businessAreaId, schemaId) {
    await this.ensureAuthenticated();

    const response = await fetch(
      `${API_BASE}/v1/businessareas/${businessAreaId}/schemas/${schemaId}`,
      {
        headers: { 'Authorization': this.accessToken }
      }
    );

    if (!response.ok) {
      if (response.status === 404) return null;
      throw new Error(`Failed to fetch schema: ${response.statusText}`);
    }

    return response.json();
  }

  async getSchemaNames(businessAreaName) {
    const schemas = await this.getSchemasByBusinessAreaName(businessAreaName);
    return schemas.filter(s => s.active).map(s => s.name);
  }

  async getSearchableFields(businessAreaId, schemaId) {
    const schema = await this.getSchemaDetails(businessAreaId, schemaId);
    if (!schema) return [];

    return schema.fields
      .filter(f => f.searchable)
      .map(f => f.name);
  }

  async search(query, options = {}) {
    await this.ensureAuthenticated();

    const params = new URLSearchParams({ query, ...options });
    const response = await fetch(
      `${API_BASE}/dataobjects/search?${params}`,
      {
        headers: { 'Authorization': this.accessToken }
      }
    );

    return response.json();
  }
}

// Usage
const client = new PretectumClient('your_client_id', 'your_secret');

// Get all schemas in Customer business area
const schemas = await client.getSchemasByBusinessAreaName('Customer');
console.log('Customer schemas:', schemas.map(s => s.name));

// Get schema names for a dropdown
const schemaNames = await client.getSchemaNames('Customer');
console.log('Schema options:', schemaNames);

// Search within a specific schema
const results = await client.search('John', {
  businessArea: 'Customer',
  schema: 'Individual Customer'
});
console.log(`Found ${results.total} results`);

Building a Schema-Aware Search Interface

Create cascading dropdowns for business area and schema selection:
import { useState, useEffect } from 'react';

function SchemaAwareSearch({ client }) {
  const [businessAreas, setBusinessAreas] = useState([]);
  const [schemas, setSchemas] = useState([]);
  const [selectedArea, setSelectedArea] = useState('');
  const [selectedSchema, setSelectedSchema] = useState('');
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  // Load business areas on mount
  useEffect(() => {
    async function load() {
      const areas = await client.getBusinessAreas();
      setBusinessAreas(areas.filter(a => a.active));
    }
    load();
  }, [client]);

  // Load schemas when business area changes
  useEffect(() => {
    if (!selectedArea) {
      setSchemas([]);
      setSelectedSchema('');
      return;
    }

    async function loadSchemas() {
      const areaSchemas = await client.getSchemasByBusinessAreaName(selectedArea);
      setSchemas(areaSchemas.filter(s => s.active));
      setSelectedSchema('');
    }
    loadSchemas();
  }, [selectedArea, client]);

  const handleSearch = async () => {
    const options = {};
    if (selectedArea) options.businessArea = selectedArea;
    if (selectedSchema) options.schema = selectedSchema;

    const searchResults = await client.search(query, options);
    setResults(searchResults.hits);
  };

  return (
    <div>
      <select
        value={selectedArea}
        onChange={(e) => setSelectedArea(e.target.value)}
      >
        <option value="">All Business Areas</option>
        {businessAreas.map(area => (
          <option key={area.businessAreaId} value={area.name}>
            {area.name}
          </option>
        ))}
      </select>

      <select
        value={selectedSchema}
        onChange={(e) => setSelectedSchema(e.target.value)}
        disabled={!selectedArea}
      >
        <option value="">All Schemas</option>
        {schemas.map(schema => (
          <option key={schema.schemaId} value={schema.name}>
            {schema.name}
          </option>
        ))}
      </select>

      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />

      <button onClick={handleSearch}>Search</button>

      <ul>
        {results.map(result => (
          <li key={result._dataObjectId}>
            {JSON.stringify(result)}
          </li>
        ))}
      </ul>
    </div>
  );
}

Error Handling

Handle common errors when working with schemas:
async function safeGetSchemas(client, businessAreaId) {
  try {
    return await client.getSchemas(businessAreaId);
  } catch (error) {
    if (error.message.includes('401')) {
      await client.authenticate();
      return await client.getSchemas(businessAreaId);
    }
    if (error.message.includes('404')) {
      console.error('Business area not found');
      return [];
    }
    throw error;
  }
}

Best Practices

Schemas change infrequently. Cache responses to reduce API calls:
// Client already caches in this._schemas
// Force refresh when needed:
client._schemas = {};
const freshSchemas = await client.getSchemas(businessAreaId);
Before building field-specific queries, verify the field is searchable:
const schema = await client.getSchemaDetails(areaId, schemaId);
const searchableFields = schema.fields
  .filter(f => f.searchable)
  .map(f => f.name);
Always check for pageKey in schema list responses:
# The client implementation handles this automatically
# by looping until no more pageKey is returned

Next Steps