| name | smartsuite-patterns |
| description | SmartSuite API patterns and field format validation. Covers choices vs options distinction (UUID corruption prevention), dry_run safety patterns, field format validation, and MCP integration best practices. Critical for preventing UUID corruption bugs. |
| allowed-tools | Read |
SmartSuite Patterns Skill
Purpose
Provides SmartSuite API patterns, field format requirements, and UUID corruption prevention strategies. Critical for correct SmartSuite integration and data integrity.
When to Use This Skill
Auto-activates when:
- Working with SmartSuite API
- Creating or updating SmartSuite fields
- "choices vs options" queries
- UUID corruption prevention
- SmartSuite field format validation
- SmartSuite MCP integration
⚠️ CRITICAL: UUID Corruption Prevention
THE GOLDEN RULE: Use choices, NEVER options
WRONG (destroys all data):
{
"params": {
"options": [
{"value": "draft"},
{"value": "active"}
]
}
}
CORRECT (preserves UUIDs):
{
"params": {
"choices": [
{"value": "draft", "id": "existing-uuid-1"},
{"value": "active", "id": "existing-uuid-2"}
]
}
}
Why This Matters: Using options instead of choices permanently destroys all data in that field across all records. This is IRREVERSIBLE.
Authentication Pattern
{
"Authorization": "Token ${SMARTSUITE_API_TOKEN}",
"ACCOUNT-ID": "s3qnmox1",
"Content-Type": "application/json"
}
CRITICAL: Use "Token" not "Bearer" for Authorization header
Rate Limiting
- Max: 4-5 requests/second
- Recommended delay: 250ms between requests
- 429 backoff: 30 seconds with exponential backoff
- Retry strategy: Max 3 retries for 5xx errors
Performance Benchmarks (Production Tested)
- Query Operations: 400-600ms consistently
- CRUD Operations: 400-700ms including validation
- Bulk Operations: 600-850ms for multiple records
- Schema Discovery: 300-500ms (standard), 450-850ms (intelligent API)
Core CRUD Operations
List Records (POST, not GET)
POST /api/v1/applications/{appId}/records/list/
{
"limit": 2,
"offset": 0,
"filter": {
"operator": "and",
"fields": [
{"field": "status", "comparison": "is", "value": "active"}
]
}
}
Get Single Record
GET /api/v1/applications/{appId}/records/{recordId}/
CRITICAL: Trailing slash required
Create Record
POST /api/v1/applications/{appId}/records/
{
"title": "Record Title",
"projects_link": ["68a8ff5237fde0bf797c05b3"],
"status": "draft"
}
Update Record
PATCH /api/v1/applications/{appId}/records/{recordId}/
{
"status": "active",
"assigned_to": ["user_id_1", "user_id_2"]
}
Delete Record
DELETE /api/v1/applications/{appId}/records/{recordId}/
CRITICAL: Trailing slash required or silent failure
Bulk Operations (Max 25 records)
Bulk Create
POST /api/v1/applications/{appId}/records/bulk/
{
"items": [
{"title": "Record 1", "status": "draft"},
{"title": "Record 2", "status": "active"}
]
}
Performance: 600-850ms for multiple records
Bulk Update
PATCH /api/v1/applications/{appId}/records/bulk/
{
"items": [
{"id": "record_id_1", "status": "completed"},
{"id": "record_id_2", "priority": "high"}
]
}
Critical Field Format Rules
1. Linked Record Fields
- MUST be arrays, even for single values
- Wrong:
"project_id": "68a8ff5237fde0bf797c05b3"
- Correct:
"project_id": ["68a8ff5237fde0bf797c05b3"]
- Filter operators: Use
has_any_of, has_all_of, has_none_of - NEVER is or equals
2. Rich Text Fields (SmartDoc Structure)
Populated Rich Text Field:
{
"projectBrief": {
"data": {
"type": "doc",
"content": [
{
"type": "paragraph",
"attrs": {"textAlign": "left", "size": "medium"},
"content": [
{ "type": "text", "text": "Your content here..." }
]
}
]
},
"html": "<div class=\"rendered\"><p>Your content here...</p></div>",
"preview": "Your content here..."
}
}
Empty Rich Text Field:
{
"description": {
"data": {"type": "doc", "content": []},
"html": "",
"preview": ""
}
}
3. Select/Status Fields
- Use option codes not display labels
- Wrong:
"status": "In Progress"
- Correct:
"status": "in_progress"
4. Checklist Fields
Must use full SmartDoc structure with items array:
{
"checklist99": {
"items": [
{
"id": "item-id",
"content": { },
"completed": false,
"assignee": null,
"due_date": null
}
]
}
}
5. Date Range Fields
{
"date_range": {
"from_date": {"date": "2025-01-15T00:00:00Z", "include_time": false},
"to_date": {"date": "2025-01-20T00:00:00Z", "include_time": false}
}
}
6. Formula/Rollup Fields
- READ ONLY - cannot be set via API
- Values are calculated by SmartSuite
- Attempting to set will cause validation errors
Field Discovery Process
Step 1: Use Schema Discovery
const schema = await smartsuiteSchema(tableId, 'summary');
Step 2: Get Field Details
const fieldDetail = await smartsuiteSchema(tableId, 'field_detail', fieldSlug);
Step 3: Validate Before Write
DRY-RUN Safety Pattern
ALWAYS default to dry_run: true for mutations:
smartsuiteFieldCreate(tableId, fieldConfig, dryRun: true)
smartsuiteFieldUpdate(tableId, fieldId, updates, dryRun: true)
smartsuiteRecord('update', tableId, recordId, data, dryRun: true)
Only set dryRun: false after verifying operation is correct
Common Patterns
Pattern 1: Safe Field Update (Preserving UUIDs)
const fieldDetail = await smartsuiteSchema(tableId, 'field_detail', 'status');
const existingChoices = fieldDetail.params.choices;
const updatedChoices = [
...existingChoices,
{"value": "archived", "id": generateNewUUID()}
];
await smartsuiteFieldUpdate(
tableId,
fieldId,
{ params: { choices: updatedChoices } },
dryRun: true
);
await smartsuiteFieldUpdate(
tableId,
fieldId,
{ params: { choices: updatedChoices } },
dryRun: false
);
Pattern 2: Creating Linked Record
const wrong = {
project_id: "68a8ff5237fde0bf797c05b3"
};
const correct = {
project_id: ["68a8ff5237fde0bf797c05b3"]
};
await smartsuiteRecord('create', tableId, null, correct);
Pattern 3: Filtering Linked Records
const wrongFilter = {
field: "project_id",
comparison: "is",
value: "project-uuid"
};
const correctFilter = {
field: "project_id",
comparison: "has_any_of",
value: ["project-uuid"]
};
await smartsuiteQuery('search', tableId, {
filter: {
operator: "and",
fields: [correctFilter]
}
});
Anti-Patterns to Avoid
❌ Using options instead of choices for select field updates
✅ Always use choices with preserved UUIDs
❌ Single values for linked record fields
✅ Always use arrays, even for single links
❌ Display labels for select field values
✅ Use option codes (e.g., "in_progress" not "In Progress")
❌ Skipping dry-run for mutations
✅ Always dry-run first, then execute
❌ Using is operator for linked record filters
✅ Use has_any_of, has_all_of, or has_none_of
❌ Attempting to set formula/rollup field values
✅ These are read-only, calculated by SmartSuite
❌ Forgetting trailing slashes in DELETE/GET requests
✅ Always include trailing slash or expect silent failures
Key Takeaways
- UUID preservation: Use
choices not options - this is CRITICAL
- Linked records: Always arrays, even for single values
- DRY-RUN first: Default to dry_run=true for all mutations
- Field discovery: Use schema tools before writing
- Select field codes: Use codes not display labels
- Filter operators:
has_any_of for linked records, not is
- Trailing slashes: Required for GET/DELETE or silent failure
- Formula fields: Read-only, don't attempt to set
- Rate limiting: 250ms delay between requests recommended
- SmartDoc structure: Rich text requires full document structure