HTTP Destination

Send messages to an HTTP/REST endpoint with configurable authentication, retry policies, and TLS.

Overview

The HTTP destination delivers processed messages to any HTTP or REST endpoint via standard HTTP methods. It supports multiple authentication schemes, custom headers, configurable timeouts, TLS settings, and a flexible retry policy with backoff strategies. This is the most common destination type for integrating with FHIR servers, EHR APIs, cloud services, and third-party webhooks.

Configuration

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

yaml
# intu.yaml
destinations:
  fhir-server:
    type: http
    http:
      url: https://fhir.hospital.org/fhir/R4
      method: POST
      headers:
        Content-Type: application/fhir+json
        X-Correlation-Id: ${messageId}
      timeout_ms: 30000
      auth:
        type: bearer
        token: ${FHIR_ACCESS_TOKEN}
      tls:
        insecure_skip_verify: false
        ca_cert: /etc/ssl/certs/hospital-ca.pem
      retry:
        max_attempts: 5
        backoff: exponential
        initial_delay_ms: 500
        max_delay_ms: 30000
        jitter: true
        retry_on: [500, 502, 503, 504]
        no_retry_on: [400, 401, 404, 422]

Properties

Core Properties

url string required
The target URL to send requests to. Supports environment variable interpolation with ${VAR} syntax.
method string optional
HTTP method to use. Supported values: GET, POST, PUT, PATCH, DELETE. Defaults to POST.
headers map<string, string> optional
Custom HTTP headers to include in every request. Values support environment variable interpolation.
timeout_ms int optional
Request timeout in milliseconds. Defaults to 30000 (30 seconds).

Authentication

The auth object configures request authentication. The type field determines which auth strategy is used.

auth.type string required
Authentication type. One of: basic, bearer, api_key, oauth2.

Basic Auth

yaml
auth:
  type: basic
  username: ${HTTP_USER}
  password: ${HTTP_PASS}

Bearer Token

yaml
auth:
  type: bearer
  token: ${ACCESS_TOKEN}

API Key

yaml
auth:
  type: api_key
  header: X-API-Key
  key: ${API_KEY}

OAuth2 Client Credentials

yaml
auth:
  type: oauth2
  token_url: https://auth.hospital.org/oauth/token
  client_id: ${OAUTH_CLIENT_ID}
  client_secret: ${OAUTH_CLIENT_SECRET}
  scopes:
    - system/Patient.write
    - system/Encounter.write

TLS

tls.insecure_skip_verify bool optional
Skip server certificate verification. Defaults to false. Use only in development environments.
tls.ca_cert string optional
Path to a PEM-encoded CA certificate for verifying the server.
tls.client_cert string optional
Path to a PEM-encoded client certificate for mutual TLS.
tls.client_key string optional
Path to the client private key for mutual TLS.

Retry Policy

retry.max_attempts int optional
Maximum number of retry attempts before the message is marked as failed. Defaults to 3.
retry.backoff string optional
Backoff strategy between retries. One of: constant, linear, exponential. Defaults to exponential.
retry.initial_delay_ms int optional
Initial delay in milliseconds before the first retry. Defaults to 1000.
retry.max_delay_ms int optional
Maximum delay cap in milliseconds. Prevents exponential backoff from growing unbounded. Defaults to 60000.
retry.jitter bool optional
Add random jitter to retry delays to avoid thundering-herd effects. Defaults to true.
retry.retry_on int[] optional
HTTP status codes that should trigger a retry. Defaults to [500, 502, 503, 504].
retry.no_retry_on int[] optional
HTTP status codes that should never be retried, regardless of other settings. Takes precedence over retry_on.

Full Example

A complete configuration that receives HL7v2 ADT messages via TCP/MLLP, transforms them to FHIR Patient resources, and posts them to a FHIR R4 server.

Root Configuration

yaml
# intu.yaml
destinations:
  fhir-r4-server:
    type: http
    http:
      url: https://fhir.hospital.org/fhir/R4/Patient
      method: POST
      headers:
        Content-Type: application/fhir+json
        Accept: application/fhir+json
      timeout_ms: 15000
      auth:
        type: oauth2
        token_url: https://auth.hospital.org/oauth/token
        client_id: ${FHIR_CLIENT_ID}
        client_secret: ${FHIR_CLIENT_SECRET}
        scopes:
          - system/Patient.write
      retry:
        max_attempts: 5
        backoff: exponential
        initial_delay_ms: 500
        max_delay_ms: 30000
        jitter: true
        retry_on: [500, 502, 503, 504]

Channel Configuration

yaml
# channels/adt-to-fhir/channel.yaml
name: adt-to-fhir
description: Transform HL7v2 ADT messages to FHIR Patient resources

source:
  type: tcp
  tcp:
    port: 6661
    mode: mllp

destinations:
  - fhir-r4-server

Destination Transformer

typescript
// channels/adt-to-fhir/transformer.ts
import { Message, Context } from "@intu/sdk";

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

  const pid = hl7.segments?.find((s: any) => s.name === "PID");
  if (!pid) {
    ctx.logger.error("No PID segment found in ADT message");
    throw new Error("Missing PID segment");
  }

  const patient = {
    resourceType: "Patient",
    identifier: [
      {
        system: "urn:oid:2.16.840.1.113883.4.1",
        value: pid.fields[3]?.components[0] ?? "",
      },
    ],
    name: [
      {
        family: pid.fields[5]?.components[0] ?? "",
        given: [pid.fields[5]?.components[1] ?? ""],
      },
    ],
    gender: mapGender(pid.fields[8]?.components[0]),
    birthDate: formatDate(pid.fields[7]?.components[0]),
    address: [
      {
        line: [pid.fields[11]?.components[0] ?? ""],
        city: pid.fields[11]?.components[2] ?? "",
        state: pid.fields[11]?.components[3] ?? "",
        postalCode: pid.fields[11]?.components[4] ?? "",
      },
    ],
  };

  return {
    ...msg,
    body: patient,
    headers: {
      ...msg.headers,
      "content-type": "application/fhir+json",
    },
  };
}

function mapGender(code: string | undefined): string {
  switch (code) {
    case "M": return "male";
    case "F": return "female";
    case "O": return "other";
    default:  return "unknown";
  }
}

function formatDate(raw: string | undefined): string | undefined {
  if (!raw || raw.length < 8) return undefined;
  return `${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`;
}
Note The retry policy only applies to transient failures. Responses with status codes listed in no_retry_on are immediately treated as permanent failures. Ensure your FHIR server returns appropriate status codes so that validation errors (422) are not retried.
Tip Use OAuth2 client credentials for server-to-server integrations with FHIR endpoints. Store secrets in .env and reference them with ${VAR} syntax to keep credentials out of version control.