Skip to content

17.2 Custom Data Models

Custom data models help you define the structure of data in your automations. This guide explains two approaches: Model Classes (created by developers) and Virtual Types (created by you), so you understand when and how to define custom data structures.


Overview: Two Ways to Define Data Types

Approach 1: Model Classes (Developer Feature)

What they are: Predefined data types built into the system

Who creates them: Developers with backend access

When created: During product development

Benefits:

  • Strongly validated by the system
  • Available everywhere in your organization
  • Built-in autocomplete support
  • Production-ready and tested

Examples you'll see:

  • User (contains: id, name, email, organizations)
  • Contact (contains: name, email, phone, company)
  • Account (contains: name, type, credentials)

As a user: You use these models - you don't create them


Approach 2: Virtual Types (User Feature)

What they are: Custom data structures you define yourself

Who creates them: You (any automation builder)

When created: Whenever you need them for your columns

Benefits:

  • No coding required
  • Quick to create (minutes, not days)
  • Column-specific
  • Flexible and easy to update
  • Available immediately

Use cases:

  • API returns custom format
  • Need specific structure for your workflow
  • One-off data format
  • Rapid prototyping

As a user: This is your primary tool for custom data

Two Approaches to Custom Data Types
Developer FeatureModel Classes
Predefined data types created by developers, available system-wide
Strongly validated
Reusable everywhere
Built-in autocomplete
Production-ready
You consume these models - developers create them
User FeatureVirtual Types
Custom data structures you define yourself, right in the column settings
No coding required
Quick to create
Flexible and iterative
Immediate availability
This is what you'll work with most
AspectVirtual TypeModel Class
CreationYou create inlineDeveloper creates
TimeMinutesHours/Days
CodingNo codingBackend code
Best forRapid iterationProduction systems

Part 1: Understanding Model Classes

What Are Model Classes?

Model Classes are data types that developers create and make available throughout the entire system.

How they're created:

  1. Developer writes backend code:

    • Creates a class definition
    • Specifies field names and types
    • Adds validation rules
  2. System generates frontend code:

    • Automatically creates matching interface
    • Enables autocomplete everywhere
    • Provides type checking
  3. Model becomes available:

    • Shows up in column Output Type options
    • Provides autocomplete in field references
    • Works throughout the system

When to Request a Custom Model

Good reasons to ask developers for a custom model:

Used across many workflows:

Example: "CustomerProfile" model
Used in: Lead enrichment, CRM sync, email campaigns, reporting
Benefit: Everyone uses the exact same structure

Complex nested structure:

Example: "OrderWithLineItems" model
Contains:
- Order ID
- Customer information
  - Name, email, address
- Line items (list)
  - Product name, quantity, price
- Totals
  - Subtotal, tax, shipping, total

Shared across organization:

Example: "ProjectTask" model
Used by: Multiple teams and taibles
Benefit: Consistent structure everywhere

Production-critical:

Example: "PaymentTransaction" model
Needs: Strict validation, audit trail, compliance
Benefit: Robust, tested, validated

NOT good reasons:

One-time use:

Use Virtual Type instead
Faster, no developer needed

Simple structure:

Just 2-3 fields
Use Virtual Type

Rapidly changing:

Still figuring out structure
Use Virtual Type, convert later if needed

Column-specific:

Only one column uses it
Virtual Type is better fit

How to Request a Model

If you decide a custom model is needed:

  1. Document the structure:

    Model Name: CustomerProfile
    
    Fields:
    - customer_id (text): Unique identifier
    - full_name (text): Customer's full name
    - email (text): Primary email address
    - phone (text, optional): Phone number
    - company (text, optional): Company name
    - industry (text, optional): Industry category
    - employee_count (number, optional): Company size
    - is_qualified (yes/no): Lead qualification status
    - lead_score (number): Score from 0-100
    - tags (list of text): Category tags
    - created_at (date): When created
    - updated_at (date): Last updated
  2. Explain the use case:

    Used in:
    - Lead Enrichment Taible
    - CRM Synchronization Taible
    - Email Campaign Taible
    - Reporting Dashboard
    
    Frequency:
    - Processed 1,000+ times daily
    - Critical for sales workflow
    
    Why not Virtual Type:
    - Used across 4 taibles
    - Needs consistent structure
    - Production-critical
  3. Submit to development team

  4. Wait for implementation:

    Developer creates model
    
    System generates code
    
    Model available everywhere
    
    Autocomplete works automatically
  5. Use in columns:

    • Select the model as Output Type
    • Autocomplete now works for all fields
    • Data validation happens automatically

Part 2: Working with Virtual Types (Your Primary Tool)

What Are Virtual Types?

Virtual Types = Custom data structures you define yourself, right in the column settings

Key concept: You describe what fields your data contains, and the system uses that for autocomplete and validation

Column Configuration
What is Output Type?
Define the structure of data this column will produce. This enables autocomplete and data validation.

Creating a Virtual Type

Scenario: An API returns custom data you want to capture

API response:

{
  "lead": {
    "contact_name": "John Smith",
    "contact_email": "john@example.com",
    "score": 85,
    "qualified": true,
    "tags": ["enterprise", "hot-lead"]
  }
}
Creating a Virtual Type: Step by Step
Step 1Add Column

Create a new column that will return custom data

Columns
Email
Company
+ Add Column: lead_enrichment
Step 2Configure Data Source

Set up your API call or custom code that returns the data

HTTP Request Configuration
URL
https://api.enrichment.com/v1/enrich
Method
POST
Body
{"email": "[Email Address]"}
Step 3Define Virtual Type

Describe the structure of your data in the Output Type field

Output Type
"type": "object",
"properties": {
"company_name": "string",
"industry": "string",
"employee_count": "number"
}
This tells the system: "This column returns an object with these specific fields"
Step 4Use with Autocomplete

Reference your data in other columns with full autocomplete support

Using the Data
Email Template
Hello! I noticed [Company Name] is in the [Industry] industry.
As you type field names, the system suggests them from your Virtual Type definition!
Step 1 of 4

Virtual Type Syntax

Basic structure:

Virtual Type Structure
"type": "object",
"properties": {
"contact_name": "string",
"contact_email": "string",
"score": "number",
"qualified": "boolean"
}
stringText values
numberNumeric values
booleanTrue/False
objectNested data

The structure follows a simple pattern:

  1. Declare it's an "object"
  2. List the "properties" (fields)
  3. Specify each field's type

Primitive types:

You can use these basic types:

  • string - Text values
  • number - Numeric values
  • boolean - True/false values
  • date - Date/time values

Example:

json
{
  "type": "object",
  "properties": {
    "name": "string",
    "age": "number",
    "active": "boolean",
    "created": "date"
  }
}

Arrays (lists):

For fields that contain multiple values:

json
{
  "type": "object",
  "properties": {
    "tags": "string[]",
    "scores": "number[]",
    "flags": "boolean[]"
  }
}

The [] means "array of" (list of multiple items)


Nested objects:

For data with sub-sections:

json
{
  "type": "object",
  "properties": {
    "person": {
      "type": "object",
      "properties": {
        "name": "string",
        "age": "number"
      }
    },
    "company": {
      "type": "object",
      "properties": {
        "name": "string",
        "employees": "number"
      }
    }
  }
}

Access in other columns:

  • [Person Name] - Gets the person's name
  • [Company Employees] - Gets the employee count

Arrays of objects:

For lists where each item has multiple fields:

json
{
  "type": "object",
  "properties": {
    "contacts": {
      "type": "array",
      "elementType": {
        "type": "object",
        "properties": {
          "email": "string",
          "type": "string"
        }
      }
    }
  }
}

Access in other columns:

  • [Contacts First Email] - Gets the first contact's email
  • [Contacts First Type] - Gets the first contact's type

Virtual Type Examples

Example 1: API Response Structure

Use case: Enrich leads via custom API

API returns:

json
{
  "company": {
    "name": "Acme Corp",
    "domain": "acme.com",
    "size": "100-500"
  },
  "confidence": 0.95,
  "sources": ["clearbit", "linkedin", "crunchbase"]
}

Virtual Type definition:

json
{
  "type": "object",
  "properties": {
    "company": {
      "type": "object",
      "properties": {
        "name": "string",
        "domain": "string",
        "size": "string"
      }
    },
    "confidence": "number",
    "sources": "string[]"
  }
}

Using it in other columns:

  • Email subject: "About [Company Name]"
  • Condition: If [Confidence] is greater than 0.8
  • Template: "Found on [Sources First Item]"
Autocomplete in Action
Email Subject: Hello [Customer Name]
Available fields from lead_data:
stringcontact_name
stringcontact_email
numberscore
booleanqualified
With Virtual Types defined, the system knows exactly what fields are available and can suggest them as you type!

Example 2: Multi-Stage Processing

Use case: Track processing stages with status

Data structure:

json
{
  "stage_1_enrichment": {
    "status": "complete",
    "data": {...},
    "timestamp": "2024-10-31T14:23:00Z"
  },
  "stage_2_scoring": {
    "status": "pending",
    "data": null,
    "timestamp": null
  }
}

Virtual Type definition:

json
{
  "type": "object",
  "properties": {
    "stage_1_enrichment": {
      "type": "object",
      "properties": {
        "status": "string",
        "data": "object",
        "timestamp": "string"
      }
    },
    "stage_2_scoring": {
      "type": "object",
      "properties": {
        "status": "string",
        "data": "object",
        "timestamp": "string"
      }
    }
  }
}

Use case: Check [Stage 1 Enrichment Status] before proceeding to next stage


Example 3: Validation Results

Use case: Email validation with detailed results

Data structure:

json
{
  "email": "john@example.com",
  "is_valid": true,
  "checks": {
    "syntax": true,
    "domain": true,
    "mx_record": true,
    "smtp": false
  },
  "risk_score": 25,
  "suggested_correction": null
}

Virtual Type definition:

json
{
  "type": "object",
  "properties": {
    "email": "string",
    "is_valid": "boolean",
    "checks": {
      "type": "object",
      "properties": {
        "syntax": "boolean",
        "domain": "boolean",
        "mx_record": "boolean",
        "smtp": "boolean"
      }
    },
    "risk_score": "number",
    "suggested_correction": "string"
  }
}

Use case:

  • Condition: If [Validation Is Valid] equals Yes, send email
  • Risk filter: If [Validation Risk Score] less than 50

Part 3: Virtual Type Best Practices

Practice 1: Start Simple, Iterate

Don't over-engineer from the start!

Phase 1: Basic structure:

json
{
  "type": "object",
  "properties": {
    "name": "string",
    "email": "string"
  }
}

Phase 2: Add as needed:

json
{
  "type": "object",
  "properties": {
    "name": "string",
    "email": "string",
    "phone": "string",
    "company": "string"
  }
}

Phase 3: Add nesting when beneficial:

json
{
  "type": "object",
  "properties": {
    "contact": {
      "type": "object",
      "properties": {
        "name": "string",
        "email": "string",
        "phone": "string"
      }
    },
    "company": "string"
  }
}

Start small, grow as needed!


Practice 2: Match Your Data

Rule: Virtual Type should match what your column actually returns

Testing workflow:

  1. Run column once without Virtual Type
  2. Look at the actual output data
  3. Note the exact field names
  4. Define Virtual Type to match
  5. Verify autocomplete works

Example:

  • If your API returns company_name, use "company_name": "string" (not companyName or name)
  • Field names must match exactly

Practice 3: Use Descriptive Field Names

Bad:

json
{
  "type": "object",
  "properties": {
    "d1": "string",
    "d2": "number",
    "f": "boolean"
  }
}

❌ What do d1, d2, f mean? Confusing!

Good:

json
{
  "type": "object",
  "properties": {
    "company_name": "string",
    "employee_count": "number",
    "is_verified": "boolean"
  }
}

✓ Self-documenting, clear purpose


Practice 4: Document Complex Types

For complex Virtual Types, add documentation:

In the column description, document:

Column: api_enrichment
Output Type: [Virtual Type definition shown above]

Documentation:
This column returns enrichment data from CustomAPI.

Structure:
- company_name: Company legal name
- company_domain: Primary domain
- company_size: Employee count range (e.g., "100-500")
- confidence: Match confidence score (0-1)
- sources: List of data sources used

Last updated: 2024-10-31
API docs: https://customapi.com/docs

This helps future you and your team!


Practice 5: Consider Upgrade Path

When Virtual Type grows complex:

Signs it's time to request a Model Class:

  • ✅ Used in 3+ taibles
  • ✅ 10+ fields
  • ✅ Critical for business operations
  • ✅ Needs strict validation
  • ✅ Frequently updated/maintained

Upgrade process:

  1. Document current Virtual Type
  2. Request Model Class from developers (see Part 1)
  3. Developers create the model
  4. Update columns to use new Model
  5. Remove Virtual Type definitions

Benefit: Better performance, validation, and easier maintenance


Part 4: Virtual Types vs. Model Classes

Quick Decision Guide

Use Virtual Type when:

  • ☑ Quick prototyping
  • ☑ Column-specific structure
  • ☑ One or two taibles use it
  • ☑ Structure may change
  • ☑ No developer access needed
  • ☑ Immediate need

Request Model Class when:

  • ☑ Used across organization
  • ☑ Production-critical
  • ☑ Complex validation needed
  • ☑ Shared by multiple teams
  • ☑ Long-term structure (stable)
  • ☑ Developer resources available

Comparison Table

AspectVirtual TypeModel Class
CreationYou create inlineDeveloper creates in code
Time to createMinutesHours/Days
Coding requiredNoYes
ReusabilityColumn-specificSystem-wide
ValidationBasicStrict, customizable
AutocompleteYesYes
MaintenanceYou maintainDeveloper maintains
Best forRapid iterationProduction systems

Part 5: Practical Workflow

Typical Virtual Type Workflow

Scenario: Integrate with new API that returns custom format


Step 1: Explore API

Test the API manually:

  • Use a tool like Postman
  • See the actual response
  • Understand the structure

Example response:

json
{
  "profile": {
    "name": "John Smith",
    "title": "CEO"
  },
  "company": "Acme Corp",
  "verified": true
}

Step 2: Create Column

Add HTTP Request column:

  • URL: API endpoint
  • Headers: Authentication
  • Body: Request payload

Step 3: Test Without Type

Run the column:

  • Check output in the cell
  • Verify response matches what you saw
  • Note exact field names (case-sensitive!)

Step 4: Define Virtual Type

In the column's Output Type field, enter:

json
{
  "type": "object",
  "properties": {
    "profile": {
      "type": "object",
      "properties": {
        "name": "string",
        "title": "string"
      }
    },
    "company": "string",
    "verified": "boolean"
  }
}

Step 5: Verify Autocomplete

Create a text template column:

  • Start typing field names
  • Autocomplete appears ✓
  • Select fields from the list
  • No more typos! ✓

Step 6: Use in Automation

Now build your workflow:

  • Condition: If [API Column Verified] equals Yes
  • Email subject: "Hello [API Column Profile Name]"
  • Logic based on: [API Column Profile Title]

Step 7: Document and Maintain

Add to column description:

Virtual Type matches CustomAPI v2 response.
See API docs: [link]
Last verified: 2024-10-31

Update when API changes!


Summary: Custom Data Models

You now understand both approaches to custom data structures:

Model Classes (developer feature):

  • Created by developers in backend code
  • Available system-wide automatically
  • Request for: shared, complex, production-critical structures
  • Developers create, you use them
  • Best for: reusable, validated, long-term models

Virtual Types (user feature):

  • Define yourself in column settings
  • No coding required
  • Create immediately as needed
  • Best for: column-specific, rapid iteration, prototyping
  • Upgrade to Model Class when it outgrows Virtual Type

Virtual Type creation:

  • Define structure in JSON-like format
  • Specify field names and types
  • Supports objects, arrays, nesting
  • Enables autocomplete in field references
  • Basic validation included

Decision making:

  • Start with Virtual Type (fast, flexible)
  • Test and iterate structure
  • Upgrade to Model Class when: used widely, production-critical, needs strict validation

Best practices:

  • Start simple, add complexity as needed
  • Match Virtual Type to actual data
  • Use descriptive field names
  • Document complex structures
  • Consider upgrade path to Model Class

Practical workflow:

  • Explore data structure
  • Create column
  • Test without type
  • Define Virtual Type
  • Verify autocomplete
  • Use in automation
  • Document and maintain

Next Steps

You've completed Section 17.2: Custom Data Models and Chapter 17: Type System and Data Modeling!

Next: Chapter 18: Filters and Views → Learn how to filter data, create saved views, and organize your taibles for efficient workflows.

Let's master data organization! 📊

Built with VitePress