Letterbox.Plug (Letterbox v0.1.0-rc.0)

Copy Markdown View Source

The webhooks of the ingresses (Letterbox.Ingress), a route of the app's own router. mix letterbox.install writes one for every ingress, the ingress named by the path:

scope "/inbound" do
  pipe_through :webhooks
  post "/webhooks/:ingress", Letterbox.Plug, []
end

The path, the scope and the pipeline are the app's to change: nothing in Letterbox depends on them. A route can also name its ingress, one route for each, at any path:

post "/mail/resend", Letterbox.Plug, ingress: :resend
post "/hooks/postfix", Letterbox.Plug, ingress: :relay

The ingress is the route's :ingress option, else the :ingress parameter of its path. In an app without Phoenix's router, it is a Plug like any other (forward "/inbound/resend", Letterbox.Plug, ingress: :resend).

It answers:

  • 204 - the request was the ingress's, and what it brought is kept (or, for an email to fetch, noted): the provider stops sending it. Again for a message already kept.
  • 401 - it was not the ingress's (verify/3 refused it).
  • 404 - no ingress of that name.
  • 413 - the body is larger than :max_bytes (Letterbox.Config).
  • 422 - the ingress could not read it.
  • 500 - it could not be kept (the store, the database): the provider sends it again later.

The route takes no session and no CSRF token: a provider has neither. Its pipeline must not have :protect_from_forgery.

A signed webhook needs its body as it was sent: the plug declares it (use PhoenixMountable.RawBody), and the endpoint's Plug.Parsers keeps it with body_reader: {PhoenixMountable.RawBody, :read_body, [MyAppWeb.Router]}, which mix letterbox.install adds. A relay's raw email (message/rfc822) is passed by the parsers unread, and needs no copy.