Dev Guide
Dev GuideUser GuideGitHubNuGetDevCommunitySubmit a ticketLog In
GitHubNuGetDevCommunitySubmit a ticket

Schema for objects and fields

Create custom objects and fields for your app using Optimizely Data Platform (ODP) and Optimizely Connect Platform (OCP).

Data in Optimizely Data Platform (ODP) is stored in collections called objects (also known as a database table). Objects are composed of fields. Identifiers are a specific type of field that identifity customers. Fields link objects together using relationships.

  • Objects – Database tables that act as a collection of client data. Objects contain fields that are the names of the table columns. Objects are connected to other objects by relationships.
  • Fields – Columns in a database table (an Object in OCP). All objects have one primary key field that must be a string or number. Fields have one of the following data types: string, number, boolean, timestamp.
    • string – Any printable UTF-8 character, including space. Text is limited to 1024 characters.
    • number – Represented in standard decimal or integer format (for example: 0, 3.14159, -2.3, -0.112).
    • boolean – Must be 0, 1, true, or false.
    • timestamp – Must be ISO 8601 format or UNIX epoch (seconds since January 1, 1970). For example: 1435708800, 2015-07-01T00:00:00-00:00, 2015-07-01T12:30:00-07:00.



      If time and timezone are not provided, the system assumes 12am UTC for the time.

  • Identifiers – Special fields that identify customers. You can only create identifiers for the customer object. Learn more about identifiers in the ODP documentation.
  • Relationships – 1:1 database table relationships.

For more information on viewing your existing default and custom objects and fields in ODP, see Review data fields.



You must prefix all objects and fields that are unique to your app with your app_id. For example, if your app_id is acme, prefix all of your objects and fields with acme.

An exception are fields within an object that your app creates. For example, if your app created an acme_tickets object, then you do not have to prefix fields within this object.

There are two ways of creating and updating ODP schema from your app:

  • Static – Recommended as it is safer and easier to maintain. With static schema, when users install and upgrade your app, OCP makes sure all objects and fields declared in the app's YAML files are created in the ODP account where the user is installing/upgrading the app.
  • Dynamic – Useful when you need to create objects and fields dynamically based on user input.



Change ODP schema with caution. After you create an object or field, you cannot delete or rename it.

For more information about ODP data structure and schema, see the ODP developer documentation.

YAML file properties for defining objects and fields

The following table lists the properties and their descriptions for YAML files that define objects.

nameThe name of the object (should be plural). Must be globally unique. You must prefix all custom object names (except the customer object) with your app_id.
display_nameThe user-friendly name used for display within the UI (should be plural). You must prefix all custom object names (except the customer object) with your app name.
alias(Optional) The singular name of the object.
fields.nameThe name of the field used for API/SDK calls, CSV uploads, and liquid. Must be unique within the object.
fields.display_nameThe user-friendly name used for display within the UI. Must be unique within the object.
fields.typeMust be string, number, boolean, or timestamp.
fields.primary(Optional) Boolean. Defaults to false.

true – The field listed is the primary key for the containing object.
false – The field listed is not the primary key for the containing object.

At least one primary key is required for new objects.
relations(Optional) Should be defined on the parent object.
relations.nameThe name of the relationship used for API/SDK calls and liquid.
relations.display_nameThe user-friendly name used for display within the UI.
relations.child_objectThe child object that is connected to this object.
relations.join_fields.parentAny field on this object that you want to relate to another object.
relations.join_fields.childMust be a primary key on the child object.

Create static schema

With static schema, when users install your app, OCP parses all files within src/schema and creates all described objects and fields. You can either edit the schema of the customer object, or create new custom objects:

  • To add a field or an identifier to the customer object, create a file named customers.yaml within the src/schema folder.
  • To add a custom object, create a file named <plural_object_name>.yaml within the src/schema folder. For example, a file named acme_tickets.yaml for the acme_tickets object.



The file names within the src/schema folder must match the name of an existing object or the name of the custom object you want to create.

Below is an example of an src/schema/acme_tickets.yaml file:

name: acme_tickets
display_name: Acme Tickets
alias: acme_ticket
  - name: acme_ticket_id
    display_name: Acme Ticket ID
    type: string
    primary: true
  - name: tag_id
    display_name: Tag ID
    type: string
  - name: acme_ticket_tag
    display_name: Acme Ticket Tag
    child_object: acme_ticket_tags
      - parent: tag_id
        child: acme_ticket_tag_id

Create identifiers

Additionally, for the customers object, you can create custom identifiers. For example:

name: customers
  - name: acme_ios_push_token
    display_name: Acme iOS Push Token
    merge_confidence: high
    messaging: true
identifiers.nameThe singular name of the identifier used within API and SDK calls. Must be unique within the object and prefixed by your app_id.

All identifiers must include one of the following suffixes: id, hash, number, token, alias, address, key
identifiers.display_nameThe user-friendly name used in the ODP UI to refer to the identifier. Must be unique within the object.
identifiers.merge_confidenceThe confidence level for the identifier. Must be high or low. For more information on identifier confidence levels, see Identifier confidence.
identifiers.messaging(Optional) Boolean. Defaults to false.

true – You can use this identifier to contact the customer on a channel.
false – You can not use this identifier to contact the customer on a channel.

When you create an identifier, ODP creates three fields:

  • The plural version of the identifier (for example, ios_push_tokens) on the Customer object that contains a list of 25 last seen identifiers for that customer.
  • The singular version of the identifier (for example, ios_push_token) on the Customer object that contains the last seen identifier for that customer.
  • An event audit version that is populated when an event comes in with the singular version (for example, event_ios_push_token).

All events and data should use the singular version of the identifier name (for example, ios_push_token).

Create dynamic schema

Use dynamic schema to create customer fields per client. You cannot create these objects and fields in the schema YAML files because they differ for each install.
OCP provides a number of methods to create fields programmatically. For more information, see the Node SDK reference for Fields, Objects, and Relationships.

Usually, dynamically creating objects and fields is performed in lifecycle callback functions. For example, onInstall or onUpdate.



When updating schema dynamically, you should always check if the object or field already exists.

Below is an example of dynamically creating an acme_tickets Object.

import {z} from '@zaiusinc/node-sdk';

await z.schema.createObject({
  'name': 'acme_tickets',
  'display_name': 'Acme Tickets',
  'alias': 'acme_ticket',
  'fields': [
      'name': 'acme_ticket_id',
      'display_name': 'Acme Ticket ID',
      'type': 'string',
      'primary': true
      'name': 'tag_id',
      'display_name': 'Tag ID',
      'type': 'string'
  'relations': [
      'name': 'acme_ticket_tag',
      'display_name': 'Acme Ticket Tag',
      'child_object': 'acme_ticket_tags',
      'join_fields': [{
        'parent': 'tag_id',
        'child': 'acme_ticket_tag_id'

Below is an example of dynamically creating a tag_id Field.

import {z} from '@zaiusinc/node-sdk';

await z.schema.createField('acme_tickets', {
    'name': 'tag_id',
    'display_name': 'Tag ID',
    'type': 'string'

Below is an example of dynamically creating an acme_ticket_tag Relationship.

import {z} from '@zaiusinc/node-sdk';

await z.schema.createRelation('acme_tickets', {
    'name': 'acme_ticket_tag',
    'display_name': 'Acme Ticket Tag',
    'child_object': 'acme_ticket_tags',
    'join_fields': [{
        'parent': 'tag_id',
        'child': 'acme_ticket_tag_id'