Content Import Panel
The Content Import Panel is a self-service dashboard in Medusa where publishers upload and manage digital content for ingestion into Publica.la. It replaces the previous workflow that required SFTP clients and manual coordination with our team.
Publishers access the panel at their assigned Medusa URL, upload content files and a metadata spreadsheet, and launch the import. The system validates, processes, and ingests everything into Farfalla automatically.
Who uses it
| Role | Access | What they do |
|---|---|---|
| Publisher team members | Medusa dashboard (Livewire) | Upload files, manage imports, replace content |
| Publica.la admins | Medusa Nova | Create teams, assign API keys, configure content intakes |
Key concepts
- Content Intake: A configured content source for a tenant. Each intake has its own S3 bucket and Farfalla API key. Admins create these via Nova.
- Team: A group of users that share a single Farfalla API key. All content imported by any team member is visible to the entire team.
- Import: A single import session consisting of uploaded files plus a metadata spreadsheet. Each import tracks its own status and history.
- Import Record: One row from the spreadsheet, representing a single product to ingest.
Dashboard sections
The panel has four main sections, accessible from the left sidebar:
New Upload
This is the primary workflow. It has two steps presented as accordion sections:
Step 1: Upload content files
- Drag and drop or browse to select files (EPUB, PDF, MP3, JPG, PNG)
- Files upload directly from the browser to S3 via presigned URLs (no server relay)
- Progress bar per file with real-time status
- Up to 500 files per import, 500 MB per file
- Concurrent uploads (configurable, default 5 simultaneous)
Step 2: Upload metadata spreadsheet
- Accepts XLSX, XLS, or CSV (max 10 MB, up to 10,000 rows)
- The system auto-detects the spreadsheet format (Publica.la or VitalSource)
- Shows a preview of the first 5 rows for confirmation
- Header validation requires at least 60% of expected columns to match
- Users can override the auto-detected format if needed
Validation and launch
After both uploads complete, the system validates spreadsheet rows and matches them against uploaded files. If validation finds issues, the panel displays the first 50 errors. The user can fix and re-upload, or choose Continue with errors to skip invalid rows and proceed with the valid ones only.
Clicking Start Import creates import records in batches of 500 and dispatches processing jobs. See Validation and error reference for the full list of errors the user may encounter.
Import History
A table listing all imports for the current content intake.
| Column | Description |
|---|---|
| Date | When the import started |
| Type | Import or File Update |
| Spreadsheet | Original filename |
| Format | Publica.la or VitalSource |
| Items | Total records in the spreadsheet |
| Status | Done, Failed, Partial, Processing |
| Duration | Time from start to completion |
| User | Email of the team member who launched it |
Status indicators:
- Green (Done): All records ingested successfully
- Red (Failed): Import failed entirely
- Amber (Partial): Some records succeeded, some failed
- Blue (Processing): Import is still running
Expanding a row shows additional detail:
- While processing: Progress bar with counts (created/errors/remaining), live polling every 5 seconds
- After completion: Final stats, first 10 error messages, and a Download CSV button for the full report
The CSV report includes: Row number, External ID, Title, File Type, Status, Error Message.
Products
A table of all successfully ingested products for the team, sorted by ingestion date (newest first). This section shows the cumulative result of all imports.
Columns: ISBN/External ID, Title, Upload date, Uploaded by.
Paginated (20 per page).
File Update
Allows replacing content files (EPUB, PDF, MP3) for products that were already imported. Two main use cases:
- Dummy to original: Teams that import with placeholder files first (to get metadata in quickly) and replace with final binaries later
- Content correction: Replacing a file to fix errata or publish a new edition
Flow:
- Upload replacement files (same process as New Upload file section)
- The system matches files to existing products by external ID
- Validation confirms the external ID exists and the product was previously ingested
- The replacement triggers a full re-ingestion in Farfalla (not just a file swap; all derivatives like Smart Zoom and pagination are regenerated)
File Update runs its own validation rules before any replacement happens. See Validation and error reference.
Spreadsheet formats
Publica.la format
The standard Publica.la spreadsheet. Required columns:
| Column | Description | Example |
|---|---|---|
| ISBN | External identifier | 978-84-123-4567-8 |
| Type | File format | epub, pdf, mp3, physical |
| Name | Product title | The Butterfly Garden |
| File URL | Filename or full URL | butterfly.epub |
| Lang | Language code | es, en, pt |
| Prices | Currency:amount pairs | USD:19.99|BRL:89.90 |
| Authors | Author names | Author One|Author Two |
| Publishers | Publisher name | Editorial ABC |
Optional columns: Description, Publication Date, Cover File URL, Keywords, Narrators, Categories, Audience, Free, Retail Enabled, Countries, Collections, Editions, Allow Preview.
Custom metadata is supported via taxonomy_* columns (e.g., taxonomy_genre, taxonomy_age_group). Values are passed through to Farfalla as custom taxonomies.
List separators: Use |, ;, or , to separate multiple values in a single cell.
Price format: CurrencyCode:Amount pairs separated by |. Example: ARS:1000|USD:20|BRL:89.90.
Date format: The system accepts DD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD, and Excel serial dates. All are normalized to YYYY-MM-DD.
VitalSource format
Used for content migrated from VitalSource catalogs. This format uses a 3-row header structure:
- Row 1: Group headers (e.g., "Basic", "Asset Identifiers")
- Row 2: Column headers (used for field mapping)
- Row 3: System names (skipped during parsing)
Required columns:
| Column | Description |
|---|---|
| VBID | VitalSource Book ID (external identifier) |
| Title | Product title |
| Imprint | Publisher imprint |
| Kind | Content type |
| Print ISBN | Print edition ISBN |
| E ISBN | Digital edition ISBN |
| Author Name | Author |
| Edition | Edition number/name |
| Language | Language code |
| Publication Date | Publication date |
Optional: Price columns per currency (USD Digital List Price, GBP Digital List Price), BISAC codes, sales rights, sampling settings, description.
File matching: In VitalSource format, files are matched by VBID prefix. For example, a file named L-999-70114.epub matches the row with VBID L-999-70114. Multiple files per VBID are supported (one content file plus an optional cover image).
Teams and access control
All operations in the Import Panel are scoped by team. A team groups users who share a single Farfalla API key; content imported by any member is visible to the entire team. Roles include Admin, Team Member, and Viewer.
For the full access model, onboarding flow, and Nova admin setup, see Teams and Access Control.
Limits and configuration
| Parameter | Default | Description |
|---|---|---|
| Max files per import | 500 | Maximum content files in a single upload session |
| Max file size | 500 MB | Per-file size limit |
| Max spreadsheet rows | 10,000 | Maximum rows in the metadata spreadsheet |
| Max spreadsheet size | 10 MB | File size limit for XLSX/XLS/CSV |
| Batch size | 50 | Records sent per API call to Farfalla |
| Upload concurrency | 5 | Simultaneous file uploads to S3 |
| Retry attempts | 3 | Maximum retries on server/rate-limit errors |
| Retry delay | 30 seconds | Wait time between retries |
| Presigned URL TTL | 60 minutes | Validity of S3 upload URLs |
| Stale import threshold | 5 minutes | Time before stuck imports are auto-rescued |
Validation and error reference
Complete list of errors the user may encounter during content load, grouped by flow and stage. Messages shown are the exact strings users see in the panel.
New Upload
The Import Panel surfaces errors at five stages. Each stage gives the user actionable feedback before or during the final import.
Before a file is uploaded (browser validation)
| Condition | Message shown |
|---|---|
| File extension not allowed (allowed: pdf, epub, mp3, jpg, jpeg, png) | Unsupported file type: .{ext} |
| File larger than 500 MB | File too large (max 500 MB) |
| Already reached the 500-file limit for this import | Maximum number of files per import reached (500) |
While parsing the spreadsheet
| Condition | Outcome |
|---|---|
| More than 10,000 rows | The spreadsheet exceeds the maximum of 10,000 rows. |
| No known format matches at least 60% of the headers | A modal prompts the user to select the format manually (Publica.la or VitalSource) |
| File is empty, corrupted, or unreadable | The parser returns zero rows; all headers appear as unmatched in the preview |
While validating rows (before launch)
| Condition | Message shown |
|---|---|
Required title is missing (name for Publica.la, title for VitalSource) | Name is required |
| File type is not one of pdf, epub, audio, physical | Invalid file type: {value} |
| External ID (ISBN or VBID) already ingested for this team and intake | Already imported: {externalId} |
Referenced file url does not match any uploaded file | No uploaded file matches: {filename} |
Referenced cover file url does not match any uploaded file | No uploaded file matches: {filename} |
| VitalSource only: no uploaded content file matches the VBID prefix | No uploaded content file matches external ID: {externalId} |
Physical products skip file matching. External URLs (starting with http:// or https://) pass through without local matching.
While uploading each file to S3
| Condition | Message shown |
|---|---|
| HTTP error from S3 | Upload failed (HTTP {status}) |
| Browser network error | Network error, check your connection |
| Upload exceeds the 10-minute timeout | Upload timed out |
| User navigates away or cancels the request | Upload cancelled |
During background processing (after Start Import)
| Condition | Outcome |
|---|---|
| Metadata spreadsheet expired between upload and launch | Import is marked as failed validation; the user re-uploads the spreadsheet |
| Farfalla returns 429 (rate limit) | Transparent retry with a 30-second delay; no user action required |
| Farfalla returns 5xx (server error) | Up to 3 retries with 30-second delay; after that the batch is marked as failed (server error) |
| Farfalla returns 4xx (client error) for a whole batch | The batch is marked as failed with a client-error reason |
| Farfalla rejects a single row | That row is marked as failed; the error message is stored and shown in the CSV report |
| Background job crashes | Records reset to discovered; a watchdog re-dispatches after 5 minutes |
Users see the full list of per-row errors in the CSV report available after the import finishes (see Import History).
File Update
File Update runs its own checks before any replacement happens. Allowed extensions for replacement are limited to content files: epub, pdf, mp3.
| Condition | Message shown |
|---|---|
| Filename has no extension | File has no valid extension: {filename} |
| Extension not allowed (allowed: epub, pdf, mp3) | Unsupported file extension: {ext} |
| Same filename appears twice in the same batch | Duplicate file for external_id: {externalId} |
| External ID does not match any previously imported product | No existing product found for external_id: {externalId} |
| New file type differs from the original product | File type mismatch: uploaded {newType} but product is {originalType} |
| Audiobook filename is missing or has an invalid track number | Missing or invalid track number in filename: {filename} (expected name_{track}.mp3) |
| Two audiobook files share the same track number for the same product | Duplicate track {order} for external_id: {externalId} |
| Audiobook track numbers are not contiguous (for example 1, 2, 4 instead of 1, 2, 3) | Non-contiguous track orders for external_id: {externalId} (got {actual}, expected {expected}) |
Related documentation
- Teams and Access Control: Multi-tenant access model, roles, and admin setup
- ONIX Intake Overview: Automated ONIX XML processing (separate pipeline)
- Setting Up a New ONIX Intake: Nova configuration for ONIX intakes
- Upload Methods: Overview of all content upload methods in Publica.la