Universal Tools API Reference
May 5, 2026 ยท View on GitHub
Complete reference for all 13 universal tools that consolidate 40+ resource-specific operations. Each tool works across multiple resource types using parameter-based routing.
๐ Field Mapping Update: Enhanced with Issue #473 improvements including corrected field mappings, category validation, special character preservation, and response normalization. See Field Mapping Guide for details.
Resource Types
All universal tools use the resource_type parameter to specify the target resource:
enum UniversalResourceType {
COMPANIES = 'companies',
PEOPLE = 'people',
RECORDS = 'records',
DEALS = 'deals',
TASKS = 'tasks',
LISTS = 'lists',
NOTES = 'notes',
}
search_records, search_records_advanced, search_records_by_timeframe, get_record_details, create_record, update_record, and delete_record also accept custom object slugs discovered into the mapping configuration, such as funds or investment_opportunities.
formatResult Architecture (Updated PR #483)
IMPORTANT: All universal tools now use consistent formatResult functions that always return strings. This eliminates dual-mode behavior and improves performance by 89.7%.
Consistent formatResult Contract
// All formatResult functions follow this pattern
formatResult: (data: AttioRecord | AttioRecord[], resourceType?: UniversalResourceType): string
// Performance optimized with:
// - No environment-dependent behavior
// - Type-safe Record<string, unknown> patterns
// - Memory-efficient string templates
// - 59% ESLint warning reduction (957โ395)
Core Universal Tools (8 tools)
1. records.search
Description: Universal search across all resource types with flexible filtering and intelligent query parsing for multi-field lookups.
Consolidates: search-companies, search-people, list-records, list-tasks
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
query?: string, // Smart query string (names, emails, phones, domains)
filters?: object, // Advanced filter conditions
search_type?: 'basic' | 'content', // Search type (default: 'basic')
fields?: string[], // Fields to search (content search only)
match_type?: 'exact' | 'partial' | 'fuzzy', // Match type (default: 'partial')
sort?: 'relevance' | 'created' | 'modified' | 'name', // Sort order (default: 'name')
limit?: number, // Max results (1-100, default: 10)
offset?: number // Pagination offset (default: 0)
}
Examples:
// Basic search for companies
await client.callTool('records.search', {
resource_type: 'companies',
query: 'tech startup',
limit: 20,
});
// Multi-field people lookup (name + email handled automatically)
await client.callTool('records.search', {
resource_type: 'people',
query: 'Alex Rivera alex.rivera@example.com',
});
// Phone number search (normalizes formatting + country code)
await client.callTool('records.search', {
resource_type: 'people',
query: '555-010-4477',
});
// Domain-aware company search (extracts domains from queries/emails)
await client.callTool('records.search', {
resource_type: 'companies',
query: 'examplecorp.com',
});
// Content search across multiple fields
await client.callTool('records.search', {
resource_type: 'companies',
query: 'artificial intelligence',
search_type: 'content',
sort: 'relevance',
});
// Search people with filters
await client.callTool('records.search', {
resource_type: 'people',
query: 'john',
filters: {
and: [{ attribute: 'industry', condition: 'equals', value: 'Technology' }],
},
});
// Partial match content search
await client.callTool('records.search', {
resource_type: 'companies',
query: 'automat',
search_type: 'content',
match_type: 'partial',
});
2. records.get_details
Description: Get detailed information for any record type.
Consolidates: get-company-details, get-person-details, records.get_details, get-task-details
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
record_id: string, // Required - Record identifier
fields?: string[] // Optional - Specific fields to include
}
Examples:
// Get company details
await client.callTool('records.get_details', {
resource_type: 'companies',
record_id: 'comp_123',
});
// Get custom object details
await client.callTool('records.get_details', {
resource_type: 'funds',
record_id: 'fund_123',
});
// Get person details with specific fields
await client.callTool('records.get_details', {
resource_type: 'people',
record_id: 'person_456',
fields: ['name', 'email', 'company'],
});
3. create-record
Description: Create a new record of any supported type.
Consolidates: create-company, create-person, create-record, create-task
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
record_data: object, // Required - Record creation data
return_details?: boolean // Optional - Return full record details
}
Examples:
// Create company
await client.callTool('create-record', {
resource_type: 'companies',
record_data: {
name: 'Acme Corp',
website: 'https://acme.com',
industry: 'Technology',
},
});
// Create person
await client.callTool('create-record', {
resource_type: 'people',
record_data: {
name: 'John Doe',
email: 'john@acme.com',
title: 'CEO',
},
return_details: true,
});
// Create a custom object record
await client.callTool('create-record', {
resource_type: 'funds',
record_data: {
name: 'Fund I',
stage: 'Active',
},
});
4. update-record
Description: Update an existing record of any supported type.
Consolidates: update-company, update-person, update-record, update-task
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
record_id: string, // Required - Record identifier
record_data: object, // Required - Update data
return_details?: boolean // Optional - Return full record details
}
Examples:
// Update company
await client.callTool('update-record', {
resource_type: 'companies',
record_id: 'comp_123',
record_data: {
industry: 'Fintech',
employee_count: 50,
},
});
// Update a custom object record
await client.callTool('update-record', {
resource_type: 'funds',
record_id: 'fund_123',
record_data: {
stage: 'Closed',
},
});
5. delete-record
Description: Delete a record of any supported type.
Consolidates: delete-company, delete-person, delete-record, delete-task
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
record_id: string // Required - Record identifier
}
Examples:
// Delete company
await client.callTool('delete-record', {
resource_type: 'companies',
record_id: 'comp_123',
});
// Delete a custom object record
await client.callTool('delete-record', {
resource_type: 'funds',
record_id: 'fund_123',
});
6. records.get_attributes
Description: Get attributes for any resource type.
Consolidates: get-company-attributes, get-person-attributes, get-record-attributes
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
record_id?: string, // Optional - Specific record
categories?: string[], // Optional - Attribute categories
fields?: string[] // Optional - Specific fields
}
Examples:
// Get all company attributes
await client.callTool('records.get_attributes', {
resource_type: 'companies',
});
// Get specific record attributes
await client.callTool('records.get_attributes', {
resource_type: 'people',
record_id: 'person_456',
fields: ['name', 'email', 'company'],
});
7. records.discover_attributes
Description: Discover available attributes for any resource type.
Consolidates: discover-company-attributes, discover-person-attributes, discover-record-attributes
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks'; // Required
}
Examples:
// Discover company attributes
await client.callTool('records.discover_attributes', {
resource_type: 'companies',
});
8. records.get_info
Description: Get specific types of detailed information (contact, business, social).
Consolidates: get-company-basic-info, get-company-contact-info, get-company-business-info, get-company-social-info
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
record_id: string, // Required - Record identifier
info_type: 'contact' | 'business' | 'social' | 'basic' | 'custom' // Required
}
Examples:
// Get company contact info
await client.callTool('records.get_info', {
resource_type: 'companies',
record_id: 'comp_123',
info_type: 'contact',
});
// Get company business info
await client.callTool('records.get_info', {
resource_type: 'companies',
record_id: 'comp_123',
info_type: 'business',
});
Advanced Universal Tools (5 tools)
9. records.search_advanced
Description: Complex searches with sorting and advanced filtering.
Consolidates: records.search_advanced-companies, records.search_advanced-people
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
query?: string, // Search query string
filters?: object, // Advanced filter conditions
sort_by?: string, // Field to sort by
sort_order?: 'asc' | 'desc', // Sort direction
limit?: number, // Max results (1-100, default: 10)
offset?: number // Pagination offset (default: 0)
}
Examples:
// Advanced company search with sorting
await client.callTool('records.search_advanced', {
resource_type: 'companies',
query: 'technology',
filters: {
and: [
{ attribute: 'employee_count', condition: 'greater_than', value: 100 },
],
},
sort_by: 'created_at',
sort_order: 'desc',
limit: 25,
});
10. records.search_by_relationship
Description: Cross-resource relationship searches.
Consolidates: search-companies-by-people, search-people-by-company
Schema:
{
relationship_type: 'company_to_people' | 'people_to_company' | 'person_to_tasks' | 'company_to_tasks', // Required
source_id: string, // Required - Source record ID
target_resource_type?: 'companies' | 'people' | 'records' | 'tasks', // Optional
limit?: number, // Max results (1-100, default: 10)
offset?: number // Pagination offset (default: 0)
}
Examples:
// Find people at a company
await client.callTool('records.search_by_relationship', {
relationship_type: 'company_to_people',
source_id: 'comp_123',
limit: 50,
});
// Find companies associated with a person
await client.callTool('records.search_by_relationship', {
relationship_type: 'people_to_company',
source_id: 'person_456',
});
11. records.search_by_content
Description: Content-based searches (notes, activity, interactions).
Consolidates: search-companies-by-notes, search-people-by-notes, search-people-by-activity
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
content_type: 'notes' | 'activity' | 'interactions', // Required
search_query: string, // Required - Cannot be empty
limit?: number, // Max results (1-100, default: 10)
offset?: number // Pagination offset (default: 0)
}
Examples:
// Search companies by notes
await client.callTool('records.search_by_content', {
resource_type: 'companies',
content_type: 'notes',
search_query: 'quarterly review',
});
// Search people by activity
await client.callTool('records.search_by_content', {
resource_type: 'people',
content_type: 'activity',
search_query: 'demo scheduled',
});
12. records.search_by_timeframe
Description: Time-based searches with date ranges. Supports natural language date expressions (v0.2.1+).
Consolidates: search-people-by-creation-date, search-people-by-modification-date, search-people-by-last-interaction
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
timeframe_type: 'created' | 'modified' | 'last_interaction', // Required
start_date?: string, // ISO 8601 or relative date (e.g., "last 7 days")
end_date?: string, // ISO 8601 or relative date (e.g., "yesterday")
preset?: string, // Date preset or relative expression (e.g., "this_month", "last 30 days")
limit?: number, // Max results (1-100, default: 10)
offset?: number // Pagination offset (default: 0)
}
modified remains part of the public vocabulary for backward compatibility, but Attio does not expose a live filterable modified timestamp for people or companies. Those requests now fail explicitly instead of returning a false empty result.
Supported Date Formats (v0.2.1+):
- ISO 8601:
'2024-01-01T00:00:00Z' - Relative expressions:
'today','yesterday','this week','last week','this month','last month','this year','last year' - Dynamic ranges:
'last N days','last N weeks','last N months'
Examples:
// Search people created in January 2024 (ISO format)
await client.callTool('records.search_by_timeframe', {
resource_type: 'people',
timeframe_type: 'created',
start_date: '2024-01-01T00:00:00Z',
end_date: '2024-01-31T23:59:59Z',
});
// Search people created in the last 30 days (natural language)
await client.callTool('records.search_by_timeframe', {
resource_type: 'people',
timeframe_type: 'created',
preset: 'last 30 days',
});
// Search companies with interactions after January 1, 2024
await client.callTool('records.search_by_timeframe', {
resource_type: 'companies',
timeframe_type: 'last_interaction',
start_date: '2024-01-01T00:00:00Z',
});
// Search companies by last interaction using relative dates
await client.callTool('records.search_by_timeframe', {
resource_type: 'companies',
timeframe_type: 'last_interaction',
start_date: 'last 7 days',
end_date: 'today',
});
13. records.batch
Description: Bulk operations on multiple records.
Consolidates: batch-create-companies, batch-update-companies, batch-delete-companies, records.search_batch-companies, batch-get-company-details, batch-create-records, batch-update-records
Schema:
{
resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
operation_type: 'create' | 'update' | 'delete' | 'search' | 'get', // Required
records?: Array<object>, // For create/update operations
record_ids?: string[], // For get/delete operations
query?: string, // For search operations (required, cannot be empty)
limit?: number, // Max results (1-50, default: 10)
offset?: number // Pagination offset (default: 0)
}
Examples:
// Batch create companies
await client.callTool('records.batch', {
resource_type: 'companies',
operation_type: 'create',
records: [
{ name: 'Company A', website: 'https://companya.com' },
{ name: 'Company B', website: 'https://companyb.com' },
],
});
// Batch get company details
await client.callTool('records.batch', {
resource_type: 'companies',
operation_type: 'get',
record_ids: ['comp_123', 'comp_456', 'comp_789'],
});
// Batch search companies
await client.callTool('records.batch', {
resource_type: 'companies',
operation_type: 'search',
query: 'technology startup',
limit: 50,
});
Parameter Validation Rules
Required Parameters
- All tools require
resource_type - Record operations require
record_id - Create/update operations require
record_data - Content searches require non-empty
search_query
Date Operators โ ๏ธ
Use correct operators for date filtering:
โ Correct:
{
condition: 'after';
} // Instead of greater_than_or_equals
{
condition: 'before';
} // Instead of less_than_or_equals
โ Incorrect:
{
condition: 'greater_than_or_equals';
} // Will cause API errors
{
condition: 'less_than_or_equals';
} // Will cause API errors
Valid Date Presets
('today',
'yesterday',
'this_week',
'last_week',
'this_month',
'last_month',
'this_quarter',
'last_quarter',
'this_year',
'last_year');
// โ Invalid: 'last_30_days'
Batch Operation Limits
- Maximum 50 records per batch operation
- Built-in rate limiting with 100ms delays
- Maximum 5 concurrent requests
- Error isolation - individual failures don't stop the batch
Query Requirements
- Search queries cannot be empty strings
- Use meaningful search terms
- Content searches require specific, non-generic queries
Error Handling
Common Error Types
-
Invalid Resource Type
Error: Invalid resource type: 'invalid_type' -
Missing Required Parameters
Error: Missing required parameter: record_id -
Invalid Date Operators
Error: Invalid operator: $greater_than_or_equals -
Empty Query Strings
Error: Search query cannot be empty -
Invalid Date Presets
Error: Invalid date preset: "last_30_days"
Error Response Format
{
error: string, // Error message
code: string, // Error code
resource_type?: string, // Resource type if applicable
operation?: string // Operation that failed
}
Testing and Mock Data
Test Environment Behavior
When running in test environment (NODE_ENV=test or VITEST=true), the universal tools automatically use mock data instead of making real API calls:
// Automatic mock data injection in test environment
await client.callTool('create-record', {
resource_type: 'tasks',
data: { content: 'Test task' },
});
// Returns: Mock task with proper Attio field format
// Special mock IDs trigger error scenarios
await client.callTool('records.get_details', {
resource_type: 'tasks',
record_id: 'mock-error-not-found',
});
// Returns: Error with "Record not found" message
Mock Data Format
Mock data follows the Attio field format with dual access patterns:
{
id: {
record_id: 'mock-task-123',
task_id: 'mock-task-123', // Resource-specific ID
workspace_id: 'mock-workspace'
},
// Nested format (Attio API standard)
values: {
content: [{ value: 'Task content' }],
status: [{ value: 'pending' }]
},
// Flattened format (backward compatibility)
content: 'Task content',
status: 'pending'
}
Error Testing
Use special mock IDs to test error handling:
mock-error-not-found: Triggers 404 not found errormock-error-unauthorized: Triggers 401 unauthorized errormock-error-invalid: Triggers 400 invalid request error
Performance Guidelines
Pagination
- Use
limitandoffsetfor large result sets - Default limit is 10, maximum is 100
- For batch operations, maximum limit is 50
Batch Operations
- Prefer batch operations for multiple records
- Built-in rate limiting prevents API throttling
- Error isolation ensures partial success handling
Field Selection
- Use
fieldsparameter to limit returned data - Reduces response size and improves performance
- Especially important for record details
Filtering
- Use
filtersfor precise searches instead of broad queries - Combine multiple conditions with
and/oroperators - Date range filtering is optimized for performance
Next Steps
- Migrating? โ See Migration Guide
- Need examples? โ Check User Guide
- Having issues? โ Visit Troubleshooting
- Want to extend? โ Review Developer Guide