Import Tasks
Configurable listing import tasks with categories for filtering by source
Import Tasks
Import tasks define what listings to sync from the MLS. Each task has a category that determines how listings are filtered and displayed.
Overview
Import tasks replace the original single-sync model with flexible, categorized imports:
- Agent's personal listings from their MLS ID
- Office/brokerage listings from office MLS ID
- Geographic search imports from service areas
- Custom queries for specific criteria
Task Categories
| Category | Purpose | Typical Filter |
|---|---|---|
personal | Agent's own listings | ListAgentMlsId |
office | Brokerage listings | ListOfficeKey |
search | Service area imports | City, subdivision, geo |
custom | Ad-hoc queries | Any OData filter |
Database Schema
import_tasks Table
CREATE TABLE import_tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id UUID NOT NULL REFERENCES workspaces(id),
mls_connection_id UUID NOT NULL REFERENCES mls_connections(id),
-- Task identity
name TEXT NOT NULL,
description TEXT,
category TEXT DEFAULT 'personal', -- personal, office, search, custom
-- MLS filtering
filter_config JSONB DEFAULT '{}',
status_filters TEXT[] DEFAULT ARRAY['Active'],
price_min INTEGER,
price_max INTEGER,
max_import_limit INTEGER,
-- Sync configuration
field_mapping_id UUID REFERENCES field_mappings(id),
sync_cadence TEXT DEFAULT 'manual', -- manual, hourly, daily
is_enabled BOOLEAN DEFAULT true,
is_agent_visible BOOLEAN DEFAULT true,
-- Sync state
last_synced_at TIMESTAMPTZ,
last_sync_cursor TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);Filter Config Structure
The filter_config JSONB field varies by category:
Personal listings:
{
"agentMlsId": "12345"
}Office listings:
{
"officeMlsId": "BROKER123"
}Search (service area):
{
"serviceAreaId": "abc-123",
"cities": ["miami"],
"subdivisions": ["coconut-grove"]
}Custom:
{
"customFilter": "City eq 'Miami' and ListPrice gt 1000000"
}Listing Source Tracking
import_task_id in Listings
Each synced listing stores its import task reference:
ALTER TABLE listings
ADD COLUMN import_task_id UUID REFERENCES import_tasks(id);import_category in Typesense
Listings are indexed with their category for filtering:
{
"id": "listing-uuid",
"import_category": "personal",
...
}Listing Source Filtering
The client site can filter listings by source:
| Source | Typesense Filter | Use Case |
|---|---|---|
agent | import_category:=[personal,office] | "My Listings" page |
all | No filter | Browse all available listings |
Search Function
interface SearchFilters {
listingSource?: 'agent' | 'all';
// ... other filters
}
function buildFilters(filters: SearchFilters): string {
const parts = [];
if (filters.listingSource === 'agent') {
parts.push(`import_category:=[personal,office]`);
}
// 'all' includes everything
return parts.join(' && ');
}API Endpoints
Dashboard API (Agent)
GET /api/dashboard/imports
List import tasks visible to the agent.
Response:
{
"imports": [
{
"id": "task-uuid",
"name": "My Listings",
"description": "Personal listings from MLS",
"category": "personal",
"filter_config": { "agentMlsId": "12345" },
"status_filters": ["Active", "Pending"],
"sync_cadence": "daily",
"is_enabled": true,
"last_synced_at": "2024-01-15T10:30:00Z",
"listing_count": 12,
"latest_sync_run": {
"id": "run-uuid",
"status": "success",
"started_at": "2024-01-15T10:30:00Z",
"stats": { "fetched": 15, "upserted": 12 }
}
}
]
}POST /api/dashboard/imports/[id]/sync
Trigger a manual sync for an import task.
Admin API
GET /api/admin/workspaces/[id]/imports
List all import tasks for a workspace.
POST /api/admin/workspaces/[id]/imports
Create a new import task.
Request body:
{
"name": "Miami Beach Listings",
"category": "search",
"filter_config": {
"cities": ["miami-beach"]
},
"status_filters": ["Active"],
"max_import_limit": 500,
"sync_cadence": "daily"
}PATCH /api/admin/workspaces/[id]/imports/[taskId]
Update an import task.
POST /api/admin/workspaces/[id]/imports/[taskId]/toggle
Enable/disable an import task.
POST /api/admin/workspaces/[id]/imports/[taskId]/run
Trigger a sync run.
Sync Runs
Each sync execution is logged:
sync_runs Table
CREATE TABLE sync_runs (
id UUID PRIMARY KEY,
workspace_id UUID NOT NULL REFERENCES workspaces(id),
mls_connection_id UUID REFERENCES mls_connections(id),
import_task_id UUID REFERENCES import_tasks(id),
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
finished_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'running', -- running, success, error
stats JSONB, -- { fetched, upserted, indexed, errors }
error TEXT
);Stats Structure
{
"fetched": 150,
"upserted": 148,
"indexed": 148,
"errors": 2,
"duration_ms": 45000
}Creating from Service Areas
Import tasks can be auto-generated from service areas:
POST /api/dashboard/imports/from-service-areas
{
"serviceAreaIds": ["area-1", "area-2"]
}This creates tasks with:
category: 'search'name: 'Service Area: {area.name}'- Filter config from the area definition
is_agent_visible: true
Default Task Setup
When onboarding a new workspace, create default tasks:
-
Personal Listings (
category: 'personal')- Filter by agent's MLS ID
- High priority, frequent sync
-
Office Listings (
category: 'office')- Filter by brokerage MLS ID
- Medium priority
-
Service Area Imports (
category: 'search')- Created from defined service areas
- Bulk listing import
Best Practices
- Start with personal listings — Agent's own listings should always be up-to-date
- Use max_import_limit — Prevent runaway imports for broad searches
- Set appropriate cadences — Personal: hourly, Search: daily
- Monitor sync runs — Check for errors and failed imports
- Use status_filters wisely — Usually just "Active" for public display