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
| Aspect | Virtual Type | Model Class |
|---|---|---|
| Creation | You create inline | Developer creates |
| Time | Minutes | Hours/Days |
| Coding | No coding | Backend code |
| Best for | Rapid iteration | Production 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:
Developer writes backend code:
- Creates a class definition
- Specifies field names and types
- Adds validation rules
System generates frontend code:
- Automatically creates matching interface
- Enables autocomplete everywhere
- Provides type checking
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, validatedNOT 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 fitHow to Request a Model
If you decide a custom model is needed:
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 updatedExplain 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-criticalSubmit to development team
Wait for implementation:
Developer creates model ↓ System generates code ↓ Model available everywhere ↓ Autocomplete works automaticallyUse 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
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"]
}
}Create a new column that will return custom data
Virtual Type Syntax
Basic structure:
The structure follows a simple pattern:
- Declare it's an "object"
- List the "properties" (fields)
- Specify each field's type
Primitive types:
You can use these basic types:
string- Text valuesnumber- Numeric valuesboolean- True/false valuesdate- Date/time values
Example:
{
"type": "object",
"properties": {
"name": "string",
"age": "number",
"active": "boolean",
"created": "date"
}
}Arrays (lists):
For fields that contain multiple values:
{
"type": "object",
"properties": {
"tags": "string[]",
"scores": "number[]",
"flags": "boolean[]"
}
}The [] means "array of" (list of multiple items)
Nested objects:
For data with sub-sections:
{
"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:
{
"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:
{
"company": {
"name": "Acme Corp",
"domain": "acme.com",
"size": "100-500"
},
"confidence": 0.95,
"sources": ["clearbit", "linkedin", "crunchbase"]
}Virtual Type definition:
{
"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]"
Example 2: Multi-Stage Processing
Use case: Track processing stages with status
Data structure:
{
"stage_1_enrichment": {
"status": "complete",
"data": {...},
"timestamp": "2024-10-31T14:23:00Z"
},
"stage_2_scoring": {
"status": "pending",
"data": null,
"timestamp": null
}
}Virtual Type definition:
{
"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:
{
"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:
{
"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:
{
"type": "object",
"properties": {
"name": "string",
"email": "string"
}
}Phase 2: Add as needed:
{
"type": "object",
"properties": {
"name": "string",
"email": "string",
"phone": "string",
"company": "string"
}
}Phase 3: Add nesting when beneficial:
{
"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:
- Run column once without Virtual Type
- Look at the actual output data
- Note the exact field names
- Define Virtual Type to match
- Verify autocomplete works
Example:
- If your API returns
company_name, use"company_name": "string"(notcompanyNameorname) - Field names must match exactly
Practice 3: Use Descriptive Field Names
Bad:
{
"type": "object",
"properties": {
"d1": "string",
"d2": "number",
"f": "boolean"
}
}❌ What do d1, d2, f mean? Confusing!
Good:
{
"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/docsThis 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:
- Document current Virtual Type
- Request Model Class from developers (see Part 1)
- Developers create the model
- Update columns to use new Model
- 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
| Aspect | Virtual Type | Model Class |
|---|---|---|
| Creation | You create inline | Developer creates in code |
| Time to create | Minutes | Hours/Days |
| Coding required | No | Yes |
| Reusability | Column-specific | System-wide |
| Validation | Basic | Strict, customizable |
| Autocomplete | Yes | Yes |
| Maintenance | You maintain | Developer maintains |
| Best for | Rapid iteration | Production 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:
{
"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:
{
"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-31Update 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! 📊