File API
Files in epilot are uploaded and managed through the File API.
Downloading Filesβ
Downloading a file is a two-step process: the File API returns a temporary download URL, and the file content itself is then fetched from that URL with a second request.
Step 1: Get a download URLβ
The downloadFile operation returns a temporary presigned S3 URL for downloading a file.
GET /v1/files/{id}/download
{
"download_url": "https://epilot-prod-user-content.s3.eu-central-1.amazonaws.com/...?X-Amz-..."
}
The download_url is valid for 15 minutes.
info
The downloadFile operation requires a valid access token.
Step 2: Fetch the file from the download URLβ
Make a GET request to the returned download_url to retrieve the actual file. No Authorization header is needed for this request β the URL is presigned.
GET {download_url}
The response body is the raw binary file content, served with the file's original Content-Type.
note
Public files can also be downloaded directly via their public_url property. However, downloadFile works for both public and private files and is the recommended approach.
Downloading by S3 Referenceβ
When you have a file's s3ref (bucket and key) but not its entity ID β for example from a webhook payload or an entity attribute value β use the downloadS3File operation instead:
POST /v1/files:downloadS3?s3_bucket={bucket}&s3_key={key}
It returns the same download_url response as downloadFile. Fetch the file with a second GET request as described above.
tip
Pass the s3_key value exactly as returned by the API. Object keys store the filename segment percent-encoded (e.g. a file named StraΓe 1.pdf is stored under .../Stra%C3%9Fe%201.pdf), so make sure your HTTP client URL-encodes the query parameter value β a literal % must arrive encoded as %25.
Uploading Filesβ
The uploadFileV2 operation returns a temporary presigned S3 URL for uploading a file via PUT.
After uploading, call saveFileV2 to persist the file and create a File entity. Files that are uploaded but not saved expire and are deleted within 24 hours.
info
uploadFileV2 requires a valid access token. Use uploadFilePublic for public journey submissions.
Example Upload Flowβ
Step 1: Call uploadFileV2 to get an s3refβ
POST /v2/files/upload
{
"filename": "example.pdf",
"mime_type": "application/pdf"
}
{
"s3ref": {
"bucket": "epilot-prod-user-content",
"key": "123/temp/f5e1c2be-7392-4a0d-8c45-236743423733/example.pdf"
},
"upload_url": "https://epilot-prod-user-content.s3.eu-central-1.amazonaws.com/...?X-Amz-...",
"public_url": "https://epilot-prod-user-content.s3.eu-central-1.amazonaws.com/..."
}
Step 2: Upload the file to S3β
Use the returned upload_url to upload your file via PUT.
tip
Set the Content-Type header to match the file's MIME type.
PUT {upload_url}
Content-Type: application/pdf
(binary data)
Step 3: Call saveFileV2 to persist the fileβ
POST /v2/files
{
"s3ref": {
"bucket": "epilot-prod-user-content",
"key": "123/temp/f5e1c2be-7392-4a0d-8c45-236743423733/example.pdf"
},
"filename": "example.pdf",
"access_control": "private"
}
{
"_id": "ef7d985c-2385-44f4-9c71-ae06a52264f8",
"filename": "example.pdf",
"access_control": "private",
"public_url": "...",
"type": "document",
"mime_type": "application/pdf",
"size_bytes": 0,
"versions": [...]
}
note
The public_url property is always present in the response. When access_control is private, the URL returns a 403 response.
Attach the returned file entity ID to a business entity as a relation on any file attribute, or the default _files attribute:
{
"_schema": "opportunity",
// ...other entity fields
"_files": {
"$relation": [
{ "entity_id": "ef7d985c-2385-44f4-9c71-ae06a52264f8" }
]
}
}
External Filesβ
When migrating a large document archive is impractical, skip the upload steps and use the custom_download_url property to reference files stored externally.
POST /v2/files
{
"custom_download_url": "https://external-url.io?fileID=42",
"filename": "example.pdf",
"access_control": "private"
}
{
"_id": "ef7d985c-2385-44f4-9c71-ae06a52264f8",
"filename": "example.pdf",
"access_control": "private",
"custom_download_url": "https://external-url.io?fileID=42",
"type": "unknown",
"size_bytes": 0,
"versions": [...]
}
epilot retrieves external files on the fly with a short-lived signature and streams them directly to the end user. Use the verifyCustomDownloadUrl operation to verify that a download request originates from epilot.
Signature verification details
Pass the full request URL exactly as received to verifyCustomDownloadUrl. The signature covers a canonical form of the URL (query parameters sorted by key), so validity does not depend on the query parameter order β but every parameter and value must be present and unmodified. Signatures are cryptographically bound to the epilot organization and expire 15 minutes after minting.

warning
Download requests for external files come from the user's browser. Do not include sensitive data (internal tokens, credentials) in the response -- the end user can inspect all response headers and content.
File Proxy for ERP Integrationsβ
When integrating with ERP document archives that require authentication or multi-step API calls to fetch files, use the File Proxy from the Integration Toolkit. The file proxy handles OAuth2 authentication, secret management, and complex download flows declaratively β file entities are created with a custom_download_url pointing to the proxy, and files are fetched on demand when users view them.
Updating Filesβ
Update or save new versions of File entities via the saveFileV2 operation.
Deleting Filesβ
Delete files using the deleteFile operation. This permanently deletes both the File entity and the underlying S3 object.