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.
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
| Type | Constraints |
|---|---|
STRING | Characters [a-z, A-Z, 0-9]. Default max length: 255 (configurable via typeValidator) |
LONG | 64-bit integer. No spaces, dots, or commas. Range: -9223372036854775808 to 9223372036854775808 |
BOOLEAN | Must be "true" or "false" |
DATE | YYYYMMDD, YYYY-MM-DD, or dd/MM/yyyy |
TIMESTAMP | Accepts 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 |
NUMERIC | Decimal 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.
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
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
This operation is irreversible. All data stored in the table will be permanently deleted.
{
"on": "TABLE",
"type": "DELETE"
}
Make an attribute mandatory
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
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
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.
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.
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
STRINGattributes - Big Repository: Max 100,000 rows — No
recordDisplayExpressionin display options (STRINGallowed)
{
"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.
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
}
}
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"
}
}