Skip to main content

Overview

When embedding Flatfile in React applications, it’s important to understand the proper authentication flow for making API requests. The publishableKey is designed exclusively for initializing the FlatfileProvider component, while the accessToken should be used for all subsequent API requests.

Security Best Practice

The publishableKey is restricted to its intended use: initializing the Flatfile embedded experience. For security reasons, it cannot be used to make API requests directly. Instead, use the accessToken that becomes available after the Space is created.

Accessing the Access Token

Use the useFlatfileInternal hook to access the accessToken and sessionSpace after initialization:
import { useFlatfileInternal } from "@flatfile/react";

const MyComponent = () => {
  const { accessToken, sessionSpace } = useFlatfileInternal();
  
  // accessToken is available after the space is created
  console.log("Access Token:", accessToken);
  console.log("Session Space:", sessionSpace);
  
  return <div>...</div>;
};
The accessToken will be undefined until the space is actually created (after openPortal() is called and the space is initialized). Always check for its availability before making API calls.

Making API Requests

Basic Pattern

Here’s the recommended pattern for making API requests using the accessToken:
import { useEffect, useState } from "react";
import { useFlatfileInternal } from "@flatfile/react";
import { FlatfileClient } from "@flatfile/api";

const MyComponent = () => {
  const { accessToken, sessionSpace } = useFlatfileInternal();
  const [data, setData] = useState([]);

  useEffect(() => {
    // Wait for the space to be created and token to be available
    if (!accessToken || !sessionSpace?.id) return;

    // Initialize the API client with the accessToken
    const api = new FlatfileClient({ token: accessToken });

    const fetchData = async () => {
      try {
        // Make API requests using the authenticated client
        const workbooks = await api.workbooks.list({ 
          spaceId: sessionSpace.id 
        });
        setData(workbooks.data);
      } catch (error) {
        console.error("API Error:", error);
      }
    };

    fetchData();
  }, [accessToken, sessionSpace?.id]);

  return <div>{data.length} workbooks found</div>;
};

Complete Example

Here’s a complete example showing how to set up authentication and make API requests:
import { useState, useEffect } from "react";
import { 
  FlatfileProvider, 
  useFlatfile, 
  useFlatfileInternal,
  Space,
  Workbook 
} from "@flatfile/react";
import { FlatfileClient } from "@flatfile/api";

// Main App component
export const App = ({ publishableKey }) => (
  <FlatfileProvider publishableKey={publishableKey}>
    <SpaceWithApiAccess />
  </FlatfileProvider>
);

// Component that accesses the API
const SpaceWithApiAccess = () => {
  const { openPortal } = useFlatfile();
  const { accessToken, sessionSpace } = useFlatfileInternal();
  const [records, setRecords] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!accessToken || !sessionSpace?.id) return;

    // Initialize the API client with the accessToken
    const api = new FlatfileClient({ token: accessToken });

    const fetchData = async () => {
      setLoading(true);
      try {
        // Example: Get all workbooks in the space
        const workbooks = await api.workbooks.list({ 
          spaceId: sessionSpace.id 
        });
        console.log("Workbooks:", workbooks.data);

        // Example: Get sheets from the first workbook
        if (workbooks.data?.[0]?.id) {
          const sheets = await api.sheets.list({ 
            workbookId: workbooks.data[0].id 
          });
          
          // Example: Get records from the first sheet
          if (sheets.data?.[0]?.id) {
            const sheetRecords = await api.records.get(sheets.data[0].id);
            setRecords(sheetRecords.data?.records || []);
          }
        }

        // Example: Get space details
        const space = await api.spaces.get(sessionSpace.id);
        console.log("Space details:", space.data);
      } catch (error) {
        console.error("API Error:", error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [accessToken, sessionSpace?.id]);

  return (
    <div>
      <button onClick={() => openPortal()}>
        Open Flatfile
      </button>
      
      {loading && <p>Loading...</p>}
      
      {records.length > 0 && (
        <div>
          <h2>Records</h2>
          <p>Fetched {records.length} records from the space</p>
        </div>
      )}
      
      <Space config={{ name: "My Space" }}>
        <Workbook 
          config={{ 
            name: "My Workbook", 
            sheets: [
              {
                name: "Contacts",
                slug: "contacts",
                fields: [
                  { key: "name", type: "string", label: "Name" },
                  { key: "email", type: "string", label: "Email" },
                ]
              }
            ]
          }} 
        />
      </Space>
    </div>
  );
};

Common API Operations

Once you have the accessToken, you can perform various operations:
const api = new FlatfileClient({ token: accessToken });

// Spaces
await api.spaces.get(spaceId);
await api.spaces.update(spaceId, { name: "New Name" });

// Workbooks
await api.workbooks.list({ spaceId });
await api.workbooks.get(workbookId);

// Sheets
await api.sheets.list({ workbookId });
await api.sheets.get(sheetId);

// Records
await api.records.get(sheetId);
await api.records.insert(sheetId, [
  { firstName: "John", lastName: "Doe" }
]);
await api.records.update(sheetId, { records: [...] });
await api.records.delete(sheetId, { ids: ["rec_123"] });

// Files
await api.files.list({ spaceId });
await api.files.get(fileId);

// Jobs
await api.jobs.list({ spaceId });
await api.jobs.get(jobId);

// Documents
await api.documents.list(spaceId);
await api.documents.create(spaceId, { 
  title: "Guide", 
  body: "# Welcome" 
});

Handling User Actions

For API calls triggered by user actions, ensure the token is available:
const MyComponent = () => {
  const { accessToken, sessionSpace } = useFlatfileInternal();
  
  const handleFetchRecords = async () => {
    if (!accessToken) {
      console.error("Space not initialized yet");
      return;
    }
    
    const api = new FlatfileClient({ token: accessToken });
    const records = await api.records.get(sheetId);
    console.log(records);
  };
  
  return (
    <button onClick={handleFetchRecords}>
      Fetch Records
    </button>
  );
};

Creating a Custom Hook

For cleaner code, create a custom hook for API access:
import { useMemo } from "react";
import { useFlatfileInternal } from "@flatfile/react";
import { FlatfileClient } from "@flatfile/api";

const useFlatfileAPI = () => {
  const { accessToken, sessionSpace } = useFlatfileInternal();
  
  const api = useMemo(() => {
    if (!accessToken) return null;
    return new FlatfileClient({ token: accessToken });
  }, [accessToken]);
  
  return { api, sessionSpace, isReady: !!api };
};

// Usage
const MyComponent = () => {
  const { api, sessionSpace, isReady } = useFlatfileAPI();
  
  useEffect(() => {
    if (!isReady) return;
    
    const fetchData = async () => {
      const workbooks = await api.workbooks.list({ 
        spaceId: sessionSpace.id 
      });
      console.log(workbooks);
    };
    
    fetchData();
  }, [isReady, api, sessionSpace]);
  
  return <div>...</div>;
};

Troubleshooting

”accessToken is undefined”

Cause: The space hasn’t been created yet. Solution: Add null checks and wait for the token:
useEffect(() => {
  if (!accessToken) {
    console.log("Waiting for space to be created...");
    return;
  }
  
  // Token is now available
}, [accessToken]);

“Cannot read property ‘id’ of undefined”

Cause: Trying to access sessionSpace.id before the space is created. Solution: Check both accessToken and sessionSpace:
if (!accessToken || !sessionSpace?.id) return;

Key Differences

HookExposes
useFlatfileopenPortal, closePortal, open, setListener, listener
useFlatfileInternalAll of the above PLUS accessToken, setAccessToken, sessionSpace, setSessionSpace

Summary

Three key steps for secure API requests:
  1. Use publishableKey only for FlatfileProvider initialization
  2. Access accessToken via useFlatfileInternal hook
  3. Initialize FlatfileClient with the accessToken for all API requests
This pattern ensures your API calls are properly authenticated and scoped to the correct space.

Next Steps