Airtable Setup Guide: State Authority Subsystem
Airtable Setup Guide: State Authority Subsystem
Prerequisites
- Airtable Pro account (required for automations)
- Access to create bases in your workspace
Phase 1: Create Base and Tables (T001-T010)
T001: Create Base
- Go to airtable.com
- Click “Add a base” → “Start from scratch”
- Name it:
YFT-StateAuthority - Delete the default table
T002-T003: Create Lookup Tables
States Table
- Create new table:
States -
Add fields: | Field Name | Type | Options | |————|——|———| | state_name | Single line text | Primary field | | entity_type | Single select | User, Garment, Box, Cycle | | description | Long text | | | sort_order | Number | |
- Populate with data from
seed-data/states.csv
ErrorCodes Table
- Create new table:
ErrorCodes -
Add fields: | Field Name | Type | |————|——| | error_code | Single line text | Primary field | | error_name | Single line text | | error_message | Long text | | resolution_hint | Long text |
- Populate with data from
seed-data/error-codes.csv
T004: Create Users Table
- Create new table:
Users - Rename primary field to
user_id(Auto number) - Add fields in order:
| Field Name | Type | Configuration |
|---|---|---|
| display_name | Single line text | Required |
| operational_state | Single select | Active, Paused, HoldPayment, HoldIdentity, HoldLogistics, Closed |
| weekly_anchor | Single select | Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday |
| phone | Phone | |
| address_line1 | Single line text | |
| address_line2 | Single line text | |
| city | Single line text | |
| state | Single line text | |
| zip | Single line text | |
| payment_method_ref | Single line text | (reference only for pilot) |
| fit_profile_ref | Link to another record | (will link to FitProfiles later) |
| created_at | Created time | |
| updated_at | Last modified time |
T005: Create Garments Table
- Create new table:
Garments - Rename primary field to
garment_id(Auto number) - Add fields:
| Field Name | Type | Configuration |
|---|---|---|
| barcode | Single line text | Required, unique |
| sku | Single line text | Required |
| asset_state | Single select | Available, Reserved, Packed, InTransitOutbound, Delivered, InUse, InTransitReturn, ReceivedReturn, Quarantine, Refurbish, Repair, Lost, Retired, Disposed |
| condition_grade | Single select | A, B, C, D, F |
| size | Single line text | |
| color | Single line text | |
| category | Single select | Top, Bottom, Dress, Outerwear, Accessory |
| wear_count | Number | Default: 0 |
| wash_count | Number | Default: 0 |
| repair_count | Number | Default: 0 |
| max_wear_limit | Number | Default: 50 |
| current_cycle_id | Link to another record | Link to Cycles |
| current_box_id | Link to another record | Link to Boxes |
| notes | Long text | |
| created_at | Created time | |
| retired_at | Date |
- Add formula field
lifecycle_remaining:{max_wear_limit} - {wear_count} - Add formula field
over_limit:IF({wear_count} >= {max_wear_limit}, TRUE(), FALSE())
T006: Create Boxes Table
- Create new table:
Boxes - Rename primary field to
box_id(Auto number) - Add fields:
| Field Name | Type | Configuration |
|---|---|---|
| box_barcode | Single line text | Required, unique |
| container_state | Single select | Created, Planned, Picking, PackedVerified, Shipped, Delivered, ReturnInitiated, Returning, Received, Reconciled, Closed |
| cycle_id | Link to another record | Link to Cycles |
| planned_contents | Link to another record | Link to Garments (allow multiple) |
| actual_contents | Link to another record | Link to Garments (allow multiple) |
| tracking_outbound | Single line text | |
| tracking_return | Single line text | |
| created_at | Created time |
- Add formula field
has_variance:IF( AND( LEN(ARRAYJOIN({planned_contents})) > 0, LEN(ARRAYJOIN({actual_contents})) > 0 ), IF( ARRAYJOIN(ARRAYUNIQUE(ARRAYCOMPACT({planned_contents})), ",") != ARRAYJOIN(ARRAYUNIQUE(ARRAYCOMPACT({actual_contents})), ","), TRUE(), FALSE() ), FALSE() ) - Add formula field
garment_count:COUNTA({actual_contents})
T007: Create Cycles Table
- Create new table:
Cycles - Rename primary field to
cycle_id(Auto number) - Add fields:
| Field Name | Type | Configuration |
|---|---|---|
| user_id | Link to another record | Link to Users |
| week_id | Single line text | Required (format: YYYY-WNN) |
| cycle_state | Single select | Scheduled, Committed, FulfillmentInProgress, OutboundInTransit, Delivered, WearWindowOpen, ReturnWindowOpen, ReturnInTransit, CloseoutInspection, Settled, Closed |
| box_id | Link to another record | Link to Boxes |
| scheduled_at | Date | |
| committed_at | Date | |
| shipped_at | Date | |
| delivered_at | Date | |
| return_initiated_at | Date | |
| return_received_at | Date | |
| settled_at | Date | |
| closed_at | Date |
- Add formula field
user_week_key(for uniqueness check):CONCATENATE({user_id}, "-", {week_id})
T008: Create Events Table
- Create new table:
Events - Rename primary field to
event_id(Auto number) - Add fields:
| Field Name | Type | Configuration |
|---|---|---|
| entity_type | Single select | User, Garment, Box, Cycle |
| entity_id | Number | |
| from_state | Single line text | |
| to_state | Single line text | |
| transition_type | Single select | Normal, Compensating, Rejection |
| timestamp | Created time | |
| actor_id | Single line text | |
| cycle_id | Number | |
| metadata | Long text | (JSON format) |
| idempotency_key | Single line text | |
| error_code | Single line text |
T009: Create TransitionRules Table
- Create new table:
TransitionRules - Rename primary field to
rule_id(Auto number) - Add fields:
| Field Name | Type | Configuration |
|---|---|---|
| entity_type | Single select | User, Garment, Box, Cycle |
| from_state | Single line text | |
| to_state | Single line text | |
| preconditions | Long text | (JavaScript expression) |
| postconditions | Long text | (Actions to perform) |
| is_active | Checkbox | Default: checked |
| description | Long text |
- Populate with data from
seed-data/transition-rules.csv
T010: Configure Linked Record Relationships
Verify these links are configured:
- Garments.current_cycle_id → Cycles
- Garments.current_box_id → Boxes
- Boxes.cycle_id → Cycles
- Boxes.planned_contents → Garments (multiple)
- Boxes.actual_contents → Garments (multiple)
- Cycles.user_id → Users
- Cycles.box_id → Boxes
Phase 2: Create Automations (T015-T021)
Important: Automation Architecture
Due to Airtable automation limits, we use a simplified approach:
- Automations log events AFTER transitions (not validation before)
- Validation is done in Retool before triggering updates
- Events table serves as audit log
T015: ValidateGarmentTransition Automation
- Go to Automations tab
- Create new automation:
Log Garment Transition - Trigger: “When record updated” in Garments table
- Watch field:
asset_state
- Watch field:
- Action: “Create record” in Events table
- entity_type: “Garment”
- entity_id:
{garment_id}(from trigger) - from_state: (use previous value if available, otherwise “Unknown”)
- to_state:
{asset_state}(from trigger) - transition_type: “Normal”
- actor_id: “system”
See automations/log-garment-transition.js for the full script.
T016-T018: Similar automations for Cycle, User, Box
Create identical automations for each entity type, adjusting:
- Table name
- Primary key field name
- State field name
T019: LogTransitionEvent
This is handled by the individual entity automations above.
T020: EnforceLifecycleBounds
- Create automation:
Check Lifecycle Bounds - Trigger: “When record matches conditions” in Garments
- Condition:
asset_state = "ReceivedReturn"
- Condition:
- Action: Run script
// See automations/enforce-lifecycle-bounds.js
let garment = input.config();
let wearCount = garment.wear_count || 0;
let maxLimit = garment.max_wear_limit || 50;
if (wearCount >= maxLimit) {
// Update garment to Retired
output.set('should_retire', true);
} else {
output.set('should_retire', false);
}
- Add conditional action: If should_retire is true, update Garments record to
asset_state = "Retired"
T021: EnforceWeeklyUniqueness
- Create automation:
Check Cycle Uniqueness - Trigger: “When record created” in Cycles
- Action: Run script to check for duplicates
// See automations/enforce-weekly-uniqueness.js
let newCycle = input.config();
let userId = newCycle.user_id;
let weekId = newCycle.week_id;
// Query for existing cycles with same user_week_key
// If found, mark for deletion or rejection
Note: Airtable doesn’t support pre-create validation, so this automation flags duplicates after creation. The Retool UI should check BEFORE creating.
Verification Checklist
After completing Phase 1 and 2:
- All 9 tables created with correct fields
- Linked relationships working (can select related records)
- Formula fields calculating correctly
- Single select options match spec exactly
- Automations created and enabled
- Test: Create a garment, change state, verify Event created
Next Steps
After Airtable setup is complete:
- Import seed data from CSV files
- Set up Retool apps (see
../retool/SETUP-GUIDE.md) - Run validation scenarios from quickstart.md