Skip to main content
Add “Download My Data” to your app in 10 minutes.

1. Install the SDK

npm install @dataferry/sdk

2. Get your API key

Go to DataFerry Dashboard → Settings → API Keys → Create Key. Add it to your environment:
DATAFERRY_API_KEY=sk_live_...

3. Create a portal session

When a user clicks “Download My Data” in your app, create a session and redirect them:
import { Ferry } from '@dataferry/sdk';

const ferry = new Ferry({
  apiKey: process.env.DATAFERRY_API_KEY,
});

// In your route handler (e.g., Express, Next.js API route)
app.post('/export-my-data', async (req, res) => {
  const session = await ferry.createPortalSession({
    scopeId: req.user.id, // the user, organization, or account to export data for
    returnUrl: 'https://yourapp.com/settings',
  });

  res.redirect(session.url);
});
That’s it. Your user sees a branded portal where they can download their data.

What happens next

  1. User lands on the portal
  2. They see what data is available (based on your schema config)
  3. They click “Download my data”
  4. Export runs in the background
  5. They get a ZIP file with clean, labeled CSVs

Configuration (done once in the dashboard)

Before the SDK works, you need to configure DataFerry in the dashboard:
  1. Add a connection — Your database credentials (read-only)
  2. Scan schema — We detect your tables and relationships
  3. Confirm schema — Set labels, exclude sensitive columns
  4. Set tenancy mode — Single-tenant or multi-tenant
Once configured, the SDK just works.

Single-tenant vs multi-tenant

Depending on your architecture, you will provide either scopeId or connectionId — not both. Multi-tenant (most SaaS apps): Users share a database, filtered by user_id or organization_id.
await ferry.createPortalSession({
  scopeId: req.user.id, // the user, organization, or account to export data for
});
Single-tenant: Each customer has their own database connection.
await ferry.createPortalSession({
  connectionId: 'conn_abc123',  // Optional — uses primary connection if omitted
  // No scopeId needed — all data belongs to this customer
});

Full example (Express)

import express from 'express';
import { Ferry } from '@dataferry/sdk';

const app = express();
const ferry = new Ferry({ apiKey: process.env.DATAFERRY_API_KEY });

// Assumes req.user is already authenticated
app.post('/api/export-my-data', async (req, res) => {
  try {
    const session = await ferry.createPortalSession({
      scopeId: req.user.id,
      returnUrl: `${process.env.APP_URL}/settings?export=complete`,
    });

    res.redirect(session.url);
  } catch (error) {
    console.error('Export failed:', error);
    res.status(500).json({ error: 'Failed to start export' });
  }
});

app.listen(3000);

Full example (Next.js App Router)

// app/api/export/route.ts
import { redirect } from 'next/navigation';
import { Ferry } from '@dataferry/sdk';
import { auth } from '@/lib/auth';

const ferry = new Ferry({ apiKey: process.env.DATAFERRY_API_KEY! });

export async function POST() {
  const session = await auth();

  if (!session?.user?.id) {
    return new Response('Unauthorized', { status: 401 });
  }

  const portalSession = await ferry.createPortalSession({
    scopeId: session.user.id,
    returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/settings`,
  });

  redirect(portalSession.url);
}
// app/settings/page.tsx
export default function SettingsPage() {
  return (
    <form action="/api/export" method="POST">
      <button type="submit">
        Download my data
      </button>
    </form>
  );
}

Next steps