> Use when handling file uploads, attachments, private/public file access, or S3 storage configuration. Prevents broken file URLs, permission leaks on private files, and failed uploads from incorrect MIME handling. Covers File DocType, frappe.get_file, upload API, private vs public directories, S3 integration, file URL patterns, attach field types.
npx skills add https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-core-files
| Action | Method | Notes |
|--------|--------|-------|
| Save file from bytes | save_file(fname, content, dt, dn) | Returns File doc |
| Save file from URL | save_url(file_url, fname, dt, dn) | Creates File doc from URL |
| Read file content | frappe.get_file(fname) | Returns [filename, content] |
| Get file path | get_file_path(file_name) | Resolves to absolute path |
| Upload via HTTP | POST /api/method/upload_file | Multipart form upload |
| Delete file | frappe.delete_doc("File", name) | Removes doc + filesystem file |
| Attach print | frappe.attach_print(dt, dn, print_format) | Returns {"fname", "fcontent"} |
| Get cached doc | frappe.get_cached_doc("File", name) | Read-only, cached |
What file operation do you need?
│
├─ Upload a file from user input?
│ ├─ Via web form → Attach field type (auto-handles upload)
│ └─ Via API → POST /api/method/upload_file
│
├─ Create a file programmatically?
│ ├─ From bytes/content → save_file(fname, content, dt, dn)
│ ├─ From external URL → save_url(file_url, fname, dt, dn)
│ └─ Full control → frappe.get_doc({"doctype": "File", ...}).insert()
│
├─ Read file content?
│ ├─ By filename → frappe.get_file(fname)
│ └─ By File doc → file_doc.get_content()
│
├─ Public or private?
│ ├─ Public (anyone with link) → is_private=0, URL: /files/fname
│ └─ Private (permission-based) → is_private=1, URL: /private/files/fname
│
└─ Generate PDF attachment?
└─ frappe.attach_print(doctype, name, print_format)
| Field | Type | Description |
|-------|------|-------------|
| file_name | Data | Filename without path |
| file_url | Data | URL path (e.g., /files/report.pdf) |
| file_type | Data | Extension (PDF, PNG, DOCX, etc.) |
| is_private | Check | 0 = public, 1 = private |
| is_folder | Check | True for folder entries |
| folder | Link → File | Parent folder |
| attached_to_doctype | Link → DocType | Parent document type |
| attached_to_name | Data | Parent document name |
| attached_to_field | Data | Field name on parent |
| content_hash | Data | SHA-256 for deduplication |
| file_size | Int | Size in bytes |
| Type | URL Pattern | Filesystem Path |
|------|------------|-----------------|
| Public | /files/{filename} | {site}/public/files/{filename} |
| Private | /private/files/{filename} | {site}/private/files/{filename} |
| Remote | https://... | Not stored locally |
| API | /api/method/{path} | Generated dynamically |
Valid URL prefixes: http://, https://, /api/method/, /files/, /private/files/.
ALWAYS use /private/files/ for sensitive documents. Public files are accessible to anyone with the URL, including unauthenticated users.
Frappe files use a three-tier permission model:
is_private=0) — readable by anyone with the URL (no authentication required for read)is_private=1) — access requires:attached_to_doctype/attached_to_name documentNEVER store sensitive data as public files. ALWAYS set is_private=1 for documents containing personal data, financial records, or confidential information.
from frappe.utils.file_manager import save_file
# Save a generated CSV
csv_content = "Name,Amount\nACME,1000\nGlobex,2000"
file_doc = save_file(
fname="report.csv",
content=csv_content.encode("utf-8"),
dt="Sales Invoice", # attach to this DocType
dn="SINV-00001", # attach to this document
folder="Home/Attachments", # optional folder
is_private=1, # private file
)
# file_doc.file_url → "/private/files/report.csv"
from frappe.utils.file_manager import save_url
file_doc = save_url(
file_url="https://example.com/logo.png",
filename="company-logo.png",
dt="Company",
dn="My Company",
folder="Home",
is_private=0,
)
# By filename
filename, content = frappe.get_file("report.csv")
# By File document
file_doc = frappe.get_doc("File", {"file_name": "report.csv"})
content_bytes = file_doc.get_content()
file_doc = frappe.get_doc({
"doctype": "File",
"file_name": "generated-report.pdf",
"attached_to_doctype": "Sales Invoice",
"attached_to_name": "SINV-00001",
"is_private": 1,
"content": pdf_bytes, # raw bytes — written to disk on insert
}).insert(ignore_permissions=True)
# Create PDF attachment dict (for use with sendmail)
pdf_attachment = frappe.attach_print(
"Sales Invoice",
"SINV-00001",
print_format="Standard",
)
# Returns: {"fname": "Sales Invoice - SINV-00001.pdf", "fcontent": <bytes>}
# Save PDF as file attachment
from frappe.utils.file_manager import save_file
pdf = frappe.get_print("Sales Invoice", "SINV-00001", print_format="Standard", as_pdf=True)
save_file("invoice.pdf", pdf, "Sales Invoice", "SINV-00001", is_private=1)
# Upload file attached to a document
curl -X POST https://site.example.com/api/method/upload_file \
-H "Authorization: token api_key:api_secret" \
-F "file=@/path/to/document.pdf" \
-F "doctype=Sales Invoice" \
-F "docname=SINV-00001" \
-F "is_private=1"
Response:
{
"message": {
"name": "FILE-00001",
"file_name": "document.pdf",
"file_url": "/private/files/document.pdf",
"is_private": 1
}
}
Default max file size: 10 MB per attachment.
Override in site_config.json:
{
"max_file_size": 20971520
}
Max attachments per document: Set via Customize Form → Max Attachments field on the DocType.
Check file size programmatically:
from frappe.utils.file_manager import check_max_file_size
check_max_file_size(content) # raises MaxFileSizeReachedError if too large
| Field Type | Stores | UI |
|------------|--------|----|
| Attach | Single file URL | File picker + upload button |
| Attach Image | Single image URL | Image preview + upload |
Both store the file_url string in the field value. The File DocType record is created separately with attached_to_field set.
Frappe supports custom file storage via the delete_file_data_content hook and custom upload handlers.
# In hooks.py of custom app
delete_file_data_content = "my_app.storage.delete_from_s3"
ALWAYS test file deletion when using custom storage backends — the default delete_file_from_filesystem only handles local files.
# site_config.json for S3-compatible storage
{
"s3_bucket": "my-frappe-files",
"s3_region": "eu-west-1",
"s3_access_key": "AKIA...",
"s3_secret_key": "...",
}
| Feature | v14 | v15 | v16 |
|---------|-----|-----|-----|
| File DocType | Available | Available | Available |
| content_hash dedup | Available | Available | Available |
| Image optimization | Manual | Auto (1920x1080, 85%) | Auto |
| Import/Export Zip | Not available | Available | Available |
frappe-core-permissions — Permission model for file accessfrappe-core-database — Database operations for File queriesIntegration with protocols.io API for managing scientific protocols. This skill should be used when working with protocols.io to search, create, update, or publish protocols; manage protocol steps and materials; handle discussions and comments; organize workspaces; upload and manage files; or integrate protocols.io functionality into workflows. Applicable for protocol discovery, collaborative protocol development, experiment tracking, lab protocol management, and scientific documentation.
Analyzes job descriptions and generates tailored resumes that highlight relevant experience, skills, and achievements to maximize interview chances
Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.
Build and distribute Expo development clients locally or via TestFlight
Use when you have a written implementation plan to execute in a separate session with review checkpoints
Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.
Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.
Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.
Take impertio-studio/frappe-core-files from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.