SFTP Destination

Upload processed files to a remote SFTP server for secure file exchange.

Overview

The SFTP destination uploads processed messages as files to a remote SFTP server. SFTP (SSH File Transfer Protocol) is widely used in healthcare for secure batch file exchange between organizations, such as sending claims to payers, delivering lab reports to referring physicians, or exchanging CDA documents with health information exchanges. This destination supports password and private key authentication.

Configuration

Define an SFTP destination in your root intu.yaml under the destinations key:

yaml
# intu.yaml
destinations:
  report-sftp:
    type: sftp
    sftp:
      host: sftp.partner-lab.org
      port: 22
      directory: /incoming/reports
      filename_pattern: "report_${date}_${messageId}.pdf"
      auth:
        type: private_key
        username: intu-service
        private_key: /etc/ssh/intu_rsa
        passphrase: ${SFTP_KEY_PASSPHRASE}

Properties

host string required
Hostname or IP address of the SFTP server.
port int optional
SSH port number. Defaults to 22.
directory string required
Remote directory path where files will be uploaded. The directory must exist on the server.
filename_pattern string optional
Template for generating remote filenames. Supports the same variables as the File destination: ${messageId}, ${timestamp}, ${channelId}, ${date}, ${time}. Defaults to ${messageId}.dat.
temp_prefix string optional
Prefix applied to the filename during upload. The file is renamed to its final name after the upload completes. This prevents downstream systems from picking up partial files. Defaults to .tmp_.

Authentication

auth.type string required
Authentication method. One of: password, private_key.
auth.username string required
SSH username for authentication.
auth.password string optional
SSH password. Required when auth.type is password. Use ${VAR} to reference environment variables.
auth.private_key string optional
Path to a PEM-encoded SSH private key. Required when auth.type is private_key.
auth.passphrase string optional
Passphrase for the private key, if encrypted.

Full Example

Receive CDA documents via HTTP, transform them to a standardized format, and upload to a health information exchange SFTP drop zone.

Root Configuration

yaml
# intu.yaml
destinations:
  hie-sftp-drop:
    type: sftp
    sftp:
      host: sftp.hie-network.org
      port: 22
      directory: /drop/cda-documents
      filename_pattern: "CDA_${timestamp}_${messageId}.xml"
      temp_prefix: ".uploading_"
      auth:
        type: private_key
        username: hospital-intu
        private_key: /etc/ssh/hie_rsa
        passphrase: ${HIE_KEY_PASSPHRASE}

Channel Configuration

yaml
# channels/cda-export/channel.yaml
name: cda-export
description: Export CDA clinical documents to HIE SFTP server

source:
  type: http
  http:
    port: 8081
    path: /cda/submit

destinations:
  - hie-sftp-drop

Destination Transformer

typescript
// channels/cda-export/transformer.ts
import { Message, Context } from "@intu/sdk";

export default function transform(msg: Message, ctx: Context): Message {
  const doc = msg.body;

  const patientName = doc.recordTarget?.patientRole?.patient?.name;
  const familyName = patientName?.family ?? "Unknown";
  const givenName = patientName?.given ?? "Unknown";

  ctx.logger.info(`Processing CDA for patient: ${givenName} ${familyName}`);

  const header = ``;
  const cdaXml = buildCdaDocument({
    id: msg.id,
    patientId: doc.recordTarget?.patientRole?.id?.extension ?? "",
    patientFamily: familyName,
    patientGiven: givenName,
    authorOrg: doc.author?.assignedAuthor?.representedOrganization?.name ?? "",
    sections: doc.component?.structuredBody?.sections ?? [],
  });

  return {
    ...msg,
    body: `${header}\n${cdaXml}`,
  };
}

function buildCdaDocument(data: {
  id: string;
  patientId: string;
  patientFamily: string;
  patientGiven: string;
  authorOrg: string;
  sections: any[];
}): string {
  const sectionXml = data.sections
    .map(
      (s: any) =>
        `
${s.title ?? ""}${s.text ?? ""}
` ) .join("\n"); return ` ${data.patientGiven}${data.patientFamily} ${data.authorOrg} ${sectionXml} `; }
Tip Use the temp_prefix option to prevent downstream systems from reading partially uploaded files. The file is first written with the temporary prefix, then atomically renamed once the upload completes.