Skip to main content

Custom Tables

Retrieving the structure of an existing custom table

To retrieve all custom tables in an entity:

GET /custom-table-structure/v5/entities/{entity}/custom-tables

To retrieve a single table by ID:

GET /custom-table-structure/v5/entities/{entity}/custom-tables/{customTableId}

Example response:

{
"id": "a4aa57cd-64c4-49bc-a17b-34ac8dfa76f8",
"name": "OnlineOrders",
"type": "INTERACTIONS",
"primaryKeyAttribute": "orderId",
"creationTimeAttribute": "orderMoment",
"bigTable": false,
"attributes": [
{
"name": "orderId",
"valueType": "STRING",
"mandatory": true,
"indexed": true,
"unique": true,
"valueRestriction": { "minLength": 20, "maxLength": 20 }
},
{
"name": "storeId",
"valueType": "LONG",
"mandatory": true,
"indexed": true,
"unique": false
},
{
"name": "customerId",
"valueType": "LONG",
"mandatory": true,
"indexed": true,
"unique": false
},
{
"name": "orderMoment",
"valueType": "TIMESTAMP",
"mandatory": true,
"indexed": true,
"unique": false
},
{
"name": "amount",
"valueType": "NUMBER",
"mandatory": true,
"indexed": false,
"unique": false
},
{
"name": "status",
"valueType": "STRING",
"mandatory": true,
"indexed": true,
"unique": false,
"valueRestriction": {
"acceptedValues": ["NEW", "SHIPPING", "SHIPPED", "CANCELED"]
}
}
],
"foreignKeys": [
{
"name": "link-to-customer",
"attribute": "customerId",
"reference": {
"tableType": "PROFILE_TABLE",
"tableId": "97",
"attribute": "customerId"
},
"onDelete": "CASCADE"
},
{
"name": "link-to-store",
"attribute": "storeId",
"reference": {
"tableType": "CUSTOM_TABLE",
"tableId": "d4d85214-66d4-4072-b98b-a86c9253b980",
"attribute": "storeId"
},
"onDelete": "CASCADE"
}
],
"eventsToTrigger": [
{
"name": "shipped-online-order",
"triggerRules": [
{
"onOperation": "UPDATE",
"onAnyAttribute": ["status"],
"matchingConditions": {
"stateBeforeOperation": [
{ "attribute": "status", "operator": "!=", "value": "SHIPPED" }
],
"stateAfterOperation": [
{ "attribute": "status", "operator": "=", "value": "SHIPPED" }
]
}
}
]
}
],
"cleaningRule": {
"type": "OLDEST_RECORDS",
"numberOfRecordsToKeep": 200000
},
"displayOptions": {
"displayName": "Online Orders",
"description": "Orders from an online store, linked to the Customers profile table.",
"forAttributes": [
{ "name": "orderId", "displayName": "Online Orders ID", "description": "The unique id of the order" },
{ "name": "storeId", "displayName": "Store ID", "description": "The unique id of the online store" },
{ "name": "customerId", "displayName": "Customer ID", "description": "The unique id of the customer" },
{ "name": "orderMoment", "displayName": "Order Moment", "description": "When the order was created" },
{ "name": "amount", "displayName": "Amount", "description": "The amount of the order" },
{ "name": "status", "displayName": "Status", "description": "The status of the order" }
],
"forEvents": [
{ "name": "shipped-online-order", "displayName": "Shipped Online Order" }
]
}
}

Creating a new custom table

POST /custom-table-structure/v5/entities/{entity}/custom-tables

You can build a new table structure from scratch using the API specs, or reuse a definition retrieved from an existing table.

tip

When reusing an existing table definition, remove the id, creationMoment, and updateMoment fields from the attributes and displayOptions arrays — these are generated automatically by Actito.

Example request body:

{
"name": "OnlineOrders",
"type": "INTERACTIONS",
"primaryKeyAttribute": "orderId",
"creationTimeAttribute": "orderMoment",
"compositeKey": null,
"bigTable": false,
"attributes": [
{
"name": "orderId",
"valueType": "STRING",
"mandatory": true,
"indexed": true,
"unique": true,
"valueRestriction": { "minLength": 20, "maxLength": 20 }
},
{
"name": "storeId",
"valueType": "LONG",
"mandatory": true,
"indexed": true,
"unique": false,
"valueRestriction": null
},
{
"name": "customerId",
"valueType": "LONG",
"mandatory": true,
"indexed": true,
"unique": false,
"valueRestriction": null
},
{
"name": "orderMoment",
"valueType": "TIMESTAMP",
"mandatory": true,
"indexed": true,
"unique": false,
"valueRestriction": null
},
{
"name": "amount",
"valueType": "NUMBER",
"mandatory": true,
"indexed": false,
"unique": false,
"valueRestriction": null
},
{
"name": "status",
"valueType": "STRING",
"mandatory": true,
"indexed": true,
"unique": false,
"valueRestriction": {
"acceptedValues": ["NEW", "SHIPPING", "SHIPPED", "CANCELED"]
}
}
],
"valueAttribute": "amount",
"foreignKeys": [
{
"name": "link-to-customer",
"attribute": "customerId",
"reference": {
"tableType": "PROFILE_TABLE",
"tableId": "97",
"attribute": "customerId"
},
"onDelete": "CASCADE"
},
{
"name": "link-to-store",
"attribute": "storeId",
"reference": {
"tableType": "CUSTOM_TABLE",
"tableId": "d4d85214-66d4-4072-b98b-a86c9253b980",
"attribute": "storeId"
},
"onDelete": "CASCADE"
}
],
"eventsToTrigger": [
{
"name": "shipped-online-order",
"triggerRules": [
{
"onOperation": "UPDATE",
"onAnyAttribute": ["status"],
"matchingConditions": {
"stateBeforeOperation": [
{ "attribute": "status", "operator": "!=", "value": "SHIPPED" }
],
"stateAfterOperation": [
{ "attribute": "status", "operator": "=", "value": "SHIPPED" }
]
}
}
]
}
],
"cleaningRule": {
"type": "OLDEST_RECORDS",
"numberOfRecordsToKeep": 200000
},
"displayOptions": {
"displayName": "Online Orders",
"description": "Orders from an online store, linked to the Customers profile table.",
"forAttributes": [
{ "name": "orderId", "displayName": "Online Orders ID", "description": "The unique id of the order" },
{ "name": "storeId", "displayName": "Store ID", "description": "The unique id of the online store" },
{ "name": "customerId", "displayName": "Customer ID", "description": "The unique id of the customer" },
{ "name": "orderMoment", "displayName": "Order Moment", "description": "When the order was created" },
{ "name": "amount", "displayName": "Amount", "description": "The amount of the order" },
{ "name": "status", "displayName": "Status", "description": "The status of the order" }
],
"forEvents": [
{ "name": "shipped-online-order", "displayName": "Shipped Online Order" }
]
}
}

Field value types

TypeConstraints
STRINGCharacters [a-z, A-Z, 0-9]. Default max length: 255 (configurable via typeValidator)
LONG64-bit integer. No spaces, dots, or commas. Range: -9223372036854775808 to 9223372036854775808
BOOLEANMust be "true" or "false"
DATEYYYYMMDD, YYYY-MM-DD, or dd/MM/yyyy
TIMESTAMPAccepts date-only formats (time defaults to 00:00:00) or full datetime: YYYYMMDDhhmmss, YYYY-MM-DD hh:mm:ss, dd/MM/yyyy HH:mm:ss, MM/dd/yyyy hh:mm:ss AM|PM
NUMERICDecimal separator must be .. No character limit.

Creating a table in the UI

Users can also create custom tables in the Table structure app by importing a JSON file following the same structure as the API.

tip

When creating via the UI, add an "entityName" parameter to specify the target entity (in the API, the entity is passed in the URL path):

{
"name": "Orders",
"entityName": "actito",
"type": "INTERACTIONS"
}

Updating a custom table

Updates to a custom table are applied via change requests — asynchronous operations that modify the table's metadata, field definitions, or structure.

POST /custom-table-structure/v5/entities/{entity}/custom-tables/{customTableId}/change-requests

The request body depends on the type of change. Use the One of tabs in the API specs for the relevant documentation.

Add an attribute

info

A mandatory attribute cannot be added to an already populated table.

{
"on": "TABLE",
"type": "ADD_ATTRIBUTE",
"attribute": {
"name": "My_New_Attribute",
"valueType": "STRING",
"mandatory": false,
"indexed": false,
"unique": false,
"valueRestriction": { "maxLength": 30, "minLength": 0 }
},
"displayOptions": {
"name": "My_New_Attribute",
"displayName": "My New Attribute"
}
}

Delete an attribute

{
"on": "TABLE",
"type": "REMOVE_ATTRIBUTE",
"attributeName": "My_Old_Attribute"
}

Add a foreign key

A foreign key links two tables together (e.g. an Orders table with an Order Lines table, or an Interactions table with a Repository). The foreign key attribute must match a key attribute in the target table, and both fields must share the same value type.

{
"on": "TABLE",
"type": "ADD_FOREIGN_KEY",
"foreignKey": {
"name": "link-to-stores-repository",
"attribute": "store",
"reference": {
"tableType": "CUSTOM_TABLE",
"tableId": "1f74f230-9069-4137-8ae6-d2e328721fae",
"attribute": "storeID"
},
"onDelete": "CASCADE",
"integrityCheck": true
}
}

Remove a foreign key

{
"on": "TABLE",
"type": "REMOVE_FOREIGN_KEY",
"name": "link-to-stores-repository"
}

Delete a table

danger

This operation is irreversible. All data stored in the table will be permanently deleted.

{
"on": "TABLE",
"type": "DELETE"
}

Make an attribute mandatory

info

This only works if every existing record already has a value for this attribute. Otherwise, run a data import first to populate missing values.

{
"on": "ATTRIBUTE",
"type": "MAKE_MANDATORY",
"attributeName": "MyAttribute"
}

Make an attribute non-mandatory

{
"on": "ATTRIBUTE",
"type": "MAKE_NON_MANDATORY",
"attributeName": "MyAttribute"
}

Make an attribute unique

info

This only works if all existing values for the field are already unique. Empty values are permitted — they are not considered duplicates. Remove any duplicates before applying this change.

{
"on": "ATTRIBUTE",
"type": "MAKE_UNIQUE",
"attributeName": "MyNewUniqueField"
}

Make an attribute non-unique

info

Ensure the field is not the table's primary key and is not used as a foreign key reference by another table.

{
"on": "ATTRIBUTE",
"type": "MAKE_NON_UNIQUE",
"attributeName": "MyFormerlyUniqueField"
}

Add an index on an attribute

Indexing a field allows interface users to search on it and improves targeting performance. See table index documentation.

caution

Maximum 15 indexed fields per table. Unique and technical fields are indexed by default. Too many indexes can slow down data creation and updates.

{
"on": "ATTRIBUTE",
"type": "ADD_INDEX",
"attributeName": "MyNewIndexedField"
}

Remove an index from an attribute

{
"on": "ATTRIBUTE",
"type": "REMOVE_INDEX",
"attributeName": "MyFormerlyIndexedField"
}

Add accepted values for an attribute

This only works for attributes that were originally created with value restrictions.

{
"on": "ATTRIBUTE",
"type": "ADD_ACCEPTED_VALUES",
"attributeName": "MyAttribute",
"acceptedValues": ["newValue1", "newValue2", "newValue3"]
}

Update table parameters — creationTimeAttribute

By default, the creation time is the technical creationMoment generated by Actito. Setting a custom field (e.g. orderMoment) as the creation time affects how interactions are sorted in the UI, how the OLDEST_RECORDS cleaning rule is calculated, and recency in the Activation Matrix.

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"creationTimeAttribute": "orderMoment"
}

Update table parameters — valueAttribute

Sets the monetary value of an interaction, used in Activation Matrix calculations.

info

Only applicable to INTERACTIONS type tables. The attribute must be numeric (NUMBER or LONG).

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"valueAttribute": "amount"
}

Update table parameters — bigTable

Marks the table as BIG, increasing the maximum record count by 10x but with restrictions:

  • Big Interactions / Linked Data: Max 100M rows — Max 20 attributes — No STRING attributes
  • Big Repository: Max 100,000 rows — No recordDisplayExpression in display options (STRING allowed)
{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"bigTable": true
}

Update table parameters — eventsToTrigger

Custom table events fire when a record's state changes (create, update, delete) and can trigger automations such as scenarios or webhooks.

info

This change request has PATCH behavior: to add a new event without removing existing ones, you must repeat all existing events in the request body.

Example — two events:

{
"eventsToTrigger": [
{
"name": "CREATE",
"triggerRules": [
{ "onOperation": "CREATE" }
]
},
{
"name": "offerReady",
"triggerRules": [
{
"onOperation": "UPDATE",
"onAnyAttribute": ["synchronized"],
"onMatchingState": {
"stateBeforeOperation": {
"logicalOperator": "AND",
"predicates": [{ "operator": "=", "attribute": "synchronized", "value": false }]
},
"stateAfterOperation": {
"logicalOperator": "AND",
"predicates": [{ "operator": "=", "attribute": "synchronized", "value": true }]
}
}
},
{
"onOperation": "CREATE",
"onMatchingState": {
"stateAfterOperation": {
"logicalOperator": "AND",
"predicates": [{ "operator": "=", "attribute": "synchronized", "value": true }]
}
}
}
]
}
]
}

Update table parameters — cleaningRules

Cleaning rules automatically delete records to stay within table size limits. Two strategies are available:

FIFO — keep the N most recent records:

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"cleaningRules": {
"oldestRecordsRule": { "numberOfRecordsToKeep": 2000000 }
}
}

Criteria-based — delete records matching a condition:

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"cleaningRules": {
"matchingRule": {
"condition": {
"logicalOperator": "AND",
"predicates": [{ "operator": "=", "attribute": "status", "value": "CANCELLED" }]
}
}
}
}

Both rules can be combined, and complex nested conditions are supported:

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"cleaningRules": {
"oldestRecordsRule": { "numberOfRecordsToKeep": 5000000 },
"matchingRule": {
"condition": {
"logicalOperator": "OR",
"conditions": [
{
"logicalOperator": "AND",
"predicates": [
{ "operator": "=", "attribute": "status", "value": "CANCELED" },
{ "operator": "<", "attribute": "amount", "value": 50.0 }
]
},
{
"logicalOperator": "AND",
"predicates": [
{ "operator": "=", "attribute": "storeId", "value": "2" },
{ "operator": "EMPTY", "attribute": "orderId" }
]
}
]
}
}
}
}

To remove all cleaning rules:

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"cleaningRules": {
"oldestRecordsRule": null,
"matchingRule": null
}
}
info

Cleaning rule change requests have PATCH behavior: to add a rule without removing existing ones, repeat all existing rules in the request body.

Update table parameters — displayOptions

Display options define the human-readable names and descriptions shown in the Actito interface.

{
"on": "TABLE",
"type": "UPDATE_PARAMETERS",
"displayOptions": {
"displayName": "Online Orders",
"description": "Orders from an online store, linked to the Customers profile table.",
"forAttributes": [
{ "name": "orderId", "displayName": "Online Orders ID", "description": "The unique id of the order" },
{ "name": "storeId", "displayName": "Store ID", "description": "The unique id of the online store" },
{ "name": "customerId", "displayName": "Customer ID", "description": "The unique id of the customer" },
{ "name": "orderMoment", "displayName": "Order Moment", "description": "When the order was created" },
{ "name": "amount", "displayName": "Amount", "description": "The amount of the order" },
{ "name": "status", "displayName": "Status", "description": "The status of the order" }
],
"forEvents": [
{ "name": "shipped-online-order", "displayName": "Shipped Online Order" }
]
}
}

Updating a custom table from the UI

All change requests above can also be applied in the Actito UI by uploading a JSON file in the Manage tables structure app. The Delete table operation has its own dedicated button and does not use a JSON file.

Checking the result of a change request

Since change requests are asynchronous, a successful API call only means the request was accepted — not that it completed successfully. To verify the outcome:

GET /custom-table-structure/v5/entities/{entity}/custom-tables/{customTableId}/change-requests/{changeRequestId}

Example response:

{
"id": "ae808e05-01e7-4299-99cf-f7dc190d5562",
"on": "TABLE",
"type": "ADD_ATTRIBUTE",
"attribute": {
"name": "description",
"valueType": "STRING",
"mandatory": false,
"indexed": false,
"unique": false
},
"status": "SUCCEEDED",
"_audit": {
"createdAt": "2019-11-21T13:00:00.000Z",
"updatedAt": "2019-11-21T13:01:00.000Z"
}
}