Liking cljdoc? Tell your friends :D

external

External third-party service adapters following the FC/IS pattern. Five adapters: SMTP, IMAP, Twilio, an FTPS file source, and a SOAP 1.1 client.

Stripe payments have moved to payments library.

Key namespaces

wagoe.external.schema              — Malli schemas for all five adapters
wagoe.external.ports               — Protocols: ISmtpProvider, IImapMailbox,
                                        ITwilioMessaging, IFileSource, ISoapClient
wagoe.external.core.smtp           — Pure SMTP helpers
wagoe.external.core.imap           — Pure IMAP helpers
wagoe.external.core.mail-tls       — Pure TLS settings shared by SMTP and IMAP
wagoe.external.core.twilio         — Pure Twilio helpers (build SMS/WhatsApp params)
wagoe.external.core.ftps           — Pure FTPS helpers
wagoe.external.core.soap           — Pure SOAP envelope building and parsing
wagoe.external.shell.adapters.smtp — SmtpProviderAdapter (Jakarta Mail)
wagoe.external.shell.adapters.imap — ImapMailboxAdapter (Jakarta Mail, UIDFolder)
wagoe.external.shell.adapters.twilio  — TwilioAdapter (clj-http, Basic auth)
wagoe.external.shell.adapters.ftps — FtpsFileSource (Commons Net, explicit TLS)
wagoe.external.shell.adapters.soap — SoapClient (java.net.http, SOAP 1.1)

Enabling adapters

All five adapters ship as :inactive in config.edn. Move to :active to enable:

;; In :active section of resources/conf/dev/config.edn

:wagoe.external/twilio
{:account-sid #env TWILIO_ACCOUNT_SID
 :auth-token  #env TWILIO_AUTH_TOKEN
 :from-number #env TWILIO_FROM_NUMBER}

:wagoe.external/smtp
{:host     #env SMTP_HOST
 :port     587
 :username #env SMTP_USERNAME
 :password #env SMTP_PASSWORD
 :tls?     true}

:wagoe.external/imap
{:host     #env IMAP_HOST
 :port     993
 :username #env IMAP_USERNAME
 :password #env IMAP_PASSWORD
 :ssl?     true}

:wagoe.external/ftps
{:host     #env FTPS_HOST
 :username #env FTPS_USERNAME
 :password #env FTPS_PASSWORD}

:wagoe.external/soap
{:endpoint     #env SOAP_ENDPOINT
 :contract     "IList"
 :username     #env SOAP_USERNAME
 :password     #env SOAP_PASSWORD}

TLS trust and certificate names

The SMTP and IMAP adapters check that the server’s certificate is trusted by the JVM and names :host. Two settings relax that, for a relay reached by IP or a provider alias:

:ssl-trust              "relay.internal"   ; "*" or hosts, string or vector
:check-server-identity? false              ; default true

:ssl-trust accepts any certificate from the listed hosts, self-signed or expired; "*" does so for every host. :check-server-identity? false accepts a certificate issued for another name. Either one lets someone who can intercept the connection read the credentials and the mail, so prefer adding the server’s CA to the JVM trust store and connecting by the name on the certificate. Trusting a host does not skip the name check; set both when the certificate is self-signed for another name.

Twilio SMS / WhatsApp

(require '[wagoe.external.ports :as ports])

;; Send SMS
(ports/send-sms! twilio {:to "+31612345678" :body "Your code: 123456"})

;; Send WhatsApp
(ports/send-whatsapp! twilio {:to "+31612345678" :body "Your order has shipped!"})

FTPS file imports

IFileSource lists a directory and downloads files. The connection uses explicit TLS with an encrypted data channel, passive mode and binary transfers, and the server certificate is checked against the JVM’s trust store, or :trust-store.

(ports/list-files! ftps "/outbox" {:match #"contacts-.*\.csv" :skip already-imported})
;; => {:success? true :files [{:name "contacts-1.csv" :path "/outbox/contacts-1.csv" :size 812 ...}]}

(ports/fetch-file! ftps "/outbox/contacts-1.csv")
;; => {:success? true :content <bytes>}

Transient failures (DNS, refused connection, timeouts, 4xx replies) are retried with backoff, :max-attempts times in all. A 5xx reply such as a rejected login fails at once. A failure returns {:success? false :error {:type :external-error …​}}, with :host, :attempts and :cause.

SOAP services

ISoapClient calls an operation on a SOAP 1.1 service such as WCF. Operations are described in config, not read from a WSDL: the SOAPAction defaults to namespace + :contract + / + operation, and :operations overrides it, the timeout and more per operation.

(ports/call! client :GetListEntries
             [[:Sitename "town"]
              [:filterRequest {:Sitename "town" :ListId list-id :SinceDate "2026-10-01T00:00:00"}]])
;; => {:success? true :result {:list-entries [...] :status "0"} :body {...}}

Element names are sent as written. A map is written in DataContract’s default order, ordinal by name; pass [name value] pairs where the contract declares another order. Nested members use :data-namespace. The response has kebab-case keys and string values: xsi:nil is nil, WCF dictionaries are maps and arrays vectors. Name an array that may hold one item in the operation’s :arrays, or it reads as a map.

A SOAP fault returns {:success? false :error {:type :fault :code …​ :message …​}}; no usable answer returns :type :external-error. Faults listed in :retry-faults (a throttling message, say), refused connections and 429s are retried with backoff; timeouts and 5xx are retried only for operations that are :idempotent?, the default. Credentials go as HTTP Basic or, with :credential-elements, as leading parameters, and are never logged.

Testing

clojure -M:test:test/mail:test/ftp :external

Can you improve this documentation? These fine people already did:
Thijs Creemers & thijscreemers
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close