Flatfile Spaces are micro-applications, each having their own database, filestore, and auth. Use Spaces to integrate Flatfile into your data exchange workflow, whether that happens directly in your application or as part of an offline process.
You can think of a Space as a home for any one of your Customers’ data, or as place for a discrete data migration session; you can create a new Space any time you need an isolated place to migrate new data.
Anatomy
A Space is comprised of Workbooks, Files, Users, Documents, Themes and Metadata:
- Documents - Custom pages mounted on the Space, displayed in sidebar (Build guide)
- Files - Uploaded directly to a Space for processing
- Users - Admins and configurable Guest Users (temporary or named)
- Metadata - Raw data that you can use for configuration and customizations. Learn more about utilizing metadata effectively.
- Themes - Space-level customization nested in Metadata for branding and appearance
- Workbooks - Blueprint-defined databases containing structured data
- Sheets - Blueprint-defined individual data tables
Creating Spaces
Basic Space Creation
Create a simple Space with Workbook configuration:
import { FlatfileApi } from "@flatfile/api";
const api = new FlatfileApi({
token: process.env.FLATFILE_TOKEN,
});
const space = await api.spaces.create({
name: "Customer Data Import",
workbook: {
name: "Customer Onboarding",
sheets: [customerSchema],
},
});
console.log(`Space created: ${space.id}`);
Advanced Space Configuration
Configure Space with custom settings:
const space = await api.spaces.create({
name: "Product Catalog Import",
description: "Import and validate product catalog data",
// Access control
guestAccess: true,
allowedDomains: ["@yourcompany.com"],
// Blueprint definition
workbook: {
name: "Product Catalog",
sheets: [productsSchema, categoriesSchema],
actions: [
{
operation: "validate-skus",
label: "Validate SKUs",
description: "Check SKU uniqueness and format",
},
],
},
// UI customization
theme: {
primaryColor: "#635BFF",
logo: "https://yoursite.com/logo.png",
},
// Metadata for tracking
metadata: {
project: "Q1-2024-catalog-update",
department: "marketing",
priority: "high",
},
});
Space Types and Patterns
User-Specific Spaces
Create dedicated Spaces for individual users:
const createUserSpace = async (userId, userEmail) => {
return await api.spaces.create({
name: `${userEmail}'s Import`,
guestAccess: false,
users: [
{
email: userEmail,
role: "owner",
},
],
workbook: userWorkbookConfig,
metadata: {
userId,
type: "user-specific",
},
});
};
Project-Based Spaces
Organize Spaces around specific projects:
const createProjectSpace = async (projectId, projectName) => {
return await api.spaces.create({
name: `${projectName} - Data Import`,
description: `Import workspace for ${projectName}`,
workbook: projectWorkbookConfig,
metadata: {
projectId,
type: "project-workspace",
createdAt: new Date().toISOString(),
},
});
};
Template Spaces
Create reusable Space templates:
const spaceTemplates = {
"customer-import": {
name: "Customer Data Import",
workbook: customerWorkbookConfig,
theme: customerTheme,
},
"product-import": {
name: "Product Catalog Import",
workbook: productWorkbookConfig,
theme: productTheme,
},
};
const createFromTemplate = (templateName, customizations = {}) => {
const template = spaceTemplates[templateName];
return api.spaces.create({
...template,
...customizations,
});
};
Space Lifecycle Management
Space States
Spaces progress through different states:
// Track space lifecycle
listener.on("space:created", async (event) => {
const { spaceId } = event.context;
console.log(`Space ${spaceId} created`);
// Initialize space-specific settings
await initializeSpace(spaceId);
});
Learn more about configuring [data retention policies](/guides/data-retention) for your spaces.
listener.on("space:configured", async (event) => {
const { spaceId } = event.context;
console.log(`Space ${spaceId} configured and ready`);
});
listener.on("space:completed", async (event) => {
const { spaceId } = event.context;
console.log(`Space ${spaceId} import completed`);
// Archive or cleanup space
await archiveSpace(spaceId);
});
Automatic Space Cleanup
Clean up completed Spaces automatically:
const cleanupOldSpaces = async () => {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 30); // 30 days ago
const oldSpaces = await api.spaces.list({
status: "completed",
updatedBefore: cutoffDate.toISOString(),
});
for (const space of oldSpaces) {
// Export final data before deletion
await exportSpaceData(space.id);
// Delete the space
await api.spaces.delete(space.id);
console.log(`Cleaned up space: ${space.name}`);
}
};
// Run cleanup weekly
setInterval(cleanupOldSpaces, 7 * 24 * 60 * 60 * 1000);
Access Control and Permissions
User Roles
Control what users can do within a Space:
const spaceWithRoles = await api.spaces.create({
name: "Collaborative Import",
users: [
{
email: "admin@company.com",
role: "admin", // Full control
},
{
email: "editor@company.com",
role: "editor", // Can edit data
},
{
email: "viewer@company.com",
role: "viewer", // Read-only access
},
],
});
Guest Access
Allow external users to participate:
const guestSpace = await api.spaces.create({
name: "Customer Data Submission",
guestAccess: true, // Allow guest users
allowedDomains: ["@customer.com"], // Restrict by domain
guestRole: "editor", // What guests can do
// Guest-specific settings
guestSettings: {
requireEmail: true,
allowDownload: false,
showProgressBar: true,
},
});
For advanced guest UI customization, see Customize Guest Sidebar.
### Dynamic Access Control
Change permissions based on context:
```javascript
listener.on("space:accessed", async (event) => {
const { spaceId, userId, userEmail } = event.context;
// Check if user should have access
const hasAccess = await checkUserAccess(userId, spaceId);
if (!hasAccess) {
throw new Error("Access denied");
}
// Adjust permissions based on user type
if (userEmail.endsWith("@partner.com")) {
await api.spaces.updateUser(spaceId, userId, {
role: "viewer", // Partners get read-only access
});
}
});
Space Customization
Branding and Theming
For complete theming capabilities, see our comprehensive Theme Your Space guide.
Basic theming example:
const brandedSpace = await api.spaces.create({
name: "Partner Data Import",
theme: {
primaryColor: "#635BFF",
secondaryColor: "#8B85FF",
logo: "https://yoursite.com/logo.png",
favicon: "https://yoursite.com/favicon.ico",
// Custom CSS
customStyles: `
.header { background: linear-gradient(135deg, #635BFF, #8B85FF); }
.upload-area { border: 2px dashed #635BFF; }
`,
},
// Custom messaging
messages: {
welcome: "Welcome to our partner data import portal",
uploadInstructions: "Please upload your product catalog in CSV format",
completionMessage: "Thank you! Your data has been successfully imported.",
},
});
Custom Workflows
Define Space-specific workflows:
const workflowSpace = await api.spaces.create({
name: "Multi-Step Import",
workflow: {
steps: [
{
id: "upload",
name: "Upload Data",
description: "Upload your CSV or Excel file",
},
{
id: "validate",
name: "Validate Data",
description: "Review and fix any data issues",
},
{
id: "enrich",
name: "Enrich Data",
description: "Add additional information",
},
{
id: "submit",
name: "Submit",
description: "Complete the import process",
},
],
},
});
Space Monitoring and Analytics
Event Tracking
Monitor Space activity:
listener.on("space:*", async (event) => {
const { spaceId, eventType } = event.context;
// Track space analytics
await analytics.track(eventType, {
spaceId,
timestamp: new Date(),
userId: event.context.userId,
metadata: event.context.metadata,
});
});
// Specific event handlers
listener.on("space:file:uploaded", async (event) => {
const { spaceId, fileName, fileSize } = event.context;
console.log(
`File uploaded to space ${spaceId}: ${fileName} (${fileSize} bytes)`
);
});
listener.on("space:records:validated", async (event) => {
const { spaceId, recordCount, errorCount } = event.context;
console.log(
`Validation complete in space ${spaceId}: ${recordCount} records, ${errorCount} errors`
);
});
Space Metrics
Generate Space usage reports:
const getSpaceMetrics = async (spaceId) => {
const space = await api.spaces.get(spaceId);
const events = await api.events.list({ spaceId });
return {
name: space.name,
createdAt: space.createdAt,
status: space.status,
// Activity metrics
totalEvents: events.length,
userCount: space.users.length,
fileCount: space.files.length,
// Data metrics
recordsProcessed: events
.filter((e) => e.type === "records:created")
.reduce((sum, e) => sum + e.context.recordCount, 0),
// Time metrics
timeToComplete: space.completedAt
? new Date(space.completedAt) - new Date(space.createdAt)
: null,
};
};
Space Integration Patterns
Multi-Space Orchestration
Coordinate multiple Spaces for complex workflows:
const orchestrateBulkImport = async (datasets) => {
const spaces = [];
// Create a Space for each dataset
for (const dataset of datasets) {
const space = await api.spaces.create({
name: `Import - ${dataset.name}`,
workbook: dataset.workbookConfig,
metadata: {
batchId: generateBatchId(),
dataset: dataset.name,
},
});
spaces.push(space);
}
// Wait for all Spaces to complete
const results = await Promise.all(
spaces.map((space) => waitForSpaceCompletion(space.id))
);
// Consolidate results
return consolidateImportResults(results);
};
Space Templating System
Build a system for creating standardized Spaces:
class SpaceTemplateManager {
constructor(api) {
this.api = api;
this.templates = new Map();
}
registerTemplate(name, config) {
this.templates.set(name, config);
}
async createFromTemplate(templateName, overrides = {}) {
const template = this.templates.get(templateName);
if (!template) {
throw new Error(`Template ${templateName} not found`);
}
const spaceConfig = {
...template,
...overrides,
metadata: {
...template.metadata,
...overrides.metadata,
template: templateName,
createdAt: new Date().toISOString(),
},
};
return await this.api.spaces.create(spaceConfig);
}
}
// Usage
const templateManager = new SpaceTemplateManager(api);
templateManager.registerTemplate("customer-onboarding", {
workbook: customerWorkbookConfig,
theme: customerTheme,
workflow: customerWorkflow,
});
const space = await templateManager.createFromTemplate("customer-onboarding", {
name: "Acme Corp - Customer Import",
});
Best Practices
Space Naming
Use descriptive, consistent naming:
// Good naming patterns
"Customer Import - Q1 2024";
"Acme Corp - Product Catalog";
"User Import - john@example.com";
// Avoid generic names
"Import";
"Data";
"Untitled";
Resource Management
Monitor and clean up Space resources:
// Monitor space usage
const monitorSpaceUsage = async () => {
const spaces = await api.spaces.list();
for (const space of spaces) {
const usage = await getSpaceUsage(space.id);
if (usage.storageUsed > STORAGE_LIMIT) {
await notifyStorageLimit(space);
}
if (usage.ageInDays > CLEANUP_THRESHOLD) {
await scheduleSpaceCleanup(space);
}
}
};
Error Handling
Handle Space errors gracefully:
const createSpaceWithRetry = async (config, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await api.spaces.create(config);
} catch (error) {
console.warn(`Space creation attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
throw new Error(`Failed to create space after ${maxRetries} attempts`);
}
// Wait before retry
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
}
}
};
- Workbooks - Data containers within Spaces
- Listeners - Event handlers for Space activities
- Blueprints - Define structure for Space data
- Records - Individual data entries within Spaces
- Jobs - Background processing tasks
- Environments - Containers for organizing Spaces
Responses are generated using AI and may contain mistakes.