Files API
Download Files

Download Files

Download file attachments and images from DealCloud. Downloads use GET .../entryfiles/{entryId}/fields/{fieldId}; the SDK does not take object_id for download_file() (see intapp-rest-client streaming downloads).

download_file()

Download a file's binary content. You can either get bytes in memory or stream directly to a file path (memory-efficient for large files).

from dealcloud_sdk import DealCloud, DealCloudConfig
 
config = DealCloudConfig(
    siteUrl="yoursite.dealcloud.com",
    clientId=12345,
    clientSecret="your-secret",
)
dc = DealCloud.from_config_object(config)
 
# Option 1: Get bytes in memory
content = dc.download_file(
    entry_id=12345,
    field_id=67890,  # Binary field ID or API name
)
with open("document.pdf", "wb") as f:
    f.write(content)
 
# Option 2: Save directly to file (streaming; use for large files)
path = dc.download_file(
    entry_id=12345,
    field_id=67890,
    output_path="./document.pdf",
)
 
# Option 3: With progress callback
def on_progress(downloaded, total):
    if total:
        print(f"{100 * downloaded / total:.1f}%")
 
path = dc.download_file(
    entry_id=12345,
    field_id=67890,
    output_path="./document.pdf",
    progress_callback=on_progress,
)

download_attachment() is an alias with the same signature.

Parameters

ParameterTypeDescription
entry_idintEntry ID containing the file
field_idstr | intField ID or API name of the IMAGE or BINARY field
output_pathstr | NoneOptional. If set, file is streamed to this path and the path is returned.
progress_callbackCallable | NoneOptional. Called with (bytes_downloaded, total_bytes).

Returns

  • If output_path is None: bytes (raw file content).
  • If output_path is set: str (path to the saved file).

download_attachment_metadata()

Get file metadata without downloading content. This helper loads the row via the data query API and reads the field’s metadata (there is no separate entryfiles/.../info endpoint).

info = dc.download_attachment_metadata(
    object_id="Attachment",  # object that owns the row
    entry_id=12345,
    field_id=67890,
)
 
print(f"Filename: {info.get('fileName')}")
print(f"Size: {info.get('fileSize')} bytes")
print(f"Type: {info.get('contentType')}")

Response Properties

PropertyTypeDescription
fileNamestrOriginal filename
fileSizeint | NoneFile size in bytes (when present)
contentTypestrMIME type
uploadDatestr | NoneUpload timestamp (when present)

Downloading Images

For image fields (fieldType: 16):

# Download company logo (same entryfiles URL as documents)
logo = dc.download_file(
    entry_id=12345,
    field_id="Logo",  # Image field
)
 
# Save with extension
with open("company_logo.png", "wb") as f:
    f.write(logo)

Downloading Attachments

For attachment references (fieldType: 5 → Attachments object):

# 1. Read the attachment reference
deal = dc.read_data(
    "Deal",
    output="list",
    fields=["EntryId", "Contract"],  # Contract is attachment reference
    query="{EntryId: 12345}",
)[0]
 
# 2. Resolve attachment Entry ID
attachment_ref = deal.get("Contract")
if attachment_ref:
    attachment_id = attachment_ref["id"] if isinstance(attachment_ref, dict) else attachment_ref
 
    # 3. Download binary field on the attachment row (object name is site-specific)
    content = dc.download_file(
        entry_id=attachment_id,
        field_id="File",  # Binary field on Attachment object
    )

Batch Download Pattern

Download multiple files:

def download_all_attachments(dc, object_id, entry_id, output_dir):
    """Download all attachments for an entry."""
    from pathlib import Path
 
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
 
    fields = dc.get_fields(object_id)
    attachment_fields = [f for f in fields if f.isAttachment]
 
    entry = dc.read_data(
        object_id,
        output="list",
        fields=["EntryId"] + [f.apiName for f in attachment_fields],
        query=f"{{EntryId: {entry_id}}}",
    )[0]
 
    downloaded = []
 
    for field in attachment_fields:
        ref = entry.get(field.apiName)
        if not ref:
            continue
 
        refs = ref if isinstance(ref, list) else [ref]
 
        for r in refs:
            att_id = r["id"] if isinstance(r, dict) else r
 
            info = dc.download_attachment_metadata("Attachment", att_id, "File")
            content = dc.download_file(entry_id=att_id, field_id="File")
 
            fname = info.get("fileName") or f"{att_id}_file"
            filepath = output / fname
            filepath.write_bytes(content)
            downloaded.append(str(filepath))
 
    return downloaded
 
files = download_all_attachments(dc, "Deal", 12345, "./downloads")
print(f"Downloaded {len(files)} files")

Stream Large Files

Prefer output_path so the REST client streams to disk instead of buffering the full body in memory:

path = dc.download_file(
    entry_id=12345,
    field_id="File",
    output_path="large_file.zip",
)

Error Handling

from intapp_rest_client.exceptions import HttpError
 
try:
    content = dc.download_file(entry_id=12345, field_id="File")
except HttpError as e:
    if e.status_code == 404:
        print("File not found")
    elif e.status_code == 403:
        print("Permission denied")
    else:
        raise

Common Patterns

Download with Correct Extension

def download_with_extension(dc, object_id, entry_id, field_id, output_dir):
    """Download file with a sensible filename from metadata."""
    from pathlib import Path
    import mimetypes
 
    info = dc.download_attachment_metadata(object_id, entry_id, field_id)
 
    ext = mimetypes.guess_extension(info.get("contentType") or "") or ""
    filename = info.get("fileName") or f"{entry_id}_{field_id}{ext}"
 
    content = dc.download_file(entry_id=entry_id, field_id=field_id)
 
    output_path = Path(output_dir) / filename
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_bytes(content)
 
    return str(output_path)
 
path = download_with_extension(dc, "Attachment", 12345, "File", "./downloads")

Validate Before Download

def safe_download(dc, object_id, entry_id, field_id, max_size_mb=100):
    """Download with size validation using metadata first."""
 
    info = dc.download_attachment_metadata(object_id, entry_id, field_id)
    size = info.get("fileSize")
    if size is None:
        raise ValueError("Could not read file size from metadata")
 
    size_mb = size / (1024 * 1024)
    if size_mb > max_size_mb:
        raise ValueError(f"File too large: {size_mb:.1f}MB > {max_size_mb}MB limit")
 
    return dc.download_file(entry_id=entry_id, field_id=field_id)
 
content = safe_download(dc, "Attachment", 12345, "File", max_size_mb=50)

Related