ReboFilesDocs
Start3
What is ReboFiles
Getting started
Demos to run
Put it in your app6
React component
Headless: your own UI
Without a framework
Styling and themes
Keyboard and accessibility
Languages
Files9
Uploads
Previews and the viewer
Search
Live changes and large folders
Comments
Starred and fields
Versions, trash and space
Download as .zip
Word documents
The server14
The route
Frameworks and sign-in
Who may do what
Storage: S3, GCS, Azure and disk
Checking uploads
Metadata: SQLite and Postgres
Database schema
Sharing and links
Activity and notifications
Hooks, logs and limits
Retention, holds and export
Desktop and other origins
Sync a folder
Going to production
Together1
AI assistants (MCP)
Reference5
Packages
Components and hooks
Client API
HTTP routes
Status and roadmap
↑↓ to move↵ to openEsc to close38 pages

The server Sharing and links

Sharing and links

Share, in the details of an entry, gives it to someone by the id your identify gives them, or to everyone signed in; what is in a folder goes with it.
People

Roles

Viewer, commenter or editor, on an entry and what is inside it. It shows in their Shared with me, not in their My files, which is theirs alone; beside it, how many are new since they last looked. For one drive of a whole team instead, give the server top: 'everyone'.

Links

Anyone who has one may look and download, without signing in, for a day, a week or a month, or until it is removed.

  • With a password: asked for before anything is shown, sent as x-link-password at every request, kept only as scrypt makes it, a moment late after each wrong one.
  • For one download: taken by downloading a file or an archive of it, not by looking; it still works a quarter of an hour after, for the files of that download.
// The page a link goes to: app/shared/page.tsx
'use client';
import { SharedLink } from '@rebofiles/react';
import { useSearchParams } from 'next/navigation';

export default function Shared() {
  return <SharedLink base="/api/files" token={useSearchParams().get('link') ?? ''} />;
}

// And in the drive, where its links point:
<FileDrive base="/api/files" linkAddress={(token) => `${origin}/shared?link=${token}`} />
Until

For a while

Shared with someone for a day, a week, a month or three: past it, it gives them nothing, as if it had been taken back, and is no longer in their Shared with me.

await client.share(folder.id, 'auditor', 'viewer', 30);   // for thirty days, then nothing
await client.share(folder.id, 'auditor', 'editor');       // another role: the same end
await client.share(folder.id, 'auditor', 'editor', null); // for good

The dialog and the details say until when; one ended says so, to share it again or take it away.

Uploads

Requests for files

Request files, in the menu of a folder (and beside its name once in it): a link that takes files, “Only send files”, showing nothing of what is there, or “See and add files”. Made, it is copied, to open or send; whoever opens it signed out says who they are, if they want, and sends.

// A request for files: whoever opens it sends them, and sees nothing of what is there.
const { token } = await client.createLink(folder.id, 7, {
  uploads: 'only', // or 'also': they see the folder, and may add to it
  request: {
    title: 'Northwind',
    message: 'Send what is missing, please.',
    askName: true,
    email: 'carla@northwind.com',                    // for your app to remind her, and send a receipt
    items: [{ id: 'contract', label: 'Signed contract' }, { id: 'id', label: 'ID' }],
    closeWhenDone: true,                             // closed once both came
    accept: ['application/pdf', 'image/*'],
    maxSize: 25 * 1024 * 1024,
    perSender: true,                                 // Northwind papers / Carla Ruiz / …
    rename: '{item} - {name}',                       // "Signed contract - Carla Ruiz.pdf"
    fields: { client: 'northwind' },                 // found by your CRM with withField
  },
});

createDriveHandler({
  ...options,
  onReceived: ({ entry, from, item, request, got }) => mail(request?.email, `Received: ${item}`),
  onReview: ({ entry, status, reason, from }) => status === 'rejected' && tellToSendAgain(from, reason),
});
// A cron of yours: GET /admin/requests gives every request open, whom it was sent to and what is missing.
  • Whoever sends sees each document asked for in its own place, with “Choose a file” and “Take a photo”: the photos of one document go as one PDF. What they send they may take back for ten minutes, and they keep a receipt.
  • What arrives is the owner’s of the folder, counts against their space, and says who sent it and which document it is. The owner is told in the bell, finds it all in Received, and accepts it or turns it back with a reason.
  • The folder shows its requests open over its files: what came of each, what is missing, until when.
  • It goes up as any file does, in parts straight to the storage, resumed after a cut, checked as any is; the link may do nothing else. Its expiry and its password hold as for any link.
  • The page of the link carries the name of your company (Settings → company) and the colour of the drive’s brand, so whoever sends knows to whom.
Requests

Templates, reminders, checks, what expires

What a request does besides taking files. Each is on unless the drive or the server says false, and reminders only with a way to send them.

import { createDriveHandler, mailReminders, resend } from '@rebofiles/server';

createDriveHandler({
  ...options,
  // Whom a request was sent to, reminded every `remind` days of what is missing, and with "Remind now".
  reminders: mailReminders({
    mail: resend({ apiKey: process.env.RESEND_KEY!, from: 'Acme <files@acme.com>' }), // or (mail) => yourSmtp(mail)
    url: (token) => `https://files.acme.com/?link=${token}`,
  }),
  // Told 15 days before a file's field `expires`, in the bell and here; `false` for none.
  expiring: { days: 15, onExpiring: ({ entry, expires }) => log(entry.name, expires) },
  // What is written in what came, against its document's words and patterns: the core's, yours, or false.
  // check: async ({ entry, item, text }) => ({ missing: [], fields: await readWithYourService(text) }),
});

await client.createLink(folder.id, 30, {
  uploads: 'only',
  request: {
    email: 'carla@northwind.com',
    remind: 3,
    items: [
      { id: 'id', label: 'ID', words: ['INE', 'passport'], expires: true },
      { id: 'tax', label: 'Tax certificate', find: { rfc: '\\b([A-Z&Ñ]{3,4}\\d{6}[A-Z0-9]{3})\\b' } },
    ],
  },
});

// Templates, for everyone of the drive: saved from the dialog, or given as the defaults.
createDriveHandler({ ...options, settings: { requestTemplates: [{ id: 'supplier', name: 'Supplier sign-up', request }] } });
  • Templates: “Start from a template” over the dialog of a request, and “Save as template” for who may configure the drive (what it asks and how, not to whom). Settings → Appearance lists them, to take one out. requestTemplates: false of the screen for none.
  • A QR code of the address, in the page of a request made, to show a phone or download as a PNG; qr: false for none.
  • Reminders: whom a request was sent to, every 2, 3 or 7 days while something is missing and it is open, and at once with “Remind now”. Without the server’s reminders, the dialog offers none.
  • Checks: the words a document should say and the fields to find in it, against what is written in what came (its text, or OCR of a picture). What says none is marked “Does not say …” in Received; a blurry photo is asked again before it goes (checkBlur: false of the link page for not).
  • What expires: a document may ask until when it is good. Its owner is told in the bell fifteen days before; Received and the details show the days left, and “Ask again” makes a request of it alone.
Errors

This link does not work any more.

It was removed, it expired, it was used, what it read went to the trash, or its address was changed on the way: which one, a link does not say to whoever holds it.

This link asks for its password. · That is not the password.

The page of the link asks for it first, and sends it with every request.

Notify

Telling people

Activity and notifications: onShare is called with each share, to send a mail or a notice of your application.