Skip to main content

Outbound File Delivery

Outbound file delivery sends files attached to an epilot event to an external document API. Configure the trigger, file selection, authentication, request mapping, retries, and monitoring in Integration Hub.

CustomerRequestSubmitted is a common example: a submitted request can contain several event_attachments, and each attachment can be delivered separately.

Upload versus download

This page covers files moving out of epilot. To make files from an external archive available in epilot, use the download File Proxy.

Configure in Integration Hubโ€‹

An upload uses two use cases in the same integration:

Integration Hub use casePurpose
File Proxy ยท UploadDefines how files are grouped, authenticated, mapped, and sent to the external API.
OutboundSelects the event that triggers delivery and points it to the upload File Proxy.

Before you startโ€‹

You need:

  • an integration in Integration Hub;
  • an Event Catalog event that provides file attachments, such as CustomerRequestSubmitted;
  • an external HTTP endpoint that accepts the file; and
  • target URLs and credentials stored on the integration's Environment tab.

1. Create an upload File Proxyโ€‹

  1. Open Integration Hub, select the integration, and open Use Cases.
  2. Under Other use cases, select Create โ†’ File Proxy.
  3. Enter a name and slug, set Direction to Upload, and enable the use case.
  4. Configure the upload:
    • Delivery split โ†’ One per file sends each attachment separately. Leave it off to send all attachments in one delivery.
    • OAuth2 Authentication configures Client Credentials or Resource Owner Password authentication. Leave it unconfigured for an unauthenticated endpoint.
    • Delivery rules sets Attempts per file and Maximum file size.
    • Request steps defines the target URL, method, headers, request body, and response type. Add more steps for multi-request APIs.
    • Proxy optionally routes requests through a Secure Proxy from the same integration.
  5. In Live preview, select a sample event or load one from event history. Check the rendered URL, headers, and request body for each step.
  6. Select Create Use Case.

The preview does not fetch a file or call the target API. It shows the request shape using the selected event and placeholder values for file content, environment values, and earlier step results.

2. Add the outbound triggerโ€‹

  1. Return to Use Cases and add an Outbound use case.
  2. Under Event Type, select the event that should trigger the upload. File deliveries only support events that provide file attachments.
  3. Optionally add an Event filter. For example, $count(event_attachments) > 0 handles only events that contain files.
  4. Under File Deliveries, select Add file delivery.
  5. Give the delivery a name, select the upload File Proxy under Send files through, and enable it.
  6. For a file-only outbound use case, turn off Track acknowledgements. If the same use case also contains a webhook that expects acknowledgements, leave it on or use a separate outbound use case for the file delivery.
  7. Select Create Use Case.

Configuration object referenceโ€‹

Integration Hub edits the same configuration objects exposed by the Integration Toolkit API. The objects below show the relationship between the UI and API without including use-case metadata such as name, slug, type, or enabled state.

Upload File Proxy configurationโ€‹

{
"direction": "upload",
"fan_out": {
"enabled": true
},
"auth": {
"type": "oauth2_client_credentials",
"token_url": "{{env.DOCUMENT_TOKEN_URL}}",
"client_id": "{{env.DOCUMENT_CLIENT_ID}}",
"client_secret": "{{env.DOCUMENT_CLIENT_SECRET}}"
},
"steps": [
{
"method": "POST",
"url": "{{env.DOCUMENT_API_URL}}/documents",
"headers": {
"Content-Type": "application/json"
},
"body_jsonata": "{ \"filename\": $file_data[0].filename, \"fileData\": $file_data[0].base64 }",
"response_type": "json"
}
],
"upload": {
"max_file_bytes": 26214400,
"max_delivery_attempts": 8
}
}
Configuration fieldIntegration Hub controlPurpose
directionDirection โ†’ UploadSends epilot files to an external system.
fan_out.enabledDelivery split โ†’ One per fileChooses one delivery per attachment or one delivery containing all attachments.
authOAuth2 AuthenticationConfigures the token request and credentials.
secure_proxy.use_case_slugProxyRoutes requests through a Secure Proxy in the same integration.
steps[]Request stepsDefines the ordered HTTP requests.
steps[].enabledRun this step whenRuns the step only when its JSONata condition is true.
steps[].urlURLHandlebars template for the target URL.
steps[].methodMethodGET, POST, PUT, or PATCH.
steps[].headersHeadersRequest headers with optional Handlebars values.
steps[].body_jsonataBody (JSONata)Produces a JSON object or array for the request body.
steps[].response_typeResponse TypeTreats the response as json or binary.
upload.max_delivery_attemptsDelivery rules โ†’ Attempts per fileSets 1โ€“100 delivery attempts; the default is 8.
upload.max_file_bytesDelivery rules โ†’ Maximum file sizeSets a per-file limit up to the 100 MiB platform maximum.
upload.max_total_bytesConfiguration object onlyOptionally limits the combined size when One per file is off.

Outbound configurationโ€‹

{
"event_catalog_event": "CustomerRequestSubmitted",
"event_filter": "$count(event_attachments) > 0",
"ack_tracking": "off",
"mappings": [
{
"name": "External document archive",
"enabled": true,
"delivery": {
"type": "file_proxy",
"use_case_slug": "customer-request-document-upload"
}
}
]
}
Configuration fieldIntegration Hub controlPurpose
event_catalog_eventEvent TypeSelects the event that starts delivery.
event_filterEvent filterOptionally limits which events are handled.
ack_trackingTrack acknowledgementsWaits for a consumer acknowledgement when enabled.
mappings[]File DeliveriesAdds one or more file-delivery targets.
mappings[].nameFile delivery nameLabels the delivery in Integration Hub and monitoring.
mappings[].enabledEnabledTurns this file delivery on or off.
mappings[].delivery.use_case_slugSend files throughSelects an upload File Proxy from the same integration.

For endpoint details and the full schema, see Configuration.

Delivery splitโ€‹

The One per file setting controls what each delivery contains:

  • On โ€” one delivery per attachment. Each file has its own retry and monitoring status.
  • Off โ€” one delivery containing every attachment from the event.

Request-step expressions use $file_data in both modes. It contains one item when One per file is on and all attachments when it is off.

If the event contains no attachments, no request is sent. Use the outbound Event filter when events without files should be ignored before delivery.

Configure request stepsโ€‹

Each request step has these UI fields:

  • Run this step when โ€” optional JSONata condition;
  • URL โ€” Handlebars template;
  • Method โ€” GET, POST, PUT, or PATCH;
  • Response Type โ€” json or binary;
  • Headers โ€” optional name-value pairs; and
  • Body (JSONata) โ€” optional JSONata mapping for write methods.

Map the request bodyโ€‹

Body (JSONata) must return an object or array. Leave it blank to send the files unchanged: a single attachment object when One per file is on, or the complete attachment array when it is off.

The event payload is the expression root, so fields such as contact.customer_number, ticket._purpose, and _event_id are available directly. Additional bindings start with $:

BindingContains
$file_dataFiles in this delivery, always as an array.
$stepsResults of earlier request steps as statusCode, headers, and body.
$envValues from the integration's Environment tab.
$ack_idEvent acknowledgement ID, when available.
$now()Current ISO timestamp.
$germanDate(iso)An ISO timestamp formatted as a German date.

Each $file_data item provides the values most commonly needed by a target API:

FieldContains
entity_idFile entity ID.
version_indexReferenced file version.
filenameFile name.
mime_typeMIME type.
size_bytesFile size in bytes.
base64Base64-encoded file content.
_tagsFile entity tags.
relation_tagsTags on the relation that attached the file.
categoryFile category.
file_dateDocument date.

Example:

{
"customerNumber": contact.customer_number,
"filename": $file_data[0].filename,
"mimeType": $file_data[0].mime_type,
"fileData": $file_data[0].base64,
"idempotencyKey": $file_data[0].entity_id & ":" & _event_id
}

Write $file_data[0].filename, including the $. A missing value is omitted from the resulting object; null, "", false, and 0 are sent as values. For an optional field, test its existence explicitly:

{ "pin": $exists(contact.customer_pin) ? $string(contact.customer_pin) }

Configure URLs, headers, and authenticationโ€‹

URL, Headers, and OAuth2 fields use Handlebars. Reference values from the integration's Environment tab as {{env.NAME}}:

{{env.DOCUMENT_API_URL}}/documents

Previous JSON responses are available to later steps, for example {{steps.0.body.documentId}} in a URL or header and $steps[0].body.documentId in a JSONata body. Store base URLs and credentials as environment values rather than literals; see Environments & Secrets.

Run steps conditionallyโ€‹

Use Run this step when to enter a JSONata expression that returns true or false. A false result skips that step and the remaining steps for the delivery. For example, this sends only PDF files when One per file is on:

$file_data[0].mime_type = "application/pdf"

Use the outbound Event filter for event-level selection and Run this step when for file- or step-level selection.

Delivery behaviorโ€‹

  • Delivery is at least once. If the target API supports idempotency keys, send a stable value such as $file_data[0].entity_id & ":" & _event_id.
  • Any HTTP response below 400 completes the delivery successfully.
  • 408, 429, and 5xx responses, timeouts, and temporary authentication or file-fetch errors are retried up to Attempts per file. Other 4xx responses are not retried.
  • Maximum file size defaults to the 100 MiB platform maximum. A lower value can be configured per upload File Proxy.
  • When One per file is off, upload.max_total_bytes can set an additional combined-size limit through the configuration API.

Monitor deliveriesโ€‹

Open the integration's Monitoring tab to inspect deliveries and their event traces. With One per file enabled, each attachment has its own status, while all attachments from the same source event remain grouped by the event trace.

The most relevant monitoring codes are:

OutcomeCodeMeaning
DeliveredFILE_PROXY_UPLOADEDThe external API accepted the final request.
SkippedFAN_OUT_EMPTYThe event contained no attachments.
SkippedSTEP_DISABLEDRun this step when evaluated to false.
RetryingFILE_PROXY_UPLOAD_RETRYINGAnother delivery attempt is scheduled.
FailedFILE_PROXY_UPLOAD_FAILEDDelivery failed or exhausted its attempts.
FailedFILE_FETCH_FAILEDThe file content could not be loaded.
FailedFILE_TOO_LARGEA configured or platform size limit was exceeded.
FailedATTACHMENT_NOT_FOUNDThe referenced file or version was not found.
FailedMAPPING_EXPRESSION_FAILEDA body or condition expression was invalid or returned an unsupported result.

Open an entry to see the use case, delivery, attachment, attempt, and available request or response details. Credentials and large values such as file content are not shown.

Troubleshootingโ€‹

SymptomCheck in Integration Hub
No option appears under Send files throughCreate a File Proxy in the same integration, set Direction to Upload, and enable it.
The event does not appear under Event TypeFile delivery requires an event that provides file attachments.
The preview or delivery contains no filesConfirm the selected event contains event_attachments and check the outbound Event filter.
A mapped filename or file body is missingUse $file_data, including the $ and an array index such as $file_data[0].base64.
A URL or credential is emptyConfirm the value exists on the integration's Environment tab and reference it as {{env.NAME}}.
Every delivery is skippedCheck Run this step when and the STEP_DISABLED monitoring entry.
A target 4xx response is not retriedOnly 408 and 429 are retryable client errors. Correct the request mapping or target configuration.
A file is rejected as too largeCheck Maximum file size and, when sending all files together, upload.max_total_bytes.