Pulling eFolder Attachments into Your System

Overview

When you need to pull eFolder attachments into your system, the recommended method is to use a combination of export-related API endpoints in a three-step asynchronous flow to accomplish the task.

There is no one API call that returns bytes that can accomplish the goal. Therefore, review the following processes and samples to determine the best option that fits your output type and volumes.

Step 1 - Create the Export Job to Return a jobId

You have two options depending on volume.


Option A: Single Job

Use the following API endpoint to pull a single attachment request in one call:

Query parameters (both optional):

ParameterDefaultDescription
includeNotActivefalseInclude inactive files alongside active ones.
errorOnBackgroundAttachmentfalseFail the export if images lack full coverage.

📘

Additional query parameter skipPersonaChecks (default is false) bypasses persona-based permission checks. Best practice is to discuss with ICE Mortgage Technology support team before enabling.

Sample Request

curl -X POST "https://api.elliemae.com/efolder/v1/exportjobs" \ 
  -H "Authorization: Bearer {access_token}" \ 
  -H "Content-Type: application/json" \ 
  -d '{ 
    "fileName": "CascadeExport_Loan12345", 
    "annotationSettings": { 
      "visibility": ["Public", "Internal"] 
    }, 
    "source": { 
      "entityId": "fef1f8ef-bcf7-415d-8339-efc22d57f249", 
      "entityType": "loan" 
    }, 
    "entities": [ 
      { 
        "entityId": "d7725480-ee95-410a-8e64-aed6fae2c6b2", 
        "entityType": "document" 
    } 
  ] 
}' 

📘

fileName is supplied without  an extension. Source is loan GUID and its entity type is loan. entities[] accepts the following entityTypes: attachment, document, or condition.

Sample Response - 201 Created

{ 
  "jobId": "B2.2a54a87b-bdae-4fde-b1dc-71616ed14feb", 
  "status": "Queued" 
} 

Option B: Batch Job

Use the following API endpoint to pull multiple attachment requests in one call (typical use case):

📘

Maximum of 10 export requests per call. Additional query parameter skipPersonaChecks (default is false) bypasses persona-based permission checks. Best practice is to discuss with ICE Mortgage Technology support team before enabling.

Sample Request

curl -X POST "https://api.elliemae.com/efolder/v1/loans/{loanId}/exportJobsCreator" \ 
  -H "Authorization: Bearer {access_token}" \ 
  -H "Content-Type: application/json" \ 
  -d '{ 
    "requestId": "CascadeBatch-2026-08-05-001", 
    "annotationSettings": { "visibility": ["Public"] }, 
    "exportEntity": { 
      "exportMeta": [ 
        { 
          "requestId": "Job1", 
          "fileName": "AppraisalPackage", 
          "entities": [{ 
            "entityType": "urn:elli:encompass:document", 
            "entityId": "525bae6a-9621-4474-9836-d935a86acca1" 
          }] 
        }, 
        { 
          "requestId": "Job2", 
          "fileName": "ClosingDisclosure", 
          "entities": [{ 
            "entityType": "urn:elli:encompass:document", 
            "entityId": "7f2c1a90-3ddb-4a12-9c55-1b0e9a77c410" 
        }] 
      } 
    ] 
  } 
}' 

👍

The requestId fields are yours to define. They enable you to correlate each job in the response back to your originating request. Best practice is to populate these fields.

Sample Response - 202 Accepted

[ 
  { 
    "id": "B2.2a54a87b-bdae-4fde-b1dc-71616ed14feb", 
    "requestId": "Job1", 
    "status": "Queued", 
    "object": { 
      "id": "B2.2a54a87b-bdae-4fde-b1dc-71616ed14feb", 
      "entityType": "SkyDrive", 
      "contentType": "application/pdf" 
    } 
  } 
] 

🚧

Important: Use the jobId value for status polling, not entityId. As of the 25.3 release, these two values can differ. If you have any orchestration keyed on entityId, it needs to move to jobId.


Step 2 - Poll for Status

Use the following API endpoint to retrieve the status of the specified export job:

See the endpoint documentation for status values.

Sample Response Once Complete - 200 OK

{ 
  "jobId": "B2.2a54a87b-bdae-4fde-b1dc-71616ed14feb", 
  "status": "Success", 
  "fileResponse": { 
    "entityUri": "https://.../export/B2.2a54a87b....pdf", 
    "authorizationHeader": "elli-signature 9f8c...", 
    "contentType": "application/pdf", 
    "entityType": "SkyDrive", 
    "fileSize": 284736, 
    "pageCount": 12 
  } 
} 

🚧

Important: Only proceed to the next download step once status is Success. Treat PartialSuccess as requiring inspection — some entities exported, some did not.


Step 3 - Download the File

Issue another GET call using Get Export Status to fileResponse.entityUri, passing fileResponse.authorizationHeader verbatim as the Authorization header value. That value already includes its scheme prefix (e.g., elli-signature ...). Do not prepend Bearer, and do not substitute your Encompass OAuth token.

curl -X GET "{entityUri}" \ 
  -H "Authorization: {authorizationHeader}" \ 
  --output CascadeExport_Loan12345.pdf 

C# Code Example

public async Task<byte[]> DownloadExportedJobAsync( 
    string jobId, 
    CancellationToken cancellationToken) 
{ 
    // 1. Poll until terminal state 
    ExportJobStatus job; 
    var delay = TimeSpan.FromSeconds(2); 

    while (true) 
    { 
        using var statusRequest = new HttpRequestMessage( 
            HttpMethod.Get, $"{_baseUrl}/efolder/v1/exportjobs/{jobId}"); 
        statusRequest.Headers.TryAddWithoutValidation( 
            "Authorization", $"Bearer {_encompassToken.AccessToken}"); 

        using var statusResponse = await _httpClient.SendAsync( 
            statusRequest, cancellationToken); 
        statusResponse.EnsureSuccessStatusCode(); 
 
        job = await statusResponse.Content 
            .ReadFromJsonAsync<ExportJobStatus>(cancellationToken: cancellationToken); 

        if (job.Status is "Success" or "Completed") break; 

        if (job.Status is "Failed" or "Cancelled") 
            throw new InvalidOperationException( 
                $"Export job {jobId} ended as {job.Status}. " + 
                $"code={job.Error?.Code}; summary={job.Error?.Summary}"); 

        await Task.Delay(delay, cancellationToken); 
        delay = TimeSpan.FromSeconds(Math.Min(delay.TotalSeconds * 2, 30));  // backoff 
    } 

    // 2. Download using the job's own authorization header 
    using var downloadRequest = new HttpRequestMessage( 
        HttpMethod.Get, job.FileResponse.EntityUri); 
    downloadRequest.Headers.TryAddWithoutValidation( 
        "Authorization", job.FileResponse.AuthorizationHeader);   // verbatim 

    using var downloadResponse = await _httpClient.SendAsync( 
        downloadRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken); 

    if (!downloadResponse.IsSuccessStatusCode) 
    { 
        var body = await downloadResponse.Content.ReadAsStringAsync(cancellationToken); 
        throw new HttpRequestException( 
            $"Download failed. status={(int)downloadResponse.StatusCode}; body={body}"); 
    } 

		return await downloadResponse.Content.ReadAsByteArrayAsync(cancellationToken);
}
 

Did this page help you?