Choose the download contract

For an integration that knows a file ID and needs both its filename and bytes, use File_getFile. It returns fileName, base64 fileData and cDate together. A new URL-based endpoint is not needed for that contract.

EndpointSelect bySuccessful responseUse when
File_getFileFile-record idJSON: fileName, fileData (base64), cDateForward a named attachment to another system.
File_getFile-record idRaw bytes; filename in HTTP Content-DispositionDownload through a browser or an HTTP client that preserves headers.
File_getByUrlExact stored urlJSON: filename, data, mimetype and selected metadataA caller already holds a tenant-relative stored URL. Send base64=1 for binary content.
File_getRequestedDetailsFile IDs or mapped filters, with explicit fieldsArray of projected records; no automatic backend content retrievalFetch metadata before downloading or processing several attachments.
Files_listtype_enum + resource type_idFile IDs, filenames, type and edit timestampEnumerate attachments belonging to one object.
UserFile_getTenant-relative file pathRaw bytes, or {mimetype,base64} with nonzero returnSupport a legacy path/image flow. The JSON response has no filename.

These names and keys are case-sensitive wire contracts. fileName/fileData and filename/data are different shapes. File_get does not return JSON on success.

Understand the identifiers

A file uploaded with type_enum=0 and type_id=683084 can return:

{
  "ok": 1,
  "id": 427230,
  "url": "files/0/683084/bc188e826790da0e9313d51487da1c25"
}
ValueMeaning
427230The file-record ID. Pass this as id to File_get or File_getFile.
0The relationship type: Customer file. Other common types are Asset 2, Job 3, User 4, Task 13 and Contract 14.
683084The linked Customer ID, supplied as type_id. It is neither the file ID nor the tenant ID.
bc188e...An opaque random storage token. Do not decode it or substitute another identifier.
files/0/683084/...The complete tenant-relative stored URL. Pass this unchanged as url to File_getByUrl.

The path describes the upload convention; the stored row remains authoritative. Copy its returned URL rather than rebuilding one from current relationship fields. Generic metadata edits can change relationships without moving the stored object.

In /f/<tenant>/files/0/683084/..., the number after /f/ is the account/tenant ID, a fourth identifier. It is not part of the tenant-relative url parameter. A download path is not a public share link or a credential.

base64, the second parameter shown on the legacy File_getByUrl page, is an encoding switch, not another ID. Set it to 1. Without it, invalid UTF-8 binary bytes can be substituted during JSON serialization.

Export a file with its original name

Start with the file ID from upload, a file table, or Files_list. Follow Getting started for authentication and the FM_api() helper.

$file = json_decode(FM_api('File_getFile', [
	'id' => '427230',
]), true, 512, JSON_THROW_ON_ERROR);
if (isset($file['error'])) {
	throw new RuntimeException($file['error']);
}
$filename = $file['fileName'];
$bytes = base64_decode($file['fileData'], true);
if ($bytes === false) {
	throw new RuntimeException('Invalid file encoding');
}
// Send $bytes and $filename using the receiving system's upload contract.

The filename is display data. Validate it before using it as a local filesystem path, and retain the Fieldmotion file ID in your own export checkpoint. Do not infer the upload MIME type from a File_getFile response: it has no MIME field.

To start with a Customer instead, call Files_list with type_enum=0 and type_id=<customer ID>, then download each returned id. The list is unpaginated and has no guaranteed order. Its optional last_edited lower bound is exclusive; use an overlap and deduplicate for incremental processing.

If only a stored URL is available, use:

$file = json_decode(FM_api('File_getByUrl', [
	'url' => 'files/0/683084/bc188e826790da0e9313d51487da1c25',
	'base64' => '1',
]), true, 512, JSON_THROW_ON_ERROR);
// Check error, then consume $file['filename'] and strict base64_decode($file['data'], true).

File_getByUrl does not accept an arbitrary external URL and does not interpret a numeric file ID as a URL. Its current selected fields also differ from the broad historical example: notably, type_id and cdate are not part of that projection.

Avoid the legacy enum lookup trap

File_getFilenameByEnumAndId and File_getFileDetailsByEnumAndId take a file ID plus an expected positive type_enum. They do not take a resource ID. Both reject Customer type 0, so they are not a general filename lookup for Customer attachments.

File_getByEnumAndId instead takes the resource type_id and streams the largest live matching file ID. It also rejects type zero. For multiple attachments, use a list and explicit file IDs.

UserFile_exists is another separate legacy contract: it compares metadata against a tenant-prefixed URL and returns an integer ID or zero. It does not reliably answer whether an ordinary uploaded file can be downloaded.

Mobile app downloads use a separate flow

The current app downloads cached ffiles-<file ID> records through the mobile intermediary’s syncDownloadOne path. Its file-disable settings and outsourced-Job validation are separate from these office download endpoints. Do not assume that a successful office API download proves that the same file is enabled or visible in the app.

Authentication, scope and operational limits

Use the account’s routed API host with signed credentials or a logged-in session. Ordinary content reads require a user; the shared Files View permission is added only for CRM2 sessions, with specific app/image exceptions described on each endpoint page. CRM1 does not acquire that CRM2 page requirement.

The existing configured TeamAccess entry policy is separate: feature/actor flags can require explicit visible IDs or supported resource relationships, and its implementation has no CRM-version condition. A URL-only lookup can be rejected for a scoped actor. Prefer ID-based downloads; do not add unrelated IDs to satisfy an entry guard.

Downloads load the complete file into memory. Base64 adds roughly one-third to its encoded size. The configured storage limit defaults to 12 MiB of decoded content; upload request limits are separate. File_getMaxUploadSize reports the configured limit, though its parameterless scoped entry behavior also needs confirmation.

A content read can hydrate bytes, restore an archive and update access metadata. A missing storage object can produce an empty byte result in the ID download routes. A successful metadata lookup is not proof that bytes are present; verify content as part of the receiving workflow. No endpoint here supplies an export acknowledgement or exactly-once delivery guarantee.