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

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