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
jobIdYou have two options depending on volume.
Option A: Single Job
Use the following API endpoint to pull a single attachment request in one call:
- Export Attachments
POST /efolder/v1/exportjobs
Query parameters (both optional):
| Parameter | Default | Description |
|---|---|---|
| includeNotActive | false | Include inactive files alongside active ones. |
| errorOnBackgroundAttachment | false | Fail 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"
}
]
}'
fileNameis supplied without an extension. Source is loan GUID and its entity type is loan.entities[]accepts the following entityTypes:attachment,document, orcondition.
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):
- Export Files Job Creator
POST /efolder/v1/loans/{loanId}/exportJobsCreator
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
requestIdfields 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
jobIdvalue for status polling, notentityId. As of the 25.3 release, these two values can differ. If you have any orchestration keyed onentityId, it needs to move tojobId.
Step 2 - Poll for Status
Use the following API endpoint to retrieve the status of the specified export job:
- Get Export Status
GET /efolder/v1/loans/{loanId}/exportJobsCreator
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. TreatPartialSuccessas 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);
}
Updated about 23 hours ago
