diff --git a/docs/404.html b/docs/404.html index ec0ac9b03..ba77830ff 100644 --- a/docs/404.html +++ b/docs/404.html @@ -11,7 +11,7 @@ - + @@ -330,6 +330,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -538,10 +553,10 @@

    404 - Not found

    - + - + diff --git a/docs/apis/apis/index.html b/docs/apis/apis/index.html index 38220f474..a6686b736 100644 --- a/docs/apis/apis/index.html +++ b/docs/apis/apis/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -774,10 +789,10 @@

    Common HTTP error codes - + - + diff --git a/docs/apis/campaigns/index.html b/docs/apis/campaigns/index.html index b9e03776b..aabfeb915 100644 --- a/docs/apis/campaigns/index.html +++ b/docs/apis/campaigns/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -1567,10 +1582,10 @@
    Example Response - + - + diff --git a/docs/apis/import/index.html b/docs/apis/import/index.html index d6faab2ef..d218eb22f 100644 --- a/docs/apis/import/index.html +++ b/docs/apis/import/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -910,10 +925,10 @@
    Example Response - + - + diff --git a/docs/apis/lists/index.html b/docs/apis/lists/index.html index 768ca3d2d..243944700 100644 --- a/docs/apis/lists/index.html +++ b/docs/apis/lists/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -1164,10 +1179,10 @@
    Example Response - + - + diff --git a/docs/apis/media/index.html b/docs/apis/media/index.html index d3ace76c3..1a547dd96 100644 --- a/docs/apis/media/index.html +++ b/docs/apis/media/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -817,7 +832,7 @@
    Example Responseuuid -Media file uuuid +Media file uuid string (uuid) @@ -848,7 +863,7 @@
    ParametersExample Response - + - + diff --git a/docs/apis/subscribers/index.html b/docs/apis/subscribers/index.html index cb9616311..da4401df5 100644 --- a/docs/apis/subscribers/index.html +++ b/docs/apis/subscribers/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -1649,7 +1664,7 @@
    ParametersRequest body String Required -The email address of the new susbcriber. +The email address of the new subscriber. name @@ -1684,7 +1699,7 @@
    ParametersRequest body Bool Optional -If true, marks subscriptsions as confirmed and no-optin e-mails are sent for double opt-in lists. +If true, marks subscriptions as confirmed and no-optin e-mails are sent for double opt-in lists. @@ -1718,7 +1733,7 @@
    Parameters Name -Paramter type +Parameter type Data type Required/Optional Description @@ -1737,7 +1752,7 @@
    ParametersRequest body String Required -Wether to add, remove, or unsubscribe the users. +Whether to add, remove, or unsubscribe the users. target_list_ids @@ -1934,10 +1949,10 @@
    Example Response - + - + diff --git a/docs/apis/templates/index.html b/docs/apis/templates/index.html index 455b6f981..b38807f08 100644 --- a/docs/apis/templates/index.html +++ b/docs/apis/templates/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -1145,10 +1160,10 @@
    Example Response - + - + diff --git a/docs/apis/transactional/index.html b/docs/apis/transactional/index.html index 9375eef4c..39a80a6b8 100644 --- a/docs/apis/transactional/index.html +++ b/docs/apis/transactional/index.html @@ -13,7 +13,7 @@ - + @@ -337,6 +337,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -761,10 +776,10 @@
    Response - + - + diff --git a/docs/archives/index.html b/docs/archives/index.html new file mode 100644 index 000000000..ec629c1f6 --- /dev/null +++ b/docs/archives/index.html @@ -0,0 +1,614 @@ + + + + + + + + + + + + + + + + + + + + + + Archives - listmonk / Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + +
    +
    + + + +
    +
    +
    + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    + + + + +

    Archives

    +

    A global public archive is maintained on the public web interface. It can be +enabled under Settings -> Settings -> General -> Enable public mailing list +archive.

    +

    To make a campaign available in the public archive (provided it has been +enabled in the settings as described above), enable the option +'Publish to public archive' under Campaigns -> Create new -> Archive.

    +

    When using template variables that depend on subscriber data (such as any +template variable referencing .Subscriber), such data must be supplied +as 'Campaign metadata', which is a JSON object that will be used in place +of .Subscriber when rendering the archive template and content.

    +

    When individual subscriber tracking is enabled, TrackLink requires that a UUID +of an existing user is provided as part of the campaign metadata. Any clicks on +a TrackLink from the archived campaign will be counted towards that subscriber.

    +

    As an example:

    +
    {
    +  "UUID": "5a837423-a186-5623-9a87-82691cbe3631",
    +  "email": "example@example.com",
    +  "name": "Reader",
    +  "attribs": {}
    +}
    +
    +

    Archive campaign

    + + + + + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/javascripts/bundle.b4d07000.min.js b/docs/assets/javascripts/bundle.220ee61c.min.js similarity index 68% rename from docs/assets/javascripts/bundle.b4d07000.min.js rename to docs/assets/javascripts/bundle.220ee61c.min.js index 3c0bdad9a..116072a11 100644 --- a/docs/assets/javascripts/bundle.b4d07000.min.js +++ b/docs/assets/javascripts/bundle.220ee61c.min.js @@ -24,6 +24,6 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */var wr=function(e,t){return wr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(r[o]=n[o])},wr(e,t)};function ie(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");wr(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}function fn(e,t,r,n){function o(i){return i instanceof r?i:new r(function(s){s(i)})}return new(r||(r=Promise))(function(i,s){function a(u){try{c(n.next(u))}catch(p){s(p)}}function f(u){try{c(n.throw(u))}catch(p){s(p)}}function c(u){u.done?i(u.value):o(u.value).then(a,f)}c((n=n.apply(e,t||[])).next())})}function $t(e,t){var r={label:0,sent:function(){if(i[0]&1)throw i[1];return i[1]},trys:[],ops:[]},n,o,i,s;return s={next:a(0),throw:a(1),return:a(2)},typeof Symbol=="function"&&(s[Symbol.iterator]=function(){return this}),s;function a(c){return function(u){return f([c,u])}}function f(c){if(n)throw new TypeError("Generator is already executing.");for(;r;)try{if(n=1,o&&(i=c[0]&2?o.return:c[0]?o.throw||((i=o.return)&&i.call(o),0):o.next)&&!(i=i.call(o,c[1])).done)return i;switch(o=0,i&&(c=[c[0]&2,i.value]),c[0]){case 0:case 1:i=c;break;case 4:return r.label++,{value:c[1],done:!1};case 5:r.label++,o=c[1],c=[0];continue;case 7:c=r.ops.pop(),r.trys.pop();continue;default:if(i=r.trys,!(i=i.length>0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: `+r.map(function(n,o){return o+1+") "+n.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var _=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?_:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():_))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>_),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=M("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + - + diff --git a/docs/concepts/index.html b/docs/concepts/index.html index 520a0c060..9da2421db 100644 --- a/docs/concepts/index.html +++ b/docs/concepts/index.html @@ -15,7 +15,7 @@ - + @@ -463,6 +463,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -837,10 +852,10 @@

    Bounce - + - + diff --git a/docs/configuration/index.html b/docs/configuration/index.html index 3868dce9b..cc1a6be9a 100644 --- a/docs/configuration/index.html +++ b/docs/configuration/index.html @@ -15,7 +15,7 @@ - + @@ -461,6 +461,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -917,10 +932,10 @@

    Using bind mounts - + - + diff --git a/docs/developer-setup/index.html b/docs/developer-setup/index.html index 6e172401f..011cb2108 100644 --- a/docs/developer-setup/index.html +++ b/docs/developer-setup/index.html @@ -15,7 +15,7 @@ - + @@ -394,6 +394,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -665,10 +680,10 @@

    Production build - + - + diff --git a/docs/external-integration/index.html b/docs/external-integration/index.html index bb1bb6680..5b5207d17 100644 --- a/docs/external-integration/index.html +++ b/docs/external-integration/index.html @@ -15,7 +15,7 @@ - + @@ -339,6 +339,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -632,10 +647,10 @@

    Interacting directly with the DB - + - + diff --git a/docs/i18n/index.html b/docs/i18n/index.html index af11e0b14..600216852 100644 --- a/docs/i18n/index.html +++ b/docs/i18n/index.html @@ -9,13 +9,13 @@ - + - + @@ -338,6 +338,21 @@ + +
  • + + Archives + +
  • + + + + + + + + + @@ -717,10 +732,10 @@

    Using InLang (external service) - + - + diff --git a/docs/images/archived-campaign-metadata.png b/docs/images/archived-campaign-metadata.png new file mode 100644 index 000000000..1fc26c437 Binary files /dev/null and b/docs/images/archived-campaign-metadata.png differ diff --git a/docs/index.html b/docs/index.html index d6bdb1d34..ee789da0f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -13,7 +13,7 @@ - + @@ -378,6 +378,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -616,10 +631,10 @@

    Developers - + - + diff --git a/docs/installation/index.html b/docs/installation/index.html index 14ad61249..cb2333b3d 100644 --- a/docs/installation/index.html +++ b/docs/installation/index.html @@ -15,7 +15,7 @@ - + @@ -481,6 +481,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -929,10 +944,10 @@

    3rd party hosting - + - + diff --git a/docs/messengers/index.html b/docs/messengers/index.html index 7cd141aae..6b4f792f5 100644 --- a/docs/messengers/index.html +++ b/docs/messengers/index.html @@ -12,10 +12,10 @@ - + - + @@ -380,6 +380,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -656,10 +671,10 @@

    Messenger implementations - + - + diff --git a/docs/querying-and-segmentation/index.html b/docs/querying-and-segmentation/index.html index b85be0902..85f97fd4f 100644 --- a/docs/querying-and-segmentation/index.html +++ b/docs/querying-and-segmentation/index.html @@ -15,7 +15,7 @@ - + @@ -435,6 +435,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -816,10 +831,10 @@

    Querying nested attributes - + - + diff --git a/docs/search/search_index.json b/docs/search/search_index.json index ef2ca04ca..4a1c41f59 100644 --- a/docs/search/search_index.json +++ b/docs/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":"

    listmonk is a self-hosted, high performance mailing list and newsletter manager. It comes as a standalone binary and the only dependency is a Postgres database.

    "},{"location":"#developers","title":"Developers","text":"

    listmonk is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the GitHub repository and refer to the developer setup. The backend is written in Go and the frontend is Vue with Buefy for UI.

    "},{"location":"bounces/","title":"Bounce processing","text":"

    Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled.

    "},{"location":"bounces/#pop3-bounce-mailbox","title":"POP3 bounce mailbox","text":"

    Configure the bounce mailbox in Settings -> Bounces. Either the \"From\" e-mail that is set on a campaign (or in settings) should have a POP3 mailbox behind it to receive bounce e-mails, or you should configure a dedicated POP3 mailbox and add that address as the Return-Path (envelope sender) header in Settings -> SMTP -> Custom headers box. For example:

    [\n    {\"Return-Path\": \"your-bounce-inbox@site.com\"}\n]\n

    Some mail servers may also return the bounce to the Reply-To address, which can also be added to the header settings.

    "},{"location":"bounces/#webhook-api","title":"Webhook API","text":"

    The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.

    Method Endpoint Description POST /webhooks/bounce Record a bounce event. Name Data type Required/Optional Description subscriber_uuid String Optional The UUID of the subscriber. Either this or email is required. email String Optional The e-mail of the subscriber. Either this or subscriber_uuid is required. campaign_uuid String Optional UUID of the campaign for which the bounce happened. source String Required A string indicating the source, eg: api, my_script etc. type String Required hard or soft bounce. Currently, this has no effect on how the bounce is treated. meta String Optional An optional escaped JSON string with arbitrary metadata about the bounce event.
    curl -u 'username:password' -X POST localhost:9000/webhooks/bounce \\\n-H \"Content-Type: application/json\" \\\n--data '{\"email\": \"user1@mail.com\", \"campaign_uuid\": \"9f86b50d-5711-41c8-ab03-bc91c43d711b\", \"source\": \"api\", \"type\": \"hard\", \"meta\": \"{\\\"additional\\\": \\\"info\\\"}}'\n
    "},{"location":"bounces/#external-webhooks","title":"External webhooks","text":"

    listmonk supports receiving bounce webhook events from the following SMTP providers.

    Endpoint Description https://listmonk.yoursite.com/webhooks/service/ses Amazon (AWS) SES You can use these Mautic steps as a general guide, but use your listmonk's endpoint instead. * When creating the topic select \"standard\" instead of the preselected \"FIFO\". You can put a name and leave everything else at default. When creating a subscription choose HTTPS for \"Protocol\", and leave \"Enable raw message delivery\" UNCHECKED. * On the \"SES -> verified identities\" page, make sure to check \"include original headers\"*. * The Mautic screenshot suggests you should turn off email feedback forwarding, but that's completely optional depending on whether you want want email notifications. https://listmonk.yoursite.com/webhooks/service/sendgrid Sendgrid / Twilio Signed event webhook More info"},{"location":"concepts/","title":"Concepts","text":""},{"location":"concepts/#subscriber","title":"Subscriber","text":"

    A subscriber is a recipient identified by an e-mail address and name. Subscribers receive e-mails that are sent from listmonk. A subscriber can be added to any number of lists. Subscribers who are not a part of any lists are considered orphan records.

    "},{"location":"concepts/#attributes","title":"Attributes","text":"

    Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be queried and segmented into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:

    {\n\"city\": \"Bengaluru\",\n\"likes_tea\": true,\n\"spoken_languages\": [\"English\", \"Malayalam\"],\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"],\n\"preferred_language\": \"go\"\n}\n}\n
    "},{"location":"concepts/#subscription-statuses","title":"Subscription statuses","text":"

    A subscriber can be added to one or more lists, and each such relationship can have one of these statuses.

    Status Description unconfirmed The subscriber was added to the list directly without their explicit confirmation. Nonetheless, the subscriber will receive campaign messages sent to single optin campaigns. confirmed The subscriber confirmed their subscription by clicking on 'accept' in the confirmation e-mail. Only confirmed subscribers in opt-in lists will receive campaign messages send to the list. unsubscribed The subscriber is unsubscribed from the list and will not receive any campaign messages sent to the list."},{"location":"concepts/#segmentation","title":"Segmentation","text":"

    Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. Learn more.

    "},{"location":"concepts/#list","title":"List","text":"

    A list (or a mailing list) is a collection of subscribers grouped under a name, for instance, clients. Lists are used to organise subscribers and send e-mails to specific groups. A list can be single optin or double optin. Subscribers added to double optin lists have to explicitly accept the subscription by clicking on the confirmation e-mail they receive. Until then, they do not receive campaign messages.

    "},{"location":"concepts/#campaign","title":"Campaign","text":"

    A campaign is an e-mail (or any other kind of messages) that is sent to one or more lists.

    "},{"location":"concepts/#transactional-message","title":"Transactional message","text":"

    A transactional message is an arbitrary message sent to a subscriber using the transactional message API. For example a welcome e-mail on signing up to a service; an order confirmation e-mail on purchasing an item; a password reset e-mail when a user initiates an online account recovery process.

    "},{"location":"concepts/#template","title":"Template","text":"

    A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. Learn more.

    "},{"location":"concepts/#messenger","title":"Messenger","text":"

    listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Learn more.

    "},{"location":"concepts/#tracking-pixel","title":"Tracking pixel","text":"

    The tracking pixel is a tiny, invisible image that is inserted into an e-mail body to track e-mail views. This allows measuring the read rate of e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track reads anonymously without associating an e-mail read to a subscriber.

    "},{"location":"concepts/#click-tracking","title":"Click tracking","text":"

    It is possible to track the clicks on every link that is sent in an e-mail. This allows measuring the clickthrough rates of links in e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track link clicks anonymously without associating an e-mail read to a subscriber.

    "},{"location":"concepts/#bounce","title":"Bounce","text":"

    A bounce occurs when an e-mail that is sent to a recipient \"bounces\" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. listmonk can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. Learn more.

    "},{"location":"configuration/","title":"Configuration","text":""},{"location":"configuration/#toml-configuration-file","title":"TOML Configuration file","text":"

    One or more TOML files can be read by passing --config config.toml multiple times. Apart from a few low level configuration variables and the database configuration, all other settings can be managed from the Settings dashboard on the admin UI.

    To generate a new sample configuration file, run --listmonk --new-config

    "},{"location":"configuration/#environment-variables","title":"Environment variables","text":"

    Variables in config.toml can also be provided as environment variables prefixed by LISTMONK_ with periods replaced by __ (double underscore). Example:

    Environment variable Example value LISTMONK_app__address \"0.0.0.0:9000\" LISTMONK_app__admin_username listmonk LISTMONK_app__admin_password listmonk LISTMONK_db__host db LISTMONK_db__port 9432 LISTMONK_db__user listmonk LISTMONK_db__password listmonk LISTMONK_db__database listmonk LISTMONK_db__ssl_mode disable"},{"location":"configuration/#customizing-system-templates","title":"Customizing system templates","text":"

    Read this

    "},{"location":"configuration/#http-routes","title":"HTTP routes","text":"

    When configuring auth proxies and web application firewalls, use this table.

    "},{"location":"configuration/#private-admin-endpoints","title":"Private admin endpoints.","text":"Methods Route Description * /api/* Admin APIs GET /admin/* Admin UI and HTML pages POST /webhooks/bounce Admin bounce webhook"},{"location":"configuration/#public-endpoints-to-expose-to-the-internet","title":"Public endpoints to expose to the internet.","text":"Methods Route Description GET, POST /subscription/* HTML subscription pages GET, /link/* Tracked link redirection GET /campaign/* Pixel tracking image GET /public/* Static files for HTML subscription pages POST /webhooks/service/* Bounce webhook endpoints for AWS and Sendgrid"},{"location":"configuration/#media-uploads","title":"Media Uploads","text":""},{"location":"configuration/#filesystem","title":"Filesystem","text":"

    When configuring docker volume mounts for using filesystem media uploads, you can follow either of two approaches.

    "},{"location":"configuration/#using-volumes","title":"Using volumes","text":"

    Using docker volumes, you can specify the name of volume and destination for the files to be uploaded inside the container.

    app:\n    volumes:\n      - type: volume\n        source: listmonk-uploads\n        target: /listmonk/uploads\n\nvolumes:\n  listmonk-uploads:\n

    Note

    This volume is managed by docker itself, and you can see find the host path with docker volume inspect listmonk_listmonk-uploads.

    "},{"location":"configuration/#using-bind-mounts","title":"Using bind mounts","text":"
        volumes:\n      - /data/uploads:/listmonk/uploads\n

    The files will be available inside /data/uploads directory on the host machine.

    "},{"location":"developer-setup/","title":"Developer setup","text":"

    The app has two distinct components, the Go backend and the VueJS frontend. In the dev environment, both are run independently.

    "},{"location":"developer-setup/#pre-requisites","title":"Pre-requisites","text":"
    • go
    • nodejs (if you are working on the frontend) and yarn
    • Postgres database. If there is no local installation, the demo docker DB can be used for development (docker-compose up demo-db)
    "},{"location":"developer-setup/#first-time-setup","title":"First time setup","text":"

    git clone https://github.com/knadh/listmonk.git. The project uses go.mod, so it's best to clone it outside the Go src path.

    1. Copy config.toml.sample as config.toml and add your config.
    2. make dist to build the listmonk binary. Once the binary is built, run ./listmonk --install to run the DB setup. For subsequent dev runs, use make run.

    mailhog is an excellent standalone mock SMTP server (with a UI) for testing and dev.

    "},{"location":"developer-setup/#running-the-dev-environment","title":"Running the dev environment","text":"
    1. Run make run to start the listmonk dev server on :9000.
    2. Run make run-frontend to start the Vue frontend in dev mode using yarn on :8080. All /api/* calls are proxied to the app running on :9000. Refer to the frontend README for an overview on how the frontend is structured.
    3. Visit http://localhost:8080
    "},{"location":"developer-setup/#production-build","title":"Production build","text":"

    Run make dist to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, listmonk

    "},{"location":"external-integration/","title":"Integrating with external systems","text":"

    In many environments, a mailing list manager's subscriber database is not run independently but as a part of an existing customer database or a CRM. There are multiple ways of keeping listmonk in sync with external systems.

    "},{"location":"external-integration/#using-apis","title":"Using APIs","text":"

    The subscriber APIs offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.

    "},{"location":"external-integration/#interacting-directly-with-the-db","title":"Interacting directly with the DB","text":"

    listmonk uses tables with simple schemas to represent subscribers (subscribers), lists (lists), and subscriptions (subscriber_lists). It is easy to add, update, and delete subscriber information directly with the database tables for advanced usecases. See the table schemas for more information.

    "},{"location":"i18n/","title":"Internationalization (i18n)","text":"

    listmonk comes available in multiple languages thanks to language packs contributed by volunteers. A language pack is a JSON file with a map of keys and corresponding translations. The bundled languages can be viewed here.

    "},{"location":"i18n/#additional-language-packs","title":"Additional language packs","text":"

    These additional language packs can be downloaded and passed to listmonk with the --i18n-dir flag as described in the next section.

    Language Description Deutsch (formal) German language with formal pronouns"},{"location":"i18n/#customizing-languages","title":"Customizing languages","text":"

    To customize an existing language or to load a new language, put one or more .json language files in a directory, and pass the directory path to listmonk with the--i18n-dir=/path/to/dir flag.

    "},{"location":"i18n/#contributing-a-new-language","title":"Contributing a new language","text":""},{"location":"i18n/#using-the-basic-editor","title":"Using the basic editor","text":"
    • Visit https://listmonk.app/i18n
    • Click on Createa new language, or to make changes to an existing language, use Load language.
    • Translate the text in the text fields on the UI.
    • Once done, use the Download raw JSON to download the language file.
    • Send a pull request to add the file to the i18n directory on the GitHub repo.
    "},{"location":"i18n/#using-inlang-external-service","title":"Using InLang (external service)","text":"
    • Visit https://inlang.com/editor/github.com/knadh/listmonk
    • To make changes and push them, you need to log in to GitHub using OAuth and fork the project from the UI.
    • Translate the text in the input fields on the UI. You can use the filters to see only the necessary translations.
    • Once you're done, push the changes from the UI and click on \"Open a pull request.\" This will take you to GitHub, where you can write a PR message.
    "},{"location":"installation/","title":"Installation","text":"

    listmonk requires Postgres \u2a7e v9.4.

    "},{"location":"installation/#binary","title":"Binary","text":"
    • Download the latest release and extract the listmonk binary.
    • ./listmonk --new-config to generate config.toml. Then, edit the file.
    • ./listmonk --install to install the tables in the Postgres DB.
    • Run ./listmonk and visit http://localhost:9000.
    "},{"location":"installation/#docker","title":"Docker","text":"

    The latest image is available on DockerHub at listmonk/listmonk:latest

    Use the sample docker-compose.yml to run listmonk and Postgres DB with docker-compose as follows:

    "},{"location":"installation/#demo","title":"Demo","text":""},{"location":"installation/#easy-docker-install","title":"Easy Docker install","text":"
    mkdir listmonk-demo\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)\"\n
    "},{"location":"installation/#manual-docker-install","title":"Manual Docker install","text":"
    wget -O docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml\ndocker-compose up -d demo-db demo-app\n

    Warning

    The demo does not persist Postgres after the containers are removed. DO NOT use this demo setup in production.

    "},{"location":"installation/#production","title":"Production","text":""},{"location":"installation/#easy-docker-install_1","title":"Easy Docker install","text":"

    This setup is recommended if you want to quickly setup listmonk in production.

    mkdir listmonk\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)\"\n

    The above shell script performs the following actions:

    • Downloads docker-compose.yml and generates a config.toml.
    • Runs a Postgres container and installs the database schema.
    • Runs the listmonk container.

    Note

    It's recommended to examine the contents of the shell script, before running in your environment.

    "},{"location":"installation/#manual-docker-install_1","title":"Manual Docker install","text":"

    The following workflow is recommended to setup listmonk manually using docker-compose. You are encouraged to customise the contents of docker-compose.yml to your needs. The overall setup looks like:

    • docker-compose up db to run the Postgres DB.
    • docker-compose run --rm app ./listmonk --install to setup the DB (or --upgrade to upgrade an existing DB).
    • Copy config.toml.sample to your directory and make the following changes:
      • app.address => 0.0.0.0:9000 (Port forwarding on Docker will work only if the app is advertising on all interfaces.)
      • db.host => listmonk_db (Container Name of the DB container)
    • Run docker-compose up app and visit http://localhost:9000.
    "},{"location":"installation/#mounting-a-custom-configtoml","title":"Mounting a custom config.toml","text":"

    To mount a local config.toml file, add the following section to docker-compose.yml:

      app:\n    <<: *app-defaults\n    depends_on:\n      - db\n    volumes:\n    - ./path/on/your/host/config.toml:/listmonk/config.toml\n

    Note

    Some common changes done inside config.toml for Docker based setups:

    • Change app.address to 0.0.0.0:9000.
    • Change db.host to listmonk_db.

    Here's a sample config.toml you can use:

    [app]\naddress = \"0.0.0.0:9000\"\nadmin_username = \"listmonk\"\nadmin_password = \"listmonk\"\n\n# Database.\n[db]\nhost = \"listmonk_db\"\nport = 5432\nuser = \"listmonk\"\npassword = \"listmonk\"\ndatabase = \"listmonk\"\nssl_mode = \"disable\"\nmax_open = 25\nmax_idle = 25\nmax_lifetime = \"300s\"\n

    Mount the local config.toml inside the container at listmonk/config.toml.

    Tip

    • See configuring with environment variables for variables like app.admin_password and db.password
    • Ensure that both app and db containers are in running. If the containers are not running, restart them docker-compose restart app db.
    • Refer to this tutorial for setting up a production instance with Docker + Nginx + LetsEncrypt SSL.

    Info

    The example docker-compose.yml file works with Docker Engine 18.06.0+ and docker-compose which supports file format 3.7.

    "},{"location":"installation/#compiling-from-source","title":"Compiling from source","text":"

    To compile the latest unreleased version (master branch):

    1. Make sure go, nodejs, and yarn are installed on your system.
    2. git clone git@github.com:knadh/listmonk.git
    3. cd listmonk && make dist. This will generate the listmonk binary.
    "},{"location":"installation/#release-candidate-rc","title":"Release candidate (RC)","text":"

    The master branch with bleeding edge changes is periodically built and published as listmonk/listmonk:rc on DockerHub. To run the latest pre-release version, replace all instances of listmonk/listmonk:latest with listmonk/listmonk:rc in the docker-compose.yml file and follow the Docker installation steps above. While it is generally safe to run release candidate versions, they may have issues that only get resolved in a general release.

    "},{"location":"installation/#3rd-party-hosting","title":"3rd party hosting","text":"

    Tutorial for deploying on Fly.io

    "},{"location":"messengers/","title":"Messengers","text":"

    listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc.

    A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Messengers are registered in the Settings -> Messengers UI, and can be selected on individual campaigns.

    Messengers support optional BasicAuth authentication. Plain text format for campaign content is ideal for messengers such as SMS and FCM.

    When a campaign starts, listmonk POSTs messages in the following format to the selected messenger's endpoint. The endpoint should return a 200 OK response in case of a successful request.

    The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as subscriber attributes.

    {\n\"subject\": \"Welcome to listmonk\",\n\"body\": \"The message body\",\n\"content_type\": \"plain\",\n\"recipients\": [{\n\"uuid\": \"e44b4135-1e1d-40c5-8a30-0f9a886c2884\",\n\"email\": \"anon@example.com\",\n\"name\": \"Anon Doe\",\n\"attribs\": {\n\"phone\": \"123123123\",\n\"fcm_id\": \"2e7e4b512e7e4b512e7e4b51\",\n\"city\": \"Bengaluru\"\n},\n\"status\": \"enabled\"\n}],\n\"campaign\": {\n\"uuid\": \"2e7e4b51-f31b-418a-a120-e41800cb689f\",\n\"name\": \"Test campaign\",\n\"tags\": [\"test-campaign\"]\n}\n}\n
    "},{"location":"messengers/#messenger-implementations","title":"Messenger implementations","text":"

    Following is a list of HTTP messenger servers that connect to various backends.

    Name Backend listmonk-messenger AWS Pinpoint SMS"},{"location":"querying-and-segmentation/","title":"Querying and segmenting subscribers","text":"

    listmonk allows the writing of partial Postgres SQL expressions to query, filter, and segment subscribers.

    "},{"location":"querying-and-segmentation/#database-fields","title":"Database fields","text":"

    These are the fields in the subscriber database that can be queried.

    Field Description subscribers.uuid The randomly generated unique ID of the subscriber subscribers.email E-mail ID of the subscriber subscribers.name Name of the subscriber subscribers.status Status of the subscriber (enabled, disabled, blocklisted) subscribers.attribs Map of arbitrary attributes represented as JSON. Accessed via the -> and ->> Postgres operator. subscribers.created_at Timestamp when the subscriber was first added subscribers.updated_at Timestamp when the subscriber was modified"},{"location":"querying-and-segmentation/#sample-attributes","title":"Sample attributes","text":"

    Here's a sample JSON map of attributes assigned to an imaginary subscriber.

    {\n\"city\": \"Bengaluru\",\n\"likes_tea\": true,\n\"spoken_languages\": [\"English\", \"Malayalam\"],\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"],\n\"preferred_language\": \"go\"\n}\n}\n

    "},{"location":"querying-and-segmentation/#sample-sql-query-expressions","title":"Sample SQL query expressions","text":""},{"location":"querying-and-segmentation/#find-a-subscriber-by-e-mail","title":"Find a subscriber by e-mail","text":"
    -- Exact match\nsubscribers.email = 'some@domain.com'\n\n-- Partial match to find e-mails that end in @domain.com.\nsubscribers.email LIKE '%@domain.com'\n
    "},{"location":"querying-and-segmentation/#find-a-subscriber-by-name","title":"Find a subscriber by name","text":"
    -- Find all subscribers whose name start with John.\nsubscribers.email LIKE 'John%'\n
    "},{"location":"querying-and-segmentation/#multiple-conditions","title":"Multiple conditions","text":"
    -- Find all Johns who have been blocklisted.\nsubscribers.email LIKE 'John%' AND status = 'blocklisted'\n
    "},{"location":"querying-and-segmentation/#querying-attributes","title":"Querying attributes","text":"
    -- The ->> operator returns the value as text. Find all subscribers\n-- who live in Bengaluru and have done more than 3 projects.\n-- Here 'projects' is cast into an integer so that we can apply the\n-- numerical operator >\nsubscribers.attribs->>'city' = 'Bengaluru' AND\n(subscribers.attribs->>'projects')::INT > 3\n
    "},{"location":"querying-and-segmentation/#querying-nested-attributes","title":"Querying nested attributes","text":"
    -- Find all blocklisted subscribers who like to drink tea, can code Python\n-- and prefer coding Go.\n--\n-- The -> operator returns the value as a structure. Here, the \"languages\" field\n-- The ? operator checks for the existence of a value in a list.\nsubscribers.status = 'blocklisted' AND\n(subscribers.attribs->>'likes_tea')::BOOLEAN = true AND\nsubscribers.attribs->'stack'->'languages' ? 'python' AND\nsubscribers.attribs->'stack'->>'preferred_language' = 'go'\n

    To learn how to write SQL expressions to do advancd querying on JSON attributes, refer to the Postgres JSONB documentation.

    "},{"location":"templating/","title":"Templating","text":"

    A template is a re-usable HTML design that can be used across campaigns and transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates.

    listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. It also integrates 100+ useful Sprig template functions.

    "},{"location":"templating/#campaign-templates","title":"Campaign templates","text":"

    Campaign templates are used in an e-mail campaigns. These template are created and managed on the UI under Campaigns -> Templates, and are selected when creating new campaigns.

    "},{"location":"templating/#transactional-templates","title":"Transactional templates","text":"

    Transactional templates are used for sending arbitrary transactional messages using the transactional API. These template are created and managed on the UI under Campaigns -> Templates.

    "},{"location":"templating/#template-expressions","title":"Template expressions","text":"

    There are several template functions and expressions that can be used in campaign and template bodies. They are written in the form {{ .Subscriber.Email }}, that is, an expression between double curly braces {{ and }}.

    "},{"location":"templating/#subscriber-fields","title":"Subscriber fields","text":"Expression Description {{ .Subscriber.UUID }} The randomly generated unique ID of the subscriber {{ .Subscriber.Email }} E-mail ID of the subscriber {{ .Subscriber.Name }} Name of the subscriber {{ .Subscriber.FirstName }} First name of the subscriber (automatically extracted from the name) {{ .Subscriber.LastName }} Last name of the subscriber (automatically extracted from the name) {{ .Subscriber.Status }} Status of the subscriber (enabled, disabled, blocklisted) {{ .Subscriber.Attribs }} Map of arbitrary attributes. Fields can be accessed with ., eg: .Subscriber.Attribs.city {{ .Subscriber.CreatedAt }} Timestamp when the subscriber was first added {{ .Subscriber.UpdatedAt }} Timestamp when the subscriber was modified Expression Description {{ .Campaign.UUID }} The randomly generated unique ID of the campaign {{ .Campaign.Name }} Internal name of the campaign {{ .Campaign.Subject }} E-mail subject of the campaign {{ .Campaign.FromEmail }} The e-mail address from which the campaign is being sent"},{"location":"templating/#functions","title":"Functions","text":"Function Description {{ Date \"2006-01-01\" }} Prints the current datetime for the given format expressed as a Go date layout {{ TrackLink \"https://link.com\" }} Takes a URL and generates a tracking URL over it. For use in campaign bodies and templates. https://link.com@TrackLink Shorthand for TrackLink. Eg: <a href=\"https://link.com@TrackLink\">Link</a> {{ TrackView }} Inserts a single tracking pixel. Should only be used once, ideally in the template footer. {{ UnsubscribeURL }} Unsubscription and Manage preferences URL. Ideal for use in the template footer. {{ MessageURL }} URL to view the hosted version of an e-mail message. {{ OptinURL }} URL to the double-optin confirmation page. {{ Safe \"<!-- comment -->\" }} Add any HTML code as it is."},{"location":"templating/#sprig-functions","title":"Sprig functions","text":"

    listmonk integrates the Sprig library that offers 100+ utility functions for working with strings, numbers, dates etc. that can be used in templating. Refer to the Sprig documentation for the full list of functions.

    "},{"location":"templating/#example-template","title":"Example template","text":"

    The expression {{ template \"content\" . }} should appear exactly once in every template denoting the spot where an e-mail's content is inserted. Here's a sample HTML e-mail that has a fixed header and footer that inserts the content in the middle.

    <!DOCTYPE html>\n<html>\n  <head>\n    <style>\nbody {\nbackground: #eee;\nfont-family: Arial, sans-serif;\nfont-size: 6px;\ncolor: #111;\n}\nheader {\nborder-bottom: 1px solid #ddd;\npadding-bottom: 30px;\nmargin-bottom: 30px;\n}\n.container {\nbackground: #fff;\nwidth: 450px;\nmargin: 0 auto;\npadding: 30px;\n}\n</style>\n  </head>\n  <body>\n    <section class=\"container\">\n      <header>\n        <!-- This will appear in the header of all e-mails.\n             The subscriber's name will be automatically inserted here. //-->\n        Hi {{ .Subscriber.FirstName }}!\n      </header>\n\n      <!-- This is where the e-mail body will be inserted //-->\n      <div class=\"content\">\n        {{ template \"content\" . }}\n      </div>\n\n      <footer>\n        Copyright 2019. All rights Reserved.\n      </footer>\n\n      <!-- The tracking pixel will be inserted here //-->\n      {{ TrackView }}\n    </section>\n  </body>\n</html>\n

    Info

    For use with plaintext campaigns, create a template with no HTML content and just the placeholder {{ template \"content\" . }}

    "},{"location":"templating/#example-campaign-body","title":"Example campaign body","text":"

    Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of attributes defined, this example shows how to render those values in a campaign.

    Hey, did you notice how the template showed your first name?\nYour last name is {{.Subscriber.LastName }}.\n\nYou have done {{ .Subscriber.Attribs.projects }} projects.\n\n\n{{ if eq .Subscriber.Attribs.city \"Bengaluru\" }}\n  You live in Bangalore!\n{{ else }}\n  Where do you live?\n{{ end }}\n\n\nHere is a link for you to click that will be tracked.\n<a href=\"{{ TrackLink \"https://google.com\" }}\">Google</a>\n

    The above example uses an if condition to show one of two messages depending on the value of a subscriber attribute. Many such dynamic expressions are possible with Go templating expressions.

    "},{"location":"templating/#system-templates","title":"System templates","text":"

    System templates are used for rendering public user facing pages such as the subscription management page, and in automatically generated system e-mails such as the opt-in confirmation e-mail. These are bundled into listmonk but can be customized by copying the static directory locally, and passing its path to listmonk with the ./listmonk --static-dir=your/custom/path flag.

    "},{"location":"templating/#public-pages","title":"Public pages","text":"/static/public/ index.html Base template with the header and footer that all pages use. home.html Landing page on the root domain with the login button. message.html Generic success / failure message page. optin.html Opt-in confirmation page. subscription.html Subscription management page with options for data export and wipe. subscription-form.html List selection and subscription form page.

    To edit the appearance of the public pages using CSS and Javascript, head to Settings > Appearance > Public:

    "},{"location":"templating/#system-e-mails","title":"System e-mails","text":"/static/email-templates/ base.html Base template with the header and footer that all system generated e-mails use. campaign-status.html E-mail notification that is sent to admins on campaign start, completion etc. import-status.html E-mail notification that is sent to admins on finish of an import job. subscriber-data.html E-mail that is sent to subscribers when they request a full dump of their private data. subscriber-optin.html Automatic opt-in confirmation e-mail that is sent to an unconfirmed subscriber when they are added. subscriber-optin-campaign.html E-mail content that's inserted into a campaign body when starting an opt-in campaign from the lists page. default.tpl Default campaign template that is created in Campaigns -> Templates when listmonk is first installed. This is not used after that.

    Info

    To turn system e-mail templates to plaintext, remove <!doctype html> from base.html and remove all HTML tags from the templates while retaining the Go templating code.

    "},{"location":"upgrade/","title":"Upgrade","text":"

    Some versions may require changes to the database. These changes or database \"migrations\" are applied automatically and safely, but, it is recommended to take a backup of the Postgres database before running the --upgrade option, especially if you have made customizations to the database tables.

    "},{"location":"upgrade/#binary","title":"Binary","text":"
    • Download the latest release and extract the listmonk binary.
    • ./listmonk --upgrade to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects.
    • Run ./listmonk and visit http://localhost:9000.
    "},{"location":"upgrade/#docker","title":"Docker","text":"
    • docker-compose pull to pull the latest version from DockerHub.
    • docker-compose run --rm app ./listmonk --upgrade to upgrade an existing DB.
    • Run docker-compose up app db and visit http://localhost:9000.
    "},{"location":"upgrade/#railway","title":"Railway","text":"
    • Head to your dashboard, and select your Listmonk project.
    • Select the GitHub deployment service.
    • In the Deployment tab, head to the latest deployment, click on the three vertical dots to the right, and select \"Redeploy\".
    "},{"location":"apis/apis/","title":"APIs","text":"

    All features that are available on the listmonk dashboard are also available as REST-like HTTP APIs that can be interacted with directly. Request and response bodies are JSON. This allows easy scripting of listmonk and integration with other systems, for instance, synchronisation with external subscriber databases.

    API requests require BasicAuth authentication with the admin credentials.

    The API section is a work in progress. There may be API calls that are yet to be documented. Please consider contributing to docs.

    "},{"location":"apis/apis/#openapi-swagger-spec","title":"OpenAPI (Swagger) spec","text":"

    The auto-generated OpenAPI (Swagger) specification site for the APIs are available at listmonk.app/docs/swagger

    "},{"location":"apis/apis/#response-structure","title":"Response structure","text":""},{"location":"apis/apis/#successful-request","title":"Successful request","text":"
    HTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n\"data\": {}\n}\n

    All responses from the API server are JSON with the content-type application/json unless explicitly stated otherwise. A successful 200 OK response always has a JSON response body with a status key with the value success. The data key contains the full response payload.

    "},{"location":"apis/apis/#failed-request","title":"Failed request","text":"
    HTTP/1.1 500 Server error\nContent-Type: application/json\n\n{\n\"message\": \"Error message\"\n}\n

    A failure response is preceded by the corresponding 40x or 50x HTTP header. There may be an optional data key with additional payload.

    "},{"location":"apis/apis/#timestamps","title":"Timestamps","text":"

    All timestamp fields are in the format 2019-01-01T09:00:00.000000+05:30. The seconds component is suffixed by the milliseconds, followed by the + and the timezone offset.

    "},{"location":"apis/apis/#common-http-error-codes","title":"Common HTTP error codes","text":"code 400 Missing or bad request parameters or values 403 Session expired or invalidate. Must relogin 404 Request resource was not found 405 Request method (GET, POST etc.) is not allowed on the requested endpoint 410 The requested resource is gone permanently 429 Too many requests to the API (rate limiting) 500 Something unexpected went wrong 502 The backend OMS is down and the API is unable to communicate with it 503 Service unavailable; the API is down 504 Gateway timeout; the API is unreachable"},{"location":"apis/campaigns/","title":"API / Campaigns","text":"Method Endpoint Description GET /api/campaigns Gets all campaigns. GET /api/campaigns/:campaign_id Gets a single campaign. GET /api/campaigns/:campaign_id/preview Gets the HTML preview of a campaign body. GET /api/campaigns/running/stats Gets the stats of a given set of campaigns. POST /api/campaigns Creates a new campaign. POST /api/campaigns/:campaign_id/test Posts campaign message to arbitrary subscribers for testing. PUT /api/campaigns/:campaign_id Modifies a campaign. PUT /api/campaigns/:campaign_id/status Start / pause / cancel / schedule a campaign. DELETE /api/campaigns/:campaign_id Deletes a campaign."},{"location":"apis/campaigns/#get-apicampaigns","title":"GET /api/campaigns","text":"

    Gets all campaigns.

    "},{"location":"apis/campaigns/#example-request","title":"Example Request","text":"
     curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns?page=1&per_page=100'\n
    "},{"location":"apis/campaigns/#parameters","title":"Parameters","text":"

    Name | Type | Required/Optional | Description --------|--------------------|-------------|---------------------|--------------------- query | string | Optional | Optional string to search a list by name. order_by | string | Optional | Field to sort results by. name|status|created_at|updated_at order | string | Optional | ASC|DESCSort by ascending or descending order. page | number | Optional | Page number for paginated results. per_page | number | Optional | Results to return per page. Setting this to all skips pagination and returns all results.

    "},{"location":"apis/campaigns/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"CampaignID\": 0,\n\"views\": 0,\n\"clicks\": 0,\n\"lists\": [\n{\n\"id\": 1,\n\"name\": \"Default list\"\n}\n],\n\"started_at\": null,\n\"to_send\": 0,\n\"sent\": 0,\n\"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Welcome to listmonk\",\n\"from_email\": \"No Reply <noreply@yoursite.com>\",\n\"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n\"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n\"status\": \"draft\",\n\"content_type\": \"richtext\",\n\"tags\": [\n\"test-campaign\"\n],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n],\n\"query\": \"\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/campaigns/#get-apicampaignscampaign_id","title":"GET /api/campaigns/:campaign_id","text":"

    Gets a single campaign.

    "},{"location":"apis/campaigns/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign you want to get."},{"location":"apis/campaigns/#example-request_1","title":"Example Request","text":"
    curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1'\n
    "},{"location":"apis/campaigns/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"CampaignID\": 0,\n\"views\": 0,\n\"clicks\": 0,\n\"lists\": [\n{\n\"id\": 1,\n\"name\": \"Default list\"\n}\n],\n\"started_at\": null,\n\"to_send\": 0,\n\"sent\": 0,\n\"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Welcome to listmonk\",\n\"from_email\": \"No Reply <noreply@yoursite.com>\",\n\"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n\"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n\"status\": \"draft\",\n\"content_type\": \"richtext\",\n\"tags\": [\n\"test-campaign\"\n],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n}\n
    "},{"location":"apis/campaigns/#get-apicampaignscampaign_idpreview","title":"GET /api/campaigns/:campaign_id/preview","text":"

    Gets the html preview of a campaign body.

    "},{"location":"apis/campaigns/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign to be previewed."},{"location":"apis/campaigns/#example-request_2","title":"Example Request","text":"
    curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1/preview'\n
    "},{"location":"apis/campaigns/#example-response_2","title":"Example Response","text":"
    <h3>Hi John!</h3>\nThis is a test e-mail campaign. Your second name is Doe and you are from Bengaluru.\n
    "},{"location":"apis/campaigns/#get-apicampaignsrunningstats","title":"GET /api/campaigns/running/stats","text":"

    Gets the running stat of a given set of campaigns.

    "},{"location":"apis/campaigns/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Query Parameters Number Required The id values of the campaigns whose stat you want to get."},{"location":"apis/campaigns/#example-request_3","title":"Example Request","text":"
    curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/running/stats?campaign_id=1'\n
    "},{"location":"apis/campaigns/#example-response_3","title":"Example Response","text":"
    {\n\"data\": []\n}\n
    "},{"location":"apis/campaigns/#post-apicampaigns","title":"POST /api/campaigns","text":"

    Creates a new campaign.

    "},{"location":"apis/campaigns/#parameters_4","title":"Parameters","text":"Name Data type Required/Optional Description name String Required Name of the campaign. subject String Required (E-mail) subject of the campaign. lists []Number Required Array of list IDs to send the campaign to. from_email String Optional From e-mail to show on the campaign e-mails. If left empty, the default value from settings is used. type String Required regular or optin campaign. content_type String Required richtext, html, markdown, plain body String Required Campaign content body. altbody String Optional Alternate plain text body for HTML (and richtext) e-mails. send_at String Optional A timestamp to schedule the campaign at. Eg: 2021-12-25T06:00:00 (YYYY-MM-DDTHH:MM:SS) messenger String Optional email or a custom messenger defined in the settings. If left empty, email is used. template_id Number Optional ID of the template to use. If left empty, the default template is used. tags []String Optional Array of string tags to mark the campaign."},{"location":"apis/campaigns/#example-request_4","title":"Example request","text":"
    curl -u \"username:password\" 'http://localhost:9000/api/campaigns' -X POST -H 'Content-Type: application/json;charset=utf-8' --data-raw '{\"name\":\"Test campaign\",\"subject\":\"Hello, world\",\"lists\":[1],\"from_email\":\"listmonk <noreply@listmonk.yoursite.com>\",\"content_type\":\"richtext\",\"messenger\":\"email\",\"type\":\"regular\",\"tags\":[\"test\"],\"template_id\":1}'\n
    "},{"location":"apis/campaigns/#example-response_4","title":"Example response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2021-12-27T11:50:23.333485Z\",\n\"updated_at\": \"2021-12-27T11:50:23.333485Z\",\n\"views\": 0,\n\"clicks\": 0,\n\"bounces\": 0,\n\"lists\": [{\n\"id\": 1,\n\"name\": \"Default list\"\n}],\n\"started_at\": null,\n\"to_send\": 1,\n\"sent\": 0,\n\"uuid\": \"90c889cc-3728-4064-bbcb-5c1c446633b3\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Hello, world\",\n\"from_email\": \"listmonk \\u003cnoreply@listmonk.yoursite.com\\u003e\",\n\"body\": \"\",\n\"altbody\": null,\n\"send_at\": null,\n\"status\": \"draft\",\n\"content_type\": \"richtext\",\n\"tags\": [\"test\"],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n}\n
    "},{"location":"apis/campaigns/#put-apicampaignscampaign_idstatus","title":"PUT /api/campaigns/:campaign_id/status","text":"

    Modifies a campaign status to start, pause, cancel, or schedule a campaign.

    "},{"location":"apis/campaigns/#parameters_5","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign whose status is to be modified. status Request Body String Required scheduled, running, paused, cancelled."},{"location":"apis/campaigns/#note","title":"Note:","text":"
    • Only \"scheduled\" campaigns can be saved as \"draft\".
    • Only \"draft\" campaigns can be \"scheduled\".
    • Only \"paused\" campaigns and \"draft\" campaigns can be started.
    • Only \"running\" campaigns can be \"cancelled\" and \"paused\".
    "},{"location":"apis/campaigns/#example-request_5","title":"Example Request","text":"
    curl -u \"username:password\" -X PUT 'http://localhost:9000/api/campaigns/1/status' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\"status\":\"scheduled\"}'\n
    "},{"location":"apis/campaigns/#example-response_5","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"updated_at\": \"2020-04-08T19:35:17.331867+01:00\",\n\"CampaignID\": 0,\n\"views\": 0,\n\"clicks\": 0,\n\"lists\": [\n{\n\"id\": 1,\n\"name\": \"Default list\"\n}\n],\n\"started_at\": null,\n\"to_send\": 0,\n\"sent\": 0,\n\"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Welcome to listmonk\",\n\"from_email\": \"No Reply <noreply@yoursite.com>\",\n\"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n\"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n\"status\": \"scheduled\",\n\"content_type\": \"richtext\",\n\"tags\": [\n\"test-campaign\"\n],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n}\n
    "},{"location":"apis/campaigns/#delete-apicampaignscampaign_id","title":"DELETE /api/campaigns/:campaign_id","text":"

    Deletes a campaign, only scheduled campaigns that have not yet been started can be deleted.

    "},{"location":"apis/campaigns/#parameters_6","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign you want to delete."},{"location":"apis/campaigns/#example-request_6","title":"Example Request","text":"
    curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/campaigns/34'\n
    "},{"location":"apis/campaigns/#example-response_6","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/import/","title":"API / Import","text":"Method Endpoint Description GET api/import/subscribers Gets a import statistics. GET api/import/subscribers/logs Get a import statistics . POST api/import/subscribers Upload a ZIP file or CSV file to bulk import subscribers. DELETE api/import/subscribers Stops and deletes a import."},{"location":"apis/import/#get-apiimportsubscribers","title":"GET api/import/subscribers","text":"

    Gets import status.

    "},{"location":"apis/import/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/import/subscribers'\n
    "},{"location":"apis/import/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"name\": \"\",\n\"total\": 0,\n\"imported\": 0,\n\"status\": \"none\"\n}\n}\n
    "},{"location":"apis/import/#get-apiimportsubscriberslogs","title":"GET api/import/subscribers/logs","text":"

    Gets import logs.

    "},{"location":"apis/import/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/import/subscribers/logs'\n
    "},{"location":"apis/import/#example-response_1","title":"Example Response","text":"
    {\n\"data\": \"2020/04/08 21:55:20 processing 'import.csv'\\n2020/04/08 21:55:21 imported finished\\n\"\n}\n
    "},{"location":"apis/import/#post-apiimportsubscribers","title":"POST api/import/subscribers","text":"

    Post a CSV (optionally zipped) file to do a bulk import. The request should be a multipart form POST.

    "},{"location":"apis/import/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description params Request body String Required Stringified JSON with import params file Request body File Required File to upload

    params (JSON string)

        {\n\"mode\": \"subscribe\", // subscribe or blocklist\n\"delim\": \",\",        // delimiter in the uploaded file\n\"lists\":[1],         // array of list IDs to import into\n\"overwrite\": true    // overwrite existing entries or skip them?\n}\n
    "},{"location":"apis/import/#delete-apiimportsubscribers","title":"DELETE api/import/subscribers","text":"

    Stops and deletes an import.

    "},{"location":"apis/import/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/import/subscribers' 
    "},{"location":"apis/import/#example-response_2","title":"Example Response","text":"
    {\n\"data\": {\n\"name\": \"\",\n\"total\": 0,\n\"imported\": 0,\n\"status\": \"none\"\n}\n}\n
    "},{"location":"apis/lists/","title":"API / Lists","text":"Method Endpoint Description GET /api/lists Gets all lists. GET /api/lists/:list_id Gets a single list. POST /api/lists Creates a new list. PUT /api/lists/:list_id Modifies a list. DELETE /api/lists/:list_id Deletes a list."},{"location":"apis/lists/#get-apilists","title":"GET /api/lists","text":"

    Gets lists.

    "},{"location":"apis/lists/#parameters","title":"Parameters","text":"Name Type Required/Optional Description query string Optional Optional string to search a list by name. order_by string Optional Field to sort results by. name|status|created_at|updated_at order string Optional ASC|DESCSort by ascending or descending order. page number Optional Page number for paginated results. per_page number Optional Results to return per page. Setting this to all skips pagination and returns all results."},{"location":"apis/lists/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/lists?page=1&per_page=100'\n
    "},{"location":"apis/lists/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-03-06T22:32:01.118327+01:00\",\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"optin\": \"double\",\n\"tags\": [\n\"test\"\n],\n\"subscriber_count\": 2\n},\n{\n\"id\": 2,\n\"created_at\": \"2020-03-04T21:12:09.555013+01:00\",\n\"updated_at\": \"2020-03-06T22:34:46.405031+01:00\",\n\"uuid\": \"f20a2308-dfb5-4420-a56d-ecf0618a102d\",\n\"name\": \"get\",\n\"type\": \"private\",\n\"optin\": \"single\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n],\n\"total\": 5,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/lists/#get-apilistslist_id","title":"GET /api/lists/:list_id","text":"

    Gets a single list.

    "},{"location":"apis/lists/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description list_id Path parameter number Required The id value of the list you want to get."},{"location":"apis/lists/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/lists/5'\n
    "},{"location":"apis/lists/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 5,\n\"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n\"name\": \"Test list\",\n\"type\": \"public\",\n\"optin\": \"double\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n}\n
    "},{"location":"apis/lists/#post-apilists","title":"POST /api/lists","text":"

    Creates a new list.

    "},{"location":"apis/lists/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description name Request body string Required The new list name. type Request body string Required List type, can be set to private or public. optin Request body string Required single or double optin. tags Request body string[] Optional The tags associated with the list."},{"location":"apis/lists/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X POST 'http://localhost:9000/api/lists'\n
    "},{"location":"apis/lists/#example-response_2","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 5,\n\"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n\"name\": \"Test list\",\n\"type\": \"public\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n}\nnull\n
    "},{"location":"apis/lists/#put-apilistslist_id","title":"PUT /api/lists/list_id","text":"

    Modifies a list.

    "},{"location":"apis/lists/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description list_id Path parameter number Required The id of the list to be modified. name Request body string Optional The name which the old name will be modified to. type Request body string Optional List type, can be set to private or public. optin Request body string Optional single or double optin. tags Request body string[] Optional The tags associated with the list."},{"location":"apis/lists/#example-request_3","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/lists/5' \\\n--form 'name=modified test list' \\\n--form 'type=private'\n
    "},{"location":"apis/lists/#example-response_3","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 5,\n\"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"updated_at\": \"2020-03-07T06:52:15.208075+01:00\",\n\"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n\"name\": \"modified test list\",\n\"type\": \"private\",\n\"optin\": \"single\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n}\n
    "},{"location":"apis/media/","title":"API / Media","text":"Method Endpoint Description GET /api/media Gets an uploaded media file. POST /api/media Uploads a media file. DELETE /api/media/:media_id Deletes uploaded media files."},{"location":"apis/media/#get-apimedia","title":"GET /api/media","text":"

    Gets an uploaded media file.

    "},{"location":"apis/media/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------093715978792575906250298'\n
    "},{"location":"apis/media/#example-response","title":"Example Response","text":"
    {\n\"data\": [\n{\n\"id\": 1,\n\"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n\"filename\": \"Media file\",\n\"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n\"thumb_url\": \"/uploads/image_thumb.jpg\",\n\"uri\": \"/uploads/image.jpg\"\n}\n]\n}\n

    Response definitions The following table describes each item in the response.

    Response item Description Data type data Array of the media file objects, which contains an information about the uploaded media files array id Media file object ID number (int) uuid Media file uuuid string (uuid) filename Name of the media file string created_at Date and time, when the media file object was created String (localDateTime) thumb_uri The thumbnail URI of the media file string uri URI of the media file string"},{"location":"apis/media/#post-apimedia","title":"POST /api/media","text":"

    Uploads a media file.

    "},{"location":"apis/media/#parameters","title":"Parameters","text":"Nam Parameter Type Data Type Required/Optional Description file Request body Media file Required The media file to be uploaded."},{"location":"apis/media/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X POST 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------183679989870526937212428' \\\n--form 'file=@/path/to/image.jpg'\n
    "},{"location":"apis/media/#example-response_1","title":"Example Response","text":"

    {\n\"data\": {\n\"id\": 1,\n\"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n\"filename\": \"Media file\",\n\"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n\"thumb_uri\": \"/uploads/image_thumb.jpg\",\n\"uri\": \"/uploads/image.jpg\"\n}\n}\n
    Response definitions

    Response item Description Data type data True means that the media file was successfully uploaded boolean"},{"location":"apis/media/#delete-apimediamedia_id","title":"DELETE /api/media/:media_id","text":"

    Deletes an uploaded media file.

    "},{"location":"apis/media/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description Media_id Path Parameter Number Required The id of the media file you want to delete."},{"location":"apis/media/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/media/1'\n
    "},{"location":"apis/media/#example-response_2","title":"Example Response","text":"
    {\n\"data\": true\n}\n

    Response definitions

    Response item Description Data type data True means that the media file was successfully deleted boolean"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint Description GET /api/subscribers Gets all subscribers. GET /api/subscribers/:id Gets a single subscriber. GET /api/subscribers/lists/:id Gets subscribers in a list. GET /api/subscribers Gets subscribers in one or more lists. GET /api/subscribers Gets subscribers filtered by an arbitrary SQL expression. POST /api/subscribers Creates a new subscriber. PUT /api/subscribers/lists Modify subscribers' list memberships. PUT /api/subscribers/:id Updates a subscriber by ID. PUT /api/subscribers/:id/blocklist Blocklists a single subscriber. PUT /api/subscribers/blocklist Blocklists one or more subscribers. PUT /api/subscribers/query/blocklist Blocklists subscribers with an arbitrary SQL expression. DELETE /api/subscribers/:id Deletes a single subscriber. DELETE /api/subscribers Deletes one or more subscribers . POST /api/subscribers/query/delete Deletes subscribers with an arbitrary SQL expression."},{"location":"apis/subscribers/#get-apisubscribers","title":"GET /api/subscribers","text":"

    Gets all subscribers.

    "},{"location":"apis/subscribers/#example-request","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers?page=1&per_page=100' 

    To skip pagination and retrieve all records, pass per_page=all.

    "},{"location":"apis/subscribers/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n\"email\": \"john@example.com\",\n\"name\": \"John Doe\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"good\": true,\n\"type\": \"known\"\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\n\"test\"\n],\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n}\n]\n},\n{\n\"id\": 2,\n\"created_at\": \"2020-02-18T21:10:17.218979+01:00\",\n\"updated_at\": \"2020-02-18T21:10:17.218979+01:00\",\n\"uuid\": \"ccf66172-f87f-4509-b7af-e8716f739860\",\n\"email\": \"quadri@example.com\",\n\"name\": \"quadri\",\n\"attribs\": {},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\n\"test\"\n],\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n}\n]\n},\n{\n\"id\": 3,\n\"created_at\": \"2020-02-19T19:10:49.36636+01:00\",\n\"updated_at\": \"2020-02-19T19:10:49.36636+01:00\",\n\"uuid\": \"5d940585-3cc8-4add-b9c5-76efba3c6edd\",\n\"email\": \"sugar@example.com\",\n\"name\": \"sugar\",\n\"attribs\": {},\n\"status\": \"enabled\",\n\"lists\": []\n}\n],\n\"query\": \"\",\n\"total\": 3,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/subscribers/#get-apisubscribersid","title":"GET /api/subscribers/:id","text":"

    Gets a single subscriber.

    "},{"location":"apis/subscribers/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Path parameter Number Required The id value of the subscriber you want to get."},{"location":"apis/subscribers/#example-request_1","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers/1' 
    "},{"location":"apis/subscribers/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n\"email\": \"john@example.com\",\n\"name\": \"John Doe\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"good\": true,\n\"type\": \"known\"\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\n\"test\"\n],\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n}\n]\n}\n}\n
    "},{"location":"apis/subscribers/#get-apisubscribers_1","title":"GET /api/subscribers","text":"

    Gets subscribers in one or more lists.

    "},{"location":"apis/subscribers/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description List_id Request body Number Required ID of the list to fetch subscribers from."},{"location":"apis/subscribers/#example-request_2","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'\n

    To skip pagination and retrieve all records, pass per_page=all.

    "},{"location":"apis/subscribers/#example-response_2","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/subscribers/#get-apisubscribers_2","title":"GET /api/subscribers","text":"

    Gets subscribers with an SQL expression.

    "},{"location":"apis/subscribers/#example-request_3","title":"Example Request","text":"
    curl -X GET 'http://localhost:9000/api/subscribers' \\\n--url-query 'page=1' \\\n--url-query 'per_page=100' \\\n--url-query \"query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'\"\n

    To skip pagination and retrieve all records, pass per_page=all.

    Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

    "},{"location":"apis/subscribers/#example-response_3","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"subscribers.name LIKE 'Test%' AND subscribers.attribs-\\u003e\\u003e'city' = 'Bengaluru'\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/subscribers/#post-apisubscribers","title":"POST /api/subscribers","text":"

    Creates a new subscriber.

    "},{"location":"apis/subscribers/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description email Request body String Required The email address of the new susbcriber. name Request body String Required The name of the new subscriber. status Request body String Required The status of the new subscriber. Can be enabled, disabled or blocklisted. lists Request body Numbers Optional Array of list IDs to subscribe to (marked as unconfirmed by default). attribs Request body json Optional JSON list containing new subscriber's attributes. preconfirm_subscriptions Request body Bool Optional If true, marks subscriptsions as confirmed and no-optin e-mails are sent for double opt-in lists."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \\\n--data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"status\":\"enabled\",\"lists\":[1],\"attribs\":{\"city\":\"Bengaluru\",\"projects\":3,\"stack\":{\"languages\":[\"go\",\"python\"]}}}'\n
    "},{"location":"apis/subscribers/#example-response_4","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 3,\n\"created_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"updated_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"uuid\": \"eb420c55-4cfb-4972-92ba-c93c34ba475d\",\n\"email\": \"subsriber@domain.com\",\n\"name\": \"The Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": { \"languages\": [\"go\", \"python\"] }\n},\n\"status\": \"enabled\",\n\"lists\": [1]\n}\n}\n
    "},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT /api/subscribers/lists","text":"

    Modify subscribers list memberships.

    "},{"location":"apis/subscribers/#parameters_3","title":"Parameters","text":"Name Paramter type Data type Required/Optional Description ids Request body Numbers Required The ids of the subscribers to be modified. action Request body String Required Wether to add, remove, or unsubscribe the users. target_list_ids Request body Numbers Required The ids of the lists to be modified. status Request body String Required for add confirmed, unconfirmed, or unsubscribed status."},{"location":"apis/subscribers/#example-request_5","title":"Example Request","text":"

    To subscribe users 1, 2, and 3 to lists 4, 5, and 6:

    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/subscribers/lists' \\\n--data-raw '{\"ids\": [1, 2, 3], \"action\": \"add\", \"target_list_ids\": [4, 5, 6], \"status\": \"confirmed\"}'\n
    "},{"location":"apis/subscribers/#example-response_5","title":"Example Response","text":"
    {\n\"data\": true\n} 
    "},{"location":"apis/subscribers/#put-apisubscribersid","title":"PUT /api/subscribers/:id","text":"

    Updates a single subscriber.

    "},{"location":"apis/subscribers/#parameters_4","title":"Parameters","text":"

    Parameters are the same as POST /api/subscribers used for subscriber creation.

    Please note that this is a PUT request, so all the parameters have to be set. For example if you don't provide lists, the subscriber will be deleted from all the lists he was previously signed on.

    "},{"location":"apis/subscribers/#put-apisubscribersidblocklist","title":"PUT /api/subscribers/:id/blocklist","text":"

    Blocklists a single subscriber.

    "},{"location":"apis/subscribers/#parameters_5","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Path parameter Number Required The id value of the subscriber you want to blocklist."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
    "},{"location":"apis/subscribers/#example-response_6","title":"Example Response","text":"
    {\n\"data\": true\n} 
    "},{"location":"apis/subscribers/#put-apisubscribersqueryblocklist","title":"PUT /api/subscribers/query/blocklist","text":"

    Blocklists subscribers with an arbitrary sql expression.

    "},{"location":"apis/subscribers/#example-request_7","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/subscribers/query/blocklist' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n

    Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

    "},{"location":"apis/subscribers/#example-response_7","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/subscribers/#delete-apisubscribersid","title":"DELETE /api/subscribers/:id","text":"

    Deletes a single subscriber.

    "},{"location":"apis/subscribers/#parameters_6","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Path parameter Number Required The id of the subscriber you want to delete."},{"location":"apis/subscribers/#example-request_8","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/subscribers/9'\n
    "},{"location":"apis/subscribers/#example-response_8","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/subscribers/#delete-apisubscribers","title":"DELETE /api/subscribers","text":"

    Deletes one or more subscribers.

    "},{"location":"apis/subscribers/#parameters_7","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Query parameters Number Required The id of the subscribers you want to delete."},{"location":"apis/subscribers/#example-request_9","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/subscribers?id=10&id=11'\n
    "},{"location":"apis/subscribers/#example-response_9","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/subscribers/#post-apisubscribersquerydelete","title":"POST /api/subscribers/query/delete","text":"

    Deletes subscribers with an arbitrary SQL expression.

    "},{"location":"apis/subscribers/#example-request_10","title":"Example Request","text":"
    curl -u \"username:username\" -X POST 'http://localhost:9000/api/subscribers/query/delete' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n

    Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

    "},{"location":"apis/subscribers/#example-response_10","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/templates/","title":"API / Templates","text":"Method Endpoint Description GET /api/templates Gets all templates. GET /api/templates/:template_id Gets a single template. GET /api/templates/:template_id/preview Gets the HTML preview of a template. POST /api/templates/preview POST /api/templates Creates a template. PUT /api/templates/:template_id Modifies a template. PUT /api/templates/:template_id/default Sets a template to the default template. DELETE /api/templates/:template_id Deletes a template."},{"location":"apis/templates/#get-apitemplates","title":"GET /api/templates","text":"

    Gets all templates.

    "},{"location":"apis/templates/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/templates'\n
    "},{"location":"apis/templates/#example-response","title":"Example Response","text":"
    {\n\"data\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"name\": \"Default template\",\n\"body\": \"{{ template \\\"content\\\" . }}\",\n\"type\": \"campaign\",\n\"is_default\": true\n}\n]\n}\n
    "},{"location":"apis/templates/#get-apitemplatestemplate_id","title":"GET /api/templates/:template_id","text":"

    Gets a single template.

    "},{"location":"apis/templates/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template you want to get."},{"location":"apis/templates/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/templates/1'\n
    "},{"location":"apis/templates/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"name\": \"Default template\",\n\"body\": \"{{ template \\\"content\\\" . }}\",\n\"type\": \"campaign\",\n\"is_default\": true\n}\n}\n
    "},{"location":"apis/templates/#get-apitemplatestemplate_idpreview","title":"GET /api/templates/:template_id/preview","text":"

    Gets the HTML preview of a template body.

    "},{"location":"apis/templates/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template whose html preview you want to get."},{"location":"apis/templates/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/templates/1/preview'\n
    "},{"location":"apis/templates/#example-response_2","title":"Example Response","text":"
    <p>Hi there</p>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et elit ac elit sollicitudin condimentum non a magna.\n    Sed tempor mauris in facilisis vehicula. Aenean nisl urna, accumsan ac tincidunt vitae, interdum cursus massa.\n    Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam varius turpis et turpis lacinia placerat.\n    Aenean id ligula a orci lacinia blandit at eu felis. Phasellus vel lobortis lacus. Suspendisse leo elit, luctus sed\n    erat ut, venenatis fermentum ipsum. Donec bibendum neque quis.</p>\n\n<h3>Sub heading</h3>\n<p>Nam luctus dui non placerat mattis. Morbi non accumsan orci, vel interdum urna. Duis faucibus id nunc ut euismod.\n    Curabitur et eros id erat feugiat fringilla in eget neque. Aliquam accumsan cursus eros sed faucibus.</p>\n\n<p>Here is a link to <a href=\"https://listmonk.app\" target=\"_blank\">listmonk</a>.</p>\n
    "},{"location":"apis/templates/#put-apitemplatestemplate_iddefault","title":"PUT /api/templates/:template_id/default","text":"

    Sets a template to the default template.

    "},{"location":"apis/templates/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template you want to set to the default template."},{"location":"apis/templates/#example-request_3","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/templates/1/default'\n
    "},{"location":"apis/templates/#example-response_3","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"name\": \"Default template\",\n\"body\": \"{{ template \\\"content\\\" . }}\",\n\"type\": \"campaign\",\n\"is_default\": true\n}\n}\n
    "},{"location":"apis/templates/#delete-apitemplatestemplate_id","title":"DELETE /api/templates/:template_id","text":"

    Deletes a template.

    "},{"location":"apis/templates/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template you want to delete."},{"location":"apis/templates/#example-request_4","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/templates/35'\n
    "},{"location":"apis/templates/#example-response_4","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/transactional/","title":"API / Transactional","text":"Method Endpoint Description POST /api/tx"},{"location":"apis/transactional/#post-apitx","title":"POST /api/tx","text":"

    Send a transactional message to a subscriber using a predefined transactional template.

    "},{"location":"apis/transactional/#parameters","title":"Parameters","text":"Name Data Type Optional Description subscriber_email String Optional E-mail of the subscriber. Either this or subscriber_id should be passed. subscriber_id Number Optional ID of the subscriber. Either this or subscriber_email should be passed. template_id Number Required ID of the transactional template to use in the message. from_email String Optional Optional from email. eg: Company <email@company.com> data Map Optional Optional data in {} nested map. Available in the template as {{ .Tx.Data.* }} headers []Map Optional Optional array of mail headers. [{\"key\": \"value\"}, {\"key\": \"value\"}] messenger String Optional Messenger to use to send the message. Default value is email. content_type String Optional html, markdown, plain"},{"location":"apis/transactional/#request","title":"Request","text":"
    curl -u \"username:password\" \"http://localhost:9000/api/tx\" -X POST \\\n-H 'Content-Type: application/json; charset=utf-8' \\\n--data-binary @- << EOF\n    {\n        \"subscriber_email\": \"user@test.com\",\n        \"template_id\": 2,\n        \"data\": {\"order_id\": \"1234\", \"date\": \"2022-07-30\", \"items\": [1, 2, 3]},\n        \"content_type\": \"html\"\n    }\nEOF\n
    "},{"location":"apis/transactional/#response","title":"Response","text":"
    {\n\"data\": true\n}\n
    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":"

    listmonk is a self-hosted, high performance mailing list and newsletter manager. It comes as a standalone binary and the only dependency is a Postgres database.

    "},{"location":"#developers","title":"Developers","text":"

    listmonk is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the GitHub repository and refer to the developer setup. The backend is written in Go and the frontend is Vue with Buefy for UI.

    "},{"location":"archives/","title":"Archives","text":"

    A global public archive is maintained on the public web interface. It can be enabled under Settings -> Settings -> General -> Enable public mailing list archive.

    To make a campaign available in the public archive (provided it has been enabled in the settings as described above), enable the option 'Publish to public archive' under Campaigns -> Create new -> Archive.

    When using template variables that depend on subscriber data (such as any template variable referencing .Subscriber), such data must be supplied as 'Campaign metadata', which is a JSON object that will be used in place of .Subscriber when rendering the archive template and content.

    When individual subscriber tracking is enabled, TrackLink requires that a UUID of an existing user is provided as part of the campaign metadata. Any clicks on a TrackLink from the archived campaign will be counted towards that subscriber.

    As an example:

    {\n\"UUID\": \"5a837423-a186-5623-9a87-82691cbe3631\",\n\"email\": \"example@example.com\",\n\"name\": \"Reader\",\n\"attribs\": {}\n}\n

    "},{"location":"bounces/","title":"Bounce processing","text":"

    Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled.

    "},{"location":"bounces/#pop3-bounce-mailbox","title":"POP3 bounce mailbox","text":"

    Configure the bounce mailbox in Settings -> Bounces. Either the \"From\" e-mail that is set on a campaign (or in settings) should have a POP3 mailbox behind it to receive bounce e-mails, or you should configure a dedicated POP3 mailbox and add that address as the Return-Path (envelope sender) header in Settings -> SMTP -> Custom headers box. For example:

    [\n    {\"Return-Path\": \"your-bounce-inbox@site.com\"}\n]\n

    Some mail servers may also return the bounce to the Reply-To address, which can also be added to the header settings.

    "},{"location":"bounces/#webhook-api","title":"Webhook API","text":"

    The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.

    Method Endpoint Description POST /webhooks/bounce Record a bounce event. Name Data type Required/Optional Description subscriber_uuid String Optional The UUID of the subscriber. Either this or email is required. email String Optional The e-mail of the subscriber. Either this or subscriber_uuid is required. campaign_uuid String Optional UUID of the campaign for which the bounce happened. source String Required A string indicating the source, eg: api, my_script etc. type String Required hard or soft bounce. Currently, this has no effect on how the bounce is treated. meta String Optional An optional escaped JSON string with arbitrary metadata about the bounce event.
    curl -u 'username:password' -X POST localhost:9000/webhooks/bounce \\\n-H \"Content-Type: application/json\" \\\n--data '{\"email\": \"user1@mail.com\", \"campaign_uuid\": \"9f86b50d-5711-41c8-ab03-bc91c43d711b\", \"source\": \"api\", \"type\": \"hard\", \"meta\": \"{\\\"additional\\\": \\\"info\\\"}}'\n
    "},{"location":"bounces/#external-webhooks","title":"External webhooks","text":"

    listmonk supports receiving bounce webhook events from the following SMTP providers.

    Endpoint Description https://listmonk.yoursite.com/webhooks/service/ses Amazon (AWS) SES You can use these Mautic steps as a general guide, but use your listmonk's endpoint instead. * When creating the topic select \"standard\" instead of the preselected \"FIFO\". You can put a name and leave everything else at default. When creating a subscription choose HTTPS for \"Protocol\", and leave \"Enable raw message delivery\" UNCHECKED. * On the \"SES -> verified identities\" page, make sure to check \"include original headers\"*. * The Mautic screenshot suggests you should turn off email feedback forwarding, but that's completely optional depending on whether you want want email notifications. https://listmonk.yoursite.com/webhooks/service/sendgrid Sendgrid / Twilio Signed event webhook More info"},{"location":"concepts/","title":"Concepts","text":""},{"location":"concepts/#subscriber","title":"Subscriber","text":"

    A subscriber is a recipient identified by an e-mail address and name. Subscribers receive e-mails that are sent from listmonk. A subscriber can be added to any number of lists. Subscribers who are not a part of any lists are considered orphan records.

    "},{"location":"concepts/#attributes","title":"Attributes","text":"

    Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be queried and segmented into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:

    {\n\"city\": \"Bengaluru\",\n\"likes_tea\": true,\n\"spoken_languages\": [\"English\", \"Malayalam\"],\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"],\n\"preferred_language\": \"go\"\n}\n}\n
    "},{"location":"concepts/#subscription-statuses","title":"Subscription statuses","text":"

    A subscriber can be added to one or more lists, and each such relationship can have one of these statuses.

    Status Description unconfirmed The subscriber was added to the list directly without their explicit confirmation. Nonetheless, the subscriber will receive campaign messages sent to single optin campaigns. confirmed The subscriber confirmed their subscription by clicking on 'accept' in the confirmation e-mail. Only confirmed subscribers in opt-in lists will receive campaign messages send to the list. unsubscribed The subscriber is unsubscribed from the list and will not receive any campaign messages sent to the list."},{"location":"concepts/#segmentation","title":"Segmentation","text":"

    Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. Learn more.

    "},{"location":"concepts/#list","title":"List","text":"

    A list (or a mailing list) is a collection of subscribers grouped under a name, for instance, clients. Lists are used to organise subscribers and send e-mails to specific groups. A list can be single optin or double optin. Subscribers added to double optin lists have to explicitly accept the subscription by clicking on the confirmation e-mail they receive. Until then, they do not receive campaign messages.

    "},{"location":"concepts/#campaign","title":"Campaign","text":"

    A campaign is an e-mail (or any other kind of messages) that is sent to one or more lists.

    "},{"location":"concepts/#transactional-message","title":"Transactional message","text":"

    A transactional message is an arbitrary message sent to a subscriber using the transactional message API. For example a welcome e-mail on signing up to a service; an order confirmation e-mail on purchasing an item; a password reset e-mail when a user initiates an online account recovery process.

    "},{"location":"concepts/#template","title":"Template","text":"

    A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. Learn more.

    "},{"location":"concepts/#messenger","title":"Messenger","text":"

    listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Learn more.

    "},{"location":"concepts/#tracking-pixel","title":"Tracking pixel","text":"

    The tracking pixel is a tiny, invisible image that is inserted into an e-mail body to track e-mail views. This allows measuring the read rate of e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track reads anonymously without associating an e-mail read to a subscriber.

    "},{"location":"concepts/#click-tracking","title":"Click tracking","text":"

    It is possible to track the clicks on every link that is sent in an e-mail. This allows measuring the clickthrough rates of links in e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track link clicks anonymously without associating an e-mail read to a subscriber.

    "},{"location":"concepts/#bounce","title":"Bounce","text":"

    A bounce occurs when an e-mail that is sent to a recipient \"bounces\" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. listmonk can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. Learn more.

    "},{"location":"configuration/","title":"Configuration","text":""},{"location":"configuration/#toml-configuration-file","title":"TOML Configuration file","text":"

    One or more TOML files can be read by passing --config config.toml multiple times. Apart from a few low level configuration variables and the database configuration, all other settings can be managed from the Settings dashboard on the admin UI.

    To generate a new sample configuration file, run --listmonk --new-config

    "},{"location":"configuration/#environment-variables","title":"Environment variables","text":"

    Variables in config.toml can also be provided as environment variables prefixed by LISTMONK_ with periods replaced by __ (double underscore). Example:

    Environment variable Example value LISTMONK_app__address \"0.0.0.0:9000\" LISTMONK_app__admin_username listmonk LISTMONK_app__admin_password listmonk LISTMONK_db__host db LISTMONK_db__port 9432 LISTMONK_db__user listmonk LISTMONK_db__password listmonk LISTMONK_db__database listmonk LISTMONK_db__ssl_mode disable"},{"location":"configuration/#customizing-system-templates","title":"Customizing system templates","text":"

    Read this

    "},{"location":"configuration/#http-routes","title":"HTTP routes","text":"

    When configuring auth proxies and web application firewalls, use this table.

    "},{"location":"configuration/#private-admin-endpoints","title":"Private admin endpoints.","text":"Methods Route Description * /api/* Admin APIs GET /admin/* Admin UI and HTML pages POST /webhooks/bounce Admin bounce webhook"},{"location":"configuration/#public-endpoints-to-expose-to-the-internet","title":"Public endpoints to expose to the internet.","text":"Methods Route Description GET, POST /subscription/* HTML subscription pages GET, /link/* Tracked link redirection GET /campaign/* Pixel tracking image GET /public/* Static files for HTML subscription pages POST /webhooks/service/* Bounce webhook endpoints for AWS and Sendgrid"},{"location":"configuration/#media-uploads","title":"Media Uploads","text":""},{"location":"configuration/#filesystem","title":"Filesystem","text":"

    When configuring docker volume mounts for using filesystem media uploads, you can follow either of two approaches.

    "},{"location":"configuration/#using-volumes","title":"Using volumes","text":"

    Using docker volumes, you can specify the name of volume and destination for the files to be uploaded inside the container.

    app:\n    volumes:\n      - type: volume\n        source: listmonk-uploads\n        target: /listmonk/uploads\n\nvolumes:\n  listmonk-uploads:\n

    Note

    This volume is managed by docker itself, and you can see find the host path with docker volume inspect listmonk_listmonk-uploads.

    "},{"location":"configuration/#using-bind-mounts","title":"Using bind mounts","text":"
        volumes:\n      - /data/uploads:/listmonk/uploads\n

    The files will be available inside /data/uploads directory on the host machine.

    "},{"location":"developer-setup/","title":"Developer setup","text":"

    The app has two distinct components, the Go backend and the VueJS frontend. In the dev environment, both are run independently.

    "},{"location":"developer-setup/#pre-requisites","title":"Pre-requisites","text":"
    • go
    • nodejs (if you are working on the frontend) and yarn
    • Postgres database. If there is no local installation, the demo docker DB can be used for development (docker-compose up demo-db)
    "},{"location":"developer-setup/#first-time-setup","title":"First time setup","text":"

    git clone https://github.com/knadh/listmonk.git. The project uses go.mod, so it's best to clone it outside the Go src path.

    1. Copy config.toml.sample as config.toml and add your config.
    2. make dist to build the listmonk binary. Once the binary is built, run ./listmonk --install to run the DB setup. For subsequent dev runs, use make run.

    mailhog is an excellent standalone mock SMTP server (with a UI) for testing and dev.

    "},{"location":"developer-setup/#running-the-dev-environment","title":"Running the dev environment","text":"
    1. Run make run to start the listmonk dev server on :9000.
    2. Run make run-frontend to start the Vue frontend in dev mode using yarn on :8080. All /api/* calls are proxied to the app running on :9000. Refer to the frontend README for an overview on how the frontend is structured.
    3. Visit http://localhost:8080
    "},{"location":"developer-setup/#production-build","title":"Production build","text":"

    Run make dist to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, listmonk

    "},{"location":"external-integration/","title":"Integrating with external systems","text":"

    In many environments, a mailing list manager's subscriber database is not run independently but as a part of an existing customer database or a CRM. There are multiple ways of keeping listmonk in sync with external systems.

    "},{"location":"external-integration/#using-apis","title":"Using APIs","text":"

    The subscriber APIs offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.

    "},{"location":"external-integration/#interacting-directly-with-the-db","title":"Interacting directly with the DB","text":"

    listmonk uses tables with simple schemas to represent subscribers (subscribers), lists (lists), and subscriptions (subscriber_lists). It is easy to add, update, and delete subscriber information directly with the database tables for advanced usecases. See the table schemas for more information.

    "},{"location":"i18n/","title":"Internationalization (i18n)","text":"

    listmonk comes available in multiple languages thanks to language packs contributed by volunteers. A language pack is a JSON file with a map of keys and corresponding translations. The bundled languages can be viewed here.

    "},{"location":"i18n/#additional-language-packs","title":"Additional language packs","text":"

    These additional language packs can be downloaded and passed to listmonk with the --i18n-dir flag as described in the next section.

    Language Description Deutsch (formal) German language with formal pronouns"},{"location":"i18n/#customizing-languages","title":"Customizing languages","text":"

    To customize an existing language or to load a new language, put one or more .json language files in a directory, and pass the directory path to listmonk with the--i18n-dir=/path/to/dir flag.

    "},{"location":"i18n/#contributing-a-new-language","title":"Contributing a new language","text":""},{"location":"i18n/#using-the-basic-editor","title":"Using the basic editor","text":"
    • Visit https://listmonk.app/i18n
    • Click on Createa new language, or to make changes to an existing language, use Load language.
    • Translate the text in the text fields on the UI.
    • Once done, use the Download raw JSON to download the language file.
    • Send a pull request to add the file to the i18n directory on the GitHub repo.
    "},{"location":"i18n/#using-inlang-external-service","title":"Using InLang (external service)","text":"
    • Visit https://inlang.com/editor/github.com/knadh/listmonk
    • To make changes and push them, you need to log in to GitHub using OAuth and fork the project from the UI.
    • Translate the text in the input fields on the UI. You can use the filters to see only the necessary translations.
    • Once you're done, push the changes from the UI and click on \"Open a pull request.\" This will take you to GitHub, where you can write a PR message.
    "},{"location":"installation/","title":"Installation","text":"

    listmonk requires Postgres \u2a7e v9.4.

    "},{"location":"installation/#binary","title":"Binary","text":"
    • Download the latest release and extract the listmonk binary.
    • ./listmonk --new-config to generate config.toml. Then, edit the file.
    • ./listmonk --install to install the tables in the Postgres DB.
    • Run ./listmonk and visit http://localhost:9000.
    "},{"location":"installation/#docker","title":"Docker","text":"

    The latest image is available on DockerHub at listmonk/listmonk:latest

    Use the sample docker-compose.yml to run listmonk and Postgres DB with docker-compose as follows:

    "},{"location":"installation/#demo","title":"Demo","text":""},{"location":"installation/#easy-docker-install","title":"Easy Docker install","text":"
    mkdir listmonk-demo\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)\"\n
    "},{"location":"installation/#manual-docker-install","title":"Manual Docker install","text":"
    wget -O docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml\ndocker-compose up -d demo-db demo-app\n

    Warning

    The demo does not persist Postgres after the containers are removed. DO NOT use this demo setup in production.

    "},{"location":"installation/#production","title":"Production","text":""},{"location":"installation/#easy-docker-install_1","title":"Easy Docker install","text":"

    This setup is recommended if you want to quickly setup listmonk in production.

    mkdir listmonk\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)\"\n

    The above shell script performs the following actions:

    • Downloads docker-compose.yml and generates a config.toml.
    • Runs a Postgres container and installs the database schema.
    • Runs the listmonk container.

    Note

    It's recommended to examine the contents of the shell script, before running in your environment.

    "},{"location":"installation/#manual-docker-install_1","title":"Manual Docker install","text":"

    The following workflow is recommended to setup listmonk manually using docker-compose. You are encouraged to customise the contents of docker-compose.yml to your needs. The overall setup looks like:

    • docker-compose up db to run the Postgres DB.
    • docker-compose run --rm app ./listmonk --install to setup the DB (or --upgrade to upgrade an existing DB).
    • Copy config.toml.sample to your directory and make the following changes:
      • app.address => 0.0.0.0:9000 (Port forwarding on Docker will work only if the app is advertising on all interfaces.)
      • db.host => listmonk_db (Container Name of the DB container)
    • Run docker-compose up app and visit http://localhost:9000.
    "},{"location":"installation/#mounting-a-custom-configtoml","title":"Mounting a custom config.toml","text":"

    To mount a local config.toml file, add the following section to docker-compose.yml:

      app:\n    <<: *app-defaults\n    depends_on:\n      - db\n    volumes:\n    - ./path/on/your/host/config.toml:/listmonk/config.toml\n

    Note

    Some common changes done inside config.toml for Docker based setups:

    • Change app.address to 0.0.0.0:9000.
    • Change db.host to listmonk_db.

    Here's a sample config.toml you can use:

    [app]\naddress = \"0.0.0.0:9000\"\nadmin_username = \"listmonk\"\nadmin_password = \"listmonk\"\n\n# Database.\n[db]\nhost = \"listmonk_db\"\nport = 5432\nuser = \"listmonk\"\npassword = \"listmonk\"\ndatabase = \"listmonk\"\nssl_mode = \"disable\"\nmax_open = 25\nmax_idle = 25\nmax_lifetime = \"300s\"\n

    Mount the local config.toml inside the container at listmonk/config.toml.

    Tip

    • See configuring with environment variables for variables like app.admin_password and db.password
    • Ensure that both app and db containers are in running. If the containers are not running, restart them docker-compose restart app db.
    • Refer to this tutorial for setting up a production instance with Docker + Nginx + LetsEncrypt SSL.

    Info

    The example docker-compose.yml file works with Docker Engine 18.06.0+ and docker-compose which supports file format 3.7.

    "},{"location":"installation/#compiling-from-source","title":"Compiling from source","text":"

    To compile the latest unreleased version (master branch):

    1. Make sure go, nodejs, and yarn are installed on your system.
    2. git clone git@github.com:knadh/listmonk.git
    3. cd listmonk && make dist. This will generate the listmonk binary.
    "},{"location":"installation/#release-candidate-rc","title":"Release candidate (RC)","text":"

    The master branch with bleeding edge changes is periodically built and published as listmonk/listmonk:rc on DockerHub. To run the latest pre-release version, replace all instances of listmonk/listmonk:latest with listmonk/listmonk:rc in the docker-compose.yml file and follow the Docker installation steps above. While it is generally safe to run release candidate versions, they may have issues that only get resolved in a general release.

    "},{"location":"installation/#3rd-party-hosting","title":"3rd party hosting","text":"

    Tutorial for deploying on Fly.io

    "},{"location":"messengers/","title":"Messengers","text":"

    listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc.

    A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Messengers are registered in the Settings -> Messengers UI, and can be selected on individual campaigns.

    Messengers support optional BasicAuth authentication. Plain text format for campaign content is ideal for messengers such as SMS and FCM.

    When a campaign starts, listmonk POSTs messages in the following format to the selected messenger's endpoint. The endpoint should return a 200 OK response in case of a successful request.

    The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as subscriber attributes.

    {\n\"subject\": \"Welcome to listmonk\",\n\"body\": \"The message body\",\n\"content_type\": \"plain\",\n\"recipients\": [{\n\"uuid\": \"e44b4135-1e1d-40c5-8a30-0f9a886c2884\",\n\"email\": \"anon@example.com\",\n\"name\": \"Anon Doe\",\n\"attribs\": {\n\"phone\": \"123123123\",\n\"fcm_id\": \"2e7e4b512e7e4b512e7e4b51\",\n\"city\": \"Bengaluru\"\n},\n\"status\": \"enabled\"\n}],\n\"campaign\": {\n\"uuid\": \"2e7e4b51-f31b-418a-a120-e41800cb689f\",\n\"name\": \"Test campaign\",\n\"tags\": [\"test-campaign\"]\n}\n}\n
    "},{"location":"messengers/#messenger-implementations","title":"Messenger implementations","text":"

    Following is a list of HTTP messenger servers that connect to various backends.

    Name Backend listmonk-messenger AWS Pinpoint SMS"},{"location":"querying-and-segmentation/","title":"Querying and segmenting subscribers","text":"

    listmonk allows the writing of partial Postgres SQL expressions to query, filter, and segment subscribers.

    "},{"location":"querying-and-segmentation/#database-fields","title":"Database fields","text":"

    These are the fields in the subscriber database that can be queried.

    Field Description subscribers.uuid The randomly generated unique ID of the subscriber subscribers.email E-mail ID of the subscriber subscribers.name Name of the subscriber subscribers.status Status of the subscriber (enabled, disabled, blocklisted) subscribers.attribs Map of arbitrary attributes represented as JSON. Accessed via the -> and ->> Postgres operator. subscribers.created_at Timestamp when the subscriber was first added subscribers.updated_at Timestamp when the subscriber was modified"},{"location":"querying-and-segmentation/#sample-attributes","title":"Sample attributes","text":"

    Here's a sample JSON map of attributes assigned to an imaginary subscriber.

    {\n\"city\": \"Bengaluru\",\n\"likes_tea\": true,\n\"spoken_languages\": [\"English\", \"Malayalam\"],\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"],\n\"preferred_language\": \"go\"\n}\n}\n

    "},{"location":"querying-and-segmentation/#sample-sql-query-expressions","title":"Sample SQL query expressions","text":""},{"location":"querying-and-segmentation/#find-a-subscriber-by-e-mail","title":"Find a subscriber by e-mail","text":"
    -- Exact match\nsubscribers.email = 'some@domain.com'\n\n-- Partial match to find e-mails that end in @domain.com.\nsubscribers.email LIKE '%@domain.com'\n
    "},{"location":"querying-and-segmentation/#find-a-subscriber-by-name","title":"Find a subscriber by name","text":"
    -- Find all subscribers whose name start with John.\nsubscribers.email LIKE 'John%'\n
    "},{"location":"querying-and-segmentation/#multiple-conditions","title":"Multiple conditions","text":"
    -- Find all Johns who have been blocklisted.\nsubscribers.email LIKE 'John%' AND status = 'blocklisted'\n
    "},{"location":"querying-and-segmentation/#querying-attributes","title":"Querying attributes","text":"
    -- The ->> operator returns the value as text. Find all subscribers\n-- who live in Bengaluru and have done more than 3 projects.\n-- Here 'projects' is cast into an integer so that we can apply the\n-- numerical operator >\nsubscribers.attribs->>'city' = 'Bengaluru' AND\n(subscribers.attribs->>'projects')::INT > 3\n
    "},{"location":"querying-and-segmentation/#querying-nested-attributes","title":"Querying nested attributes","text":"
    -- Find all blocklisted subscribers who like to drink tea, can code Python\n-- and prefer coding Go.\n--\n-- The -> operator returns the value as a structure. Here, the \"languages\" field\n-- The ? operator checks for the existence of a value in a list.\nsubscribers.status = 'blocklisted' AND\n(subscribers.attribs->>'likes_tea')::BOOLEAN = true AND\nsubscribers.attribs->'stack'->'languages' ? 'python' AND\nsubscribers.attribs->'stack'->>'preferred_language' = 'go'\n

    To learn how to write SQL expressions to do advancd querying on JSON attributes, refer to the Postgres JSONB documentation.

    "},{"location":"templating/","title":"Templating","text":"

    A template is a re-usable HTML design that can be used across campaigns and transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates.

    listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. It also integrates 100+ useful Sprig template functions.

    "},{"location":"templating/#campaign-templates","title":"Campaign templates","text":"

    Campaign templates are used in an e-mail campaigns. These template are created and managed on the UI under Campaigns -> Templates, and are selected when creating new campaigns.

    "},{"location":"templating/#transactional-templates","title":"Transactional templates","text":"

    Transactional templates are used for sending arbitrary transactional messages using the transactional API. These template are created and managed on the UI under Campaigns -> Templates.

    "},{"location":"templating/#template-expressions","title":"Template expressions","text":"

    There are several template functions and expressions that can be used in campaign and template bodies. They are written in the form {{ .Subscriber.Email }}, that is, an expression between double curly braces {{ and }}.

    "},{"location":"templating/#subscriber-fields","title":"Subscriber fields","text":"Expression Description {{ .Subscriber.UUID }} The randomly generated unique ID of the subscriber {{ .Subscriber.Email }} E-mail ID of the subscriber {{ .Subscriber.Name }} Name of the subscriber {{ .Subscriber.FirstName }} First name of the subscriber (automatically extracted from the name) {{ .Subscriber.LastName }} Last name of the subscriber (automatically extracted from the name) {{ .Subscriber.Status }} Status of the subscriber (enabled, disabled, blocklisted) {{ .Subscriber.Attribs }} Map of arbitrary attributes. Fields can be accessed with ., eg: .Subscriber.Attribs.city {{ .Subscriber.CreatedAt }} Timestamp when the subscriber was first added {{ .Subscriber.UpdatedAt }} Timestamp when the subscriber was modified Expression Description {{ .Campaign.UUID }} The randomly generated unique ID of the campaign {{ .Campaign.Name }} Internal name of the campaign {{ .Campaign.Subject }} E-mail subject of the campaign {{ .Campaign.FromEmail }} The e-mail address from which the campaign is being sent"},{"location":"templating/#functions","title":"Functions","text":"Function Description {{ Date \"2006-01-01\" }} Prints the current datetime for the given format expressed as a Go date layout {{ TrackLink \"https://link.com\" }} Takes a URL and generates a tracking URL over it. For use in campaign bodies and templates. https://link.com@TrackLink Shorthand for TrackLink. Eg: <a href=\"https://link.com@TrackLink\">Link</a> {{ TrackView }} Inserts a single tracking pixel. Should only be used once, ideally in the template footer. {{ UnsubscribeURL }} Unsubscription and Manage preferences URL. Ideal for use in the template footer. {{ MessageURL }} URL to view the hosted version of an e-mail message. {{ OptinURL }} URL to the double-optin confirmation page. {{ Safe \"<!-- comment -->\" }} Add any HTML code as it is."},{"location":"templating/#sprig-functions","title":"Sprig functions","text":"

    listmonk integrates the Sprig library that offers 100+ utility functions for working with strings, numbers, dates etc. that can be used in templating. Refer to the Sprig documentation for the full list of functions.

    "},{"location":"templating/#example-template","title":"Example template","text":"

    The expression {{ template \"content\" . }} should appear exactly once in every template denoting the spot where an e-mail's content is inserted. Here's a sample HTML e-mail that has a fixed header and footer that inserts the content in the middle.

    <!DOCTYPE html>\n<html>\n  <head>\n    <style>\nbody {\nbackground: #eee;\nfont-family: Arial, sans-serif;\nfont-size: 6px;\ncolor: #111;\n}\nheader {\nborder-bottom: 1px solid #ddd;\npadding-bottom: 30px;\nmargin-bottom: 30px;\n}\n.container {\nbackground: #fff;\nwidth: 450px;\nmargin: 0 auto;\npadding: 30px;\n}\n</style>\n  </head>\n  <body>\n    <section class=\"container\">\n      <header>\n        <!-- This will appear in the header of all e-mails.\n             The subscriber's name will be automatically inserted here. //-->\n        Hi {{ .Subscriber.FirstName }}!\n      </header>\n\n      <!-- This is where the e-mail body will be inserted //-->\n      <div class=\"content\">\n        {{ template \"content\" . }}\n      </div>\n\n      <footer>\n        Copyright 2019. All rights Reserved.\n      </footer>\n\n      <!-- The tracking pixel will be inserted here //-->\n      {{ TrackView }}\n    </section>\n  </body>\n</html>\n

    Info

    For use with plaintext campaigns, create a template with no HTML content and just the placeholder {{ template \"content\" . }}

    "},{"location":"templating/#example-campaign-body","title":"Example campaign body","text":"

    Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of attributes defined, this example shows how to render those values in a campaign.

    Hey, did you notice how the template showed your first name?\nYour last name is {{.Subscriber.LastName }}.\n\nYou have done {{ .Subscriber.Attribs.projects }} projects.\n\n\n{{ if eq .Subscriber.Attribs.city \"Bengaluru\" }}\n  You live in Bangalore!\n{{ else }}\n  Where do you live?\n{{ end }}\n\n\nHere is a link for you to click that will be tracked.\n<a href=\"{{ TrackLink \"https://google.com\" }}\">Google</a>\n

    The above example uses an if condition to show one of two messages depending on the value of a subscriber attribute. Many such dynamic expressions are possible with Go templating expressions.

    "},{"location":"templating/#system-templates","title":"System templates","text":"

    System templates are used for rendering public user facing pages such as the subscription management page, and in automatically generated system e-mails such as the opt-in confirmation e-mail. These are bundled into listmonk but can be customized by copying the static directory locally, and passing its path to listmonk with the ./listmonk --static-dir=your/custom/path flag.

    "},{"location":"templating/#public-pages","title":"Public pages","text":"/static/public/ index.html Base template with the header and footer that all pages use. home.html Landing page on the root domain with the login button. message.html Generic success / failure message page. optin.html Opt-in confirmation page. subscription.html Subscription management page with options for data export and wipe. subscription-form.html List selection and subscription form page.

    To edit the appearance of the public pages using CSS and Javascript, head to Settings > Appearance > Public:

    "},{"location":"templating/#system-e-mails","title":"System e-mails","text":"/static/email-templates/ base.html Base template with the header and footer that all system generated e-mails use. campaign-status.html E-mail notification that is sent to admins on campaign start, completion etc. import-status.html E-mail notification that is sent to admins on finish of an import job. subscriber-data.html E-mail that is sent to subscribers when they request a full dump of their private data. subscriber-optin.html Automatic opt-in confirmation e-mail that is sent to an unconfirmed subscriber when they are added. subscriber-optin-campaign.html E-mail content that's inserted into a campaign body when starting an opt-in campaign from the lists page. default.tpl Default campaign template that is created in Campaigns -> Templates when listmonk is first installed. This is not used after that.

    Info

    To turn system e-mail templates to plaintext, remove <!doctype html> from base.html and remove all HTML tags from the templates while retaining the Go templating code.

    "},{"location":"upgrade/","title":"Upgrade","text":"

    Some versions may require changes to the database. These changes or database \"migrations\" are applied automatically and safely, but, it is recommended to take a backup of the Postgres database before running the --upgrade option, especially if you have made customizations to the database tables.

    "},{"location":"upgrade/#binary","title":"Binary","text":"
    • Download the latest release and extract the listmonk binary.
    • ./listmonk --upgrade to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects.
    • Run ./listmonk and visit http://localhost:9000.
    "},{"location":"upgrade/#docker","title":"Docker","text":"
    • docker-compose pull to pull the latest version from DockerHub.
    • docker-compose run --rm app ./listmonk --upgrade to upgrade an existing DB.
    • Run docker-compose up app db and visit http://localhost:9000.
    "},{"location":"upgrade/#railway","title":"Railway","text":"
    • Head to your dashboard, and select your Listmonk project.
    • Select the GitHub deployment service.
    • In the Deployment tab, head to the latest deployment, click on the three vertical dots to the right, and select \"Redeploy\".
    "},{"location":"apis/apis/","title":"APIs","text":"

    All features that are available on the listmonk dashboard are also available as REST-like HTTP APIs that can be interacted with directly. Request and response bodies are JSON. This allows easy scripting of listmonk and integration with other systems, for instance, synchronisation with external subscriber databases.

    API requests require BasicAuth authentication with the admin credentials.

    The API section is a work in progress. There may be API calls that are yet to be documented. Please consider contributing to docs.

    "},{"location":"apis/apis/#openapi-swagger-spec","title":"OpenAPI (Swagger) spec","text":"

    The auto-generated OpenAPI (Swagger) specification site for the APIs are available at listmonk.app/docs/swagger

    "},{"location":"apis/apis/#response-structure","title":"Response structure","text":""},{"location":"apis/apis/#successful-request","title":"Successful request","text":"
    HTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n\"data\": {}\n}\n

    All responses from the API server are JSON with the content-type application/json unless explicitly stated otherwise. A successful 200 OK response always has a JSON response body with a status key with the value success. The data key contains the full response payload.

    "},{"location":"apis/apis/#failed-request","title":"Failed request","text":"
    HTTP/1.1 500 Server error\nContent-Type: application/json\n\n{\n\"message\": \"Error message\"\n}\n

    A failure response is preceded by the corresponding 40x or 50x HTTP header. There may be an optional data key with additional payload.

    "},{"location":"apis/apis/#timestamps","title":"Timestamps","text":"

    All timestamp fields are in the format 2019-01-01T09:00:00.000000+05:30. The seconds component is suffixed by the milliseconds, followed by the + and the timezone offset.

    "},{"location":"apis/apis/#common-http-error-codes","title":"Common HTTP error codes","text":"code 400 Missing or bad request parameters or values 403 Session expired or invalidate. Must relogin 404 Request resource was not found 405 Request method (GET, POST etc.) is not allowed on the requested endpoint 410 The requested resource is gone permanently 429 Too many requests to the API (rate limiting) 500 Something unexpected went wrong 502 The backend OMS is down and the API is unable to communicate with it 503 Service unavailable; the API is down 504 Gateway timeout; the API is unreachable"},{"location":"apis/campaigns/","title":"API / Campaigns","text":"Method Endpoint Description GET /api/campaigns Gets all campaigns. GET /api/campaigns/:campaign_id Gets a single campaign. GET /api/campaigns/:campaign_id/preview Gets the HTML preview of a campaign body. GET /api/campaigns/running/stats Gets the stats of a given set of campaigns. POST /api/campaigns Creates a new campaign. POST /api/campaigns/:campaign_id/test Posts campaign message to arbitrary subscribers for testing. PUT /api/campaigns/:campaign_id Modifies a campaign. PUT /api/campaigns/:campaign_id/status Start / pause / cancel / schedule a campaign. DELETE /api/campaigns/:campaign_id Deletes a campaign."},{"location":"apis/campaigns/#get-apicampaigns","title":"GET /api/campaigns","text":"

    Gets all campaigns.

    "},{"location":"apis/campaigns/#example-request","title":"Example Request","text":"
     curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns?page=1&per_page=100'\n
    "},{"location":"apis/campaigns/#parameters","title":"Parameters","text":"

    Name | Type | Required/Optional | Description --------|--------------------|-------------|---------------------|--------------------- query | string | Optional | Optional string to search a list by name. order_by | string | Optional | Field to sort results by. name|status|created_at|updated_at order | string | Optional | ASC|DESCSort by ascending or descending order. page | number | Optional | Page number for paginated results. per_page | number | Optional | Results to return per page. Setting this to all skips pagination and returns all results.

    "},{"location":"apis/campaigns/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"CampaignID\": 0,\n\"views\": 0,\n\"clicks\": 0,\n\"lists\": [\n{\n\"id\": 1,\n\"name\": \"Default list\"\n}\n],\n\"started_at\": null,\n\"to_send\": 0,\n\"sent\": 0,\n\"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Welcome to listmonk\",\n\"from_email\": \"No Reply <noreply@yoursite.com>\",\n\"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n\"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n\"status\": \"draft\",\n\"content_type\": \"richtext\",\n\"tags\": [\n\"test-campaign\"\n],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n],\n\"query\": \"\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/campaigns/#get-apicampaignscampaign_id","title":"GET /api/campaigns/:campaign_id","text":"

    Gets a single campaign.

    "},{"location":"apis/campaigns/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign you want to get."},{"location":"apis/campaigns/#example-request_1","title":"Example Request","text":"
    curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1'\n
    "},{"location":"apis/campaigns/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"CampaignID\": 0,\n\"views\": 0,\n\"clicks\": 0,\n\"lists\": [\n{\n\"id\": 1,\n\"name\": \"Default list\"\n}\n],\n\"started_at\": null,\n\"to_send\": 0,\n\"sent\": 0,\n\"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Welcome to listmonk\",\n\"from_email\": \"No Reply <noreply@yoursite.com>\",\n\"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n\"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n\"status\": \"draft\",\n\"content_type\": \"richtext\",\n\"tags\": [\n\"test-campaign\"\n],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n}\n
    "},{"location":"apis/campaigns/#get-apicampaignscampaign_idpreview","title":"GET /api/campaigns/:campaign_id/preview","text":"

    Gets the html preview of a campaign body.

    "},{"location":"apis/campaigns/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign to be previewed."},{"location":"apis/campaigns/#example-request_2","title":"Example Request","text":"
    curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1/preview'\n
    "},{"location":"apis/campaigns/#example-response_2","title":"Example Response","text":"
    <h3>Hi John!</h3>\nThis is a test e-mail campaign. Your second name is Doe and you are from Bengaluru.\n
    "},{"location":"apis/campaigns/#get-apicampaignsrunningstats","title":"GET /api/campaigns/running/stats","text":"

    Gets the running stat of a given set of campaigns.

    "},{"location":"apis/campaigns/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Query Parameters Number Required The id values of the campaigns whose stat you want to get."},{"location":"apis/campaigns/#example-request_3","title":"Example Request","text":"
    curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/running/stats?campaign_id=1'\n
    "},{"location":"apis/campaigns/#example-response_3","title":"Example Response","text":"
    {\n\"data\": []\n}\n
    "},{"location":"apis/campaigns/#post-apicampaigns","title":"POST /api/campaigns","text":"

    Creates a new campaign.

    "},{"location":"apis/campaigns/#parameters_4","title":"Parameters","text":"Name Data type Required/Optional Description name String Required Name of the campaign. subject String Required (E-mail) subject of the campaign. lists []Number Required Array of list IDs to send the campaign to. from_email String Optional From e-mail to show on the campaign e-mails. If left empty, the default value from settings is used. type String Required regular or optin campaign. content_type String Required richtext, html, markdown, plain body String Required Campaign content body. altbody String Optional Alternate plain text body for HTML (and richtext) e-mails. send_at String Optional A timestamp to schedule the campaign at. Eg: 2021-12-25T06:00:00 (YYYY-MM-DDTHH:MM:SS) messenger String Optional email or a custom messenger defined in the settings. If left empty, email is used. template_id Number Optional ID of the template to use. If left empty, the default template is used. tags []String Optional Array of string tags to mark the campaign."},{"location":"apis/campaigns/#example-request_4","title":"Example request","text":"
    curl -u \"username:password\" 'http://localhost:9000/api/campaigns' -X POST -H 'Content-Type: application/json;charset=utf-8' --data-raw '{\"name\":\"Test campaign\",\"subject\":\"Hello, world\",\"lists\":[1],\"from_email\":\"listmonk <noreply@listmonk.yoursite.com>\",\"content_type\":\"richtext\",\"messenger\":\"email\",\"type\":\"regular\",\"tags\":[\"test\"],\"template_id\":1}'\n
    "},{"location":"apis/campaigns/#example-response_4","title":"Example response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2021-12-27T11:50:23.333485Z\",\n\"updated_at\": \"2021-12-27T11:50:23.333485Z\",\n\"views\": 0,\n\"clicks\": 0,\n\"bounces\": 0,\n\"lists\": [{\n\"id\": 1,\n\"name\": \"Default list\"\n}],\n\"started_at\": null,\n\"to_send\": 1,\n\"sent\": 0,\n\"uuid\": \"90c889cc-3728-4064-bbcb-5c1c446633b3\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Hello, world\",\n\"from_email\": \"listmonk \\u003cnoreply@listmonk.yoursite.com\\u003e\",\n\"body\": \"\",\n\"altbody\": null,\n\"send_at\": null,\n\"status\": \"draft\",\n\"content_type\": \"richtext\",\n\"tags\": [\"test\"],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n}\n
    "},{"location":"apis/campaigns/#put-apicampaignscampaign_idstatus","title":"PUT /api/campaigns/:campaign_id/status","text":"

    Modifies a campaign status to start, pause, cancel, or schedule a campaign.

    "},{"location":"apis/campaigns/#parameters_5","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign whose status is to be modified. status Request Body String Required scheduled, running, paused, cancelled."},{"location":"apis/campaigns/#note","title":"Note:","text":"
    • Only \"scheduled\" campaigns can be saved as \"draft\".
    • Only \"draft\" campaigns can be \"scheduled\".
    • Only \"paused\" campaigns and \"draft\" campaigns can be started.
    • Only \"running\" campaigns can be \"cancelled\" and \"paused\".
    "},{"location":"apis/campaigns/#example-request_5","title":"Example Request","text":"
    curl -u \"username:password\" -X PUT 'http://localhost:9000/api/campaigns/1/status' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\"status\":\"scheduled\"}'\n
    "},{"location":"apis/campaigns/#example-response_5","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n\"updated_at\": \"2020-04-08T19:35:17.331867+01:00\",\n\"CampaignID\": 0,\n\"views\": 0,\n\"clicks\": 0,\n\"lists\": [\n{\n\"id\": 1,\n\"name\": \"Default list\"\n}\n],\n\"started_at\": null,\n\"to_send\": 0,\n\"sent\": 0,\n\"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n\"type\": \"regular\",\n\"name\": \"Test campaign\",\n\"subject\": \"Welcome to listmonk\",\n\"from_email\": \"No Reply <noreply@yoursite.com>\",\n\"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n\"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n\"status\": \"scheduled\",\n\"content_type\": \"richtext\",\n\"tags\": [\n\"test-campaign\"\n],\n\"template_id\": 1,\n\"messenger\": \"email\"\n}\n}\n
    "},{"location":"apis/campaigns/#delete-apicampaignscampaign_id","title":"DELETE /api/campaigns/:campaign_id","text":"

    Deletes a campaign, only scheduled campaigns that have not yet been started can be deleted.

    "},{"location":"apis/campaigns/#parameters_6","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Path Parameter Number Required The id value of the campaign you want to delete."},{"location":"apis/campaigns/#example-request_6","title":"Example Request","text":"
    curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/campaigns/34'\n
    "},{"location":"apis/campaigns/#example-response_6","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/import/","title":"API / Import","text":"Method Endpoint Description GET api/import/subscribers Gets a import statistics. GET api/import/subscribers/logs Get a import statistics . POST api/import/subscribers Upload a ZIP file or CSV file to bulk import subscribers. DELETE api/import/subscribers Stops and deletes a import."},{"location":"apis/import/#get-apiimportsubscribers","title":"GET api/import/subscribers","text":"

    Gets import status.

    "},{"location":"apis/import/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/import/subscribers'\n
    "},{"location":"apis/import/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"name\": \"\",\n\"total\": 0,\n\"imported\": 0,\n\"status\": \"none\"\n}\n}\n
    "},{"location":"apis/import/#get-apiimportsubscriberslogs","title":"GET api/import/subscribers/logs","text":"

    Gets import logs.

    "},{"location":"apis/import/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/import/subscribers/logs'\n
    "},{"location":"apis/import/#example-response_1","title":"Example Response","text":"
    {\n\"data\": \"2020/04/08 21:55:20 processing 'import.csv'\\n2020/04/08 21:55:21 imported finished\\n\"\n}\n
    "},{"location":"apis/import/#post-apiimportsubscribers","title":"POST api/import/subscribers","text":"

    Post a CSV (optionally zipped) file to do a bulk import. The request should be a multipart form POST.

    "},{"location":"apis/import/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description params Request body String Required Stringified JSON with import params file Request body File Required File to upload

    params (JSON string)

        {\n\"mode\": \"subscribe\", // subscribe or blocklist\n\"delim\": \",\",        // delimiter in the uploaded file\n\"lists\":[1],         // array of list IDs to import into\n\"overwrite\": true    // overwrite existing entries or skip them?\n}\n
    "},{"location":"apis/import/#delete-apiimportsubscribers","title":"DELETE api/import/subscribers","text":"

    Stops and deletes an import.

    "},{"location":"apis/import/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/import/subscribers' 
    "},{"location":"apis/import/#example-response_2","title":"Example Response","text":"
    {\n\"data\": {\n\"name\": \"\",\n\"total\": 0,\n\"imported\": 0,\n\"status\": \"none\"\n}\n}\n
    "},{"location":"apis/lists/","title":"API / Lists","text":"Method Endpoint Description GET /api/lists Gets all lists. GET /api/lists/:list_id Gets a single list. POST /api/lists Creates a new list. PUT /api/lists/:list_id Modifies a list. DELETE /api/lists/:list_id Deletes a list."},{"location":"apis/lists/#get-apilists","title":"GET /api/lists","text":"

    Gets lists.

    "},{"location":"apis/lists/#parameters","title":"Parameters","text":"Name Type Required/Optional Description query string Optional Optional string to search a list by name. order_by string Optional Field to sort results by. name|status|created_at|updated_at order string Optional ASC|DESCSort by ascending or descending order. page number Optional Page number for paginated results. per_page number Optional Results to return per page. Setting this to all skips pagination and returns all results."},{"location":"apis/lists/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/lists?page=1&per_page=100'\n
    "},{"location":"apis/lists/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-03-06T22:32:01.118327+01:00\",\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"optin\": \"double\",\n\"tags\": [\n\"test\"\n],\n\"subscriber_count\": 2\n},\n{\n\"id\": 2,\n\"created_at\": \"2020-03-04T21:12:09.555013+01:00\",\n\"updated_at\": \"2020-03-06T22:34:46.405031+01:00\",\n\"uuid\": \"f20a2308-dfb5-4420-a56d-ecf0618a102d\",\n\"name\": \"get\",\n\"type\": \"private\",\n\"optin\": \"single\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n],\n\"total\": 5,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/lists/#get-apilistslist_id","title":"GET /api/lists/:list_id","text":"

    Gets a single list.

    "},{"location":"apis/lists/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description list_id Path parameter number Required The id value of the list you want to get."},{"location":"apis/lists/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/lists/5'\n
    "},{"location":"apis/lists/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 5,\n\"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n\"name\": \"Test list\",\n\"type\": \"public\",\n\"optin\": \"double\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n}\n
    "},{"location":"apis/lists/#post-apilists","title":"POST /api/lists","text":"

    Creates a new list.

    "},{"location":"apis/lists/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description name Request body string Required The new list name. type Request body string Required List type, can be set to private or public. optin Request body string Required single or double optin. tags Request body string[] Optional The tags associated with the list."},{"location":"apis/lists/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X POST 'http://localhost:9000/api/lists'\n
    "},{"location":"apis/lists/#example-response_2","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 5,\n\"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n\"name\": \"Test list\",\n\"type\": \"public\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n}\nnull\n
    "},{"location":"apis/lists/#put-apilistslist_id","title":"PUT /api/lists/list_id","text":"

    Modifies a list.

    "},{"location":"apis/lists/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description list_id Path parameter number Required The id of the list to be modified. name Request body string Optional The name which the old name will be modified to. type Request body string Optional List type, can be set to private or public. optin Request body string Optional single or double optin. tags Request body string[] Optional The tags associated with the list."},{"location":"apis/lists/#example-request_3","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/lists/5' \\\n--form 'name=modified test list' \\\n--form 'type=private'\n
    "},{"location":"apis/lists/#example-response_3","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 5,\n\"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n\"updated_at\": \"2020-03-07T06:52:15.208075+01:00\",\n\"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n\"name\": \"modified test list\",\n\"type\": \"private\",\n\"optin\": \"single\",\n\"tags\": [],\n\"subscriber_count\": 0\n}\n}\n
    "},{"location":"apis/media/","title":"API / Media","text":"Method Endpoint Description GET /api/media Gets an uploaded media file. POST /api/media Uploads a media file. DELETE /api/media/:media_id Deletes uploaded media files."},{"location":"apis/media/#get-apimedia","title":"GET /api/media","text":"

    Gets an uploaded media file.

    "},{"location":"apis/media/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------093715978792575906250298'\n
    "},{"location":"apis/media/#example-response","title":"Example Response","text":"
    {\n\"data\": [\n{\n\"id\": 1,\n\"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n\"filename\": \"Media file\",\n\"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n\"thumb_url\": \"/uploads/image_thumb.jpg\",\n\"uri\": \"/uploads/image.jpg\"\n}\n]\n}\n

    Response definitions The following table describes each item in the response.

    Response item Description Data type data Array of the media file objects, which contains an information about the uploaded media files array id Media file object ID number (int) uuid Media file uuid string (uuid) filename Name of the media file string created_at Date and time, when the media file object was created String (localDateTime) thumb_uri The thumbnail URI of the media file string uri URI of the media file string"},{"location":"apis/media/#post-apimedia","title":"POST /api/media","text":"

    Uploads a media file.

    "},{"location":"apis/media/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description file Request body Media file Required The media file to be uploaded."},{"location":"apis/media/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X POST 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------183679989870526937212428' \\\n--form 'file=@/path/to/image.jpg'\n
    "},{"location":"apis/media/#example-response_1","title":"Example Response","text":"

    {\n\"data\": {\n\"id\": 1,\n\"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n\"filename\": \"Media file\",\n\"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n\"thumb_uri\": \"/uploads/image_thumb.jpg\",\n\"uri\": \"/uploads/image.jpg\"\n}\n}\n
    Response definitions

    Response item Description Data type data True means that the media file was successfully uploaded boolean"},{"location":"apis/media/#delete-apimediamedia_id","title":"DELETE /api/media/:media_id","text":"

    Deletes an uploaded media file.

    "},{"location":"apis/media/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description Media_id Path Parameter Number Required The id of the media file you want to delete."},{"location":"apis/media/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/media/1'\n
    "},{"location":"apis/media/#example-response_2","title":"Example Response","text":"
    {\n\"data\": true\n}\n

    Response definitions

    Response item Description Data type data True means that the media file was successfully deleted boolean"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint Description GET /api/subscribers Gets all subscribers. GET /api/subscribers/:id Gets a single subscriber. GET /api/subscribers/lists/:id Gets subscribers in a list. GET /api/subscribers Gets subscribers in one or more lists. GET /api/subscribers Gets subscribers filtered by an arbitrary SQL expression. POST /api/subscribers Creates a new subscriber. PUT /api/subscribers/lists Modify subscribers' list memberships. PUT /api/subscribers/:id Updates a subscriber by ID. PUT /api/subscribers/:id/blocklist Blocklists a single subscriber. PUT /api/subscribers/blocklist Blocklists one or more subscribers. PUT /api/subscribers/query/blocklist Blocklists subscribers with an arbitrary SQL expression. DELETE /api/subscribers/:id Deletes a single subscriber. DELETE /api/subscribers Deletes one or more subscribers . POST /api/subscribers/query/delete Deletes subscribers with an arbitrary SQL expression."},{"location":"apis/subscribers/#get-apisubscribers","title":"GET /api/subscribers","text":"

    Gets all subscribers.

    "},{"location":"apis/subscribers/#example-request","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers?page=1&per_page=100' 

    To skip pagination and retrieve all records, pass per_page=all.

    "},{"location":"apis/subscribers/#example-response","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n\"email\": \"john@example.com\",\n\"name\": \"John Doe\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"good\": true,\n\"type\": \"known\"\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\n\"test\"\n],\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n}\n]\n},\n{\n\"id\": 2,\n\"created_at\": \"2020-02-18T21:10:17.218979+01:00\",\n\"updated_at\": \"2020-02-18T21:10:17.218979+01:00\",\n\"uuid\": \"ccf66172-f87f-4509-b7af-e8716f739860\",\n\"email\": \"quadri@example.com\",\n\"name\": \"quadri\",\n\"attribs\": {},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\n\"test\"\n],\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n}\n]\n},\n{\n\"id\": 3,\n\"created_at\": \"2020-02-19T19:10:49.36636+01:00\",\n\"updated_at\": \"2020-02-19T19:10:49.36636+01:00\",\n\"uuid\": \"5d940585-3cc8-4add-b9c5-76efba3c6edd\",\n\"email\": \"sugar@example.com\",\n\"name\": \"sugar\",\n\"attribs\": {},\n\"status\": \"enabled\",\n\"lists\": []\n}\n],\n\"query\": \"\",\n\"total\": 3,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/subscribers/#get-apisubscribersid","title":"GET /api/subscribers/:id","text":"

    Gets a single subscriber.

    "},{"location":"apis/subscribers/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Path parameter Number Required The id value of the subscriber you want to get."},{"location":"apis/subscribers/#example-request_1","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers/1' 
    "},{"location":"apis/subscribers/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n\"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n\"email\": \"john@example.com\",\n\"name\": \"John Doe\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"good\": true,\n\"type\": \"known\"\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\n\"test\"\n],\n\"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n\"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n}\n]\n}\n}\n
    "},{"location":"apis/subscribers/#get-apisubscribers_1","title":"GET /api/subscribers","text":"

    Gets subscribers in one or more lists.

    "},{"location":"apis/subscribers/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description List_id Request body Number Required ID of the list to fetch subscribers from."},{"location":"apis/subscribers/#example-request_2","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'\n

    To skip pagination and retrieve all records, pass per_page=all.

    "},{"location":"apis/subscribers/#example-response_2","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/subscribers/#get-apisubscribers_2","title":"GET /api/subscribers","text":"

    Gets subscribers with an SQL expression.

    "},{"location":"apis/subscribers/#example-request_3","title":"Example Request","text":"
    curl -X GET 'http://localhost:9000/api/subscribers' \\\n--url-query 'page=1' \\\n--url-query 'per_page=100' \\\n--url-query \"query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'\"\n

    To skip pagination and retrieve all records, pass per_page=all.

    Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

    "},{"location":"apis/subscribers/#example-response_3","title":"Example Response","text":"
    {\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"subscribers.name LIKE 'Test%' AND subscribers.attribs-\\u003e\\u003e'city' = 'Bengaluru'\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
    "},{"location":"apis/subscribers/#post-apisubscribers","title":"POST /api/subscribers","text":"

    Creates a new subscriber.

    "},{"location":"apis/subscribers/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description email Request body String Required The email address of the new subscriber. name Request body String Required The name of the new subscriber. status Request body String Required The status of the new subscriber. Can be enabled, disabled or blocklisted. lists Request body Numbers Optional Array of list IDs to subscribe to (marked as unconfirmed by default). attribs Request body json Optional JSON list containing new subscriber's attributes. preconfirm_subscriptions Request body Bool Optional If true, marks subscriptions as confirmed and no-optin e-mails are sent for double opt-in lists."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"
    curl 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \\\n--data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"status\":\"enabled\",\"lists\":[1],\"attribs\":{\"city\":\"Bengaluru\",\"projects\":3,\"stack\":{\"languages\":[\"go\",\"python\"]}}}'\n
    "},{"location":"apis/subscribers/#example-response_4","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 3,\n\"created_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"updated_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"uuid\": \"eb420c55-4cfb-4972-92ba-c93c34ba475d\",\n\"email\": \"subsriber@domain.com\",\n\"name\": \"The Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": { \"languages\": [\"go\", \"python\"] }\n},\n\"status\": \"enabled\",\n\"lists\": [1]\n}\n}\n
    "},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT /api/subscribers/lists","text":"

    Modify subscribers list memberships.

    "},{"location":"apis/subscribers/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description ids Request body Numbers Required The ids of the subscribers to be modified. action Request body String Required Whether to add, remove, or unsubscribe the users. target_list_ids Request body Numbers Required The ids of the lists to be modified. status Request body String Required for add confirmed, unconfirmed, or unsubscribed status."},{"location":"apis/subscribers/#example-request_5","title":"Example Request","text":"

    To subscribe users 1, 2, and 3 to lists 4, 5, and 6:

    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/subscribers/lists' \\\n--data-raw '{\"ids\": [1, 2, 3], \"action\": \"add\", \"target_list_ids\": [4, 5, 6], \"status\": \"confirmed\"}'\n
    "},{"location":"apis/subscribers/#example-response_5","title":"Example Response","text":"
    {\n\"data\": true\n} 
    "},{"location":"apis/subscribers/#put-apisubscribersid","title":"PUT /api/subscribers/:id","text":"

    Updates a single subscriber.

    "},{"location":"apis/subscribers/#parameters_4","title":"Parameters","text":"

    Parameters are the same as POST /api/subscribers used for subscriber creation.

    Please note that this is a PUT request, so all the parameters have to be set. For example if you don't provide lists, the subscriber will be deleted from all the lists he was previously signed on.

    "},{"location":"apis/subscribers/#put-apisubscribersidblocklist","title":"PUT /api/subscribers/:id/blocklist","text":"

    Blocklists a single subscriber.

    "},{"location":"apis/subscribers/#parameters_5","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Path parameter Number Required The id value of the subscriber you want to blocklist."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
    "},{"location":"apis/subscribers/#example-response_6","title":"Example Response","text":"
    {\n\"data\": true\n} 
    "},{"location":"apis/subscribers/#put-apisubscribersqueryblocklist","title":"PUT /api/subscribers/query/blocklist","text":"

    Blocklists subscribers with an arbitrary sql expression.

    "},{"location":"apis/subscribers/#example-request_7","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/subscribers/query/blocklist' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n

    Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

    "},{"location":"apis/subscribers/#example-response_7","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/subscribers/#delete-apisubscribersid","title":"DELETE /api/subscribers/:id","text":"

    Deletes a single subscriber.

    "},{"location":"apis/subscribers/#parameters_6","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Path parameter Number Required The id of the subscriber you want to delete."},{"location":"apis/subscribers/#example-request_8","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/subscribers/9'\n
    "},{"location":"apis/subscribers/#example-response_8","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/subscribers/#delete-apisubscribers","title":"DELETE /api/subscribers","text":"

    Deletes one or more subscribers.

    "},{"location":"apis/subscribers/#parameters_7","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Query parameters Number Required The id of the subscribers you want to delete."},{"location":"apis/subscribers/#example-request_9","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/subscribers?id=10&id=11'\n
    "},{"location":"apis/subscribers/#example-response_9","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/subscribers/#post-apisubscribersquerydelete","title":"POST /api/subscribers/query/delete","text":"

    Deletes subscribers with an arbitrary SQL expression.

    "},{"location":"apis/subscribers/#example-request_10","title":"Example Request","text":"
    curl -u \"username:username\" -X POST 'http://localhost:9000/api/subscribers/query/delete' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n

    Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

    "},{"location":"apis/subscribers/#example-response_10","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/templates/","title":"API / Templates","text":"Method Endpoint Description GET /api/templates Gets all templates. GET /api/templates/:template_id Gets a single template. GET /api/templates/:template_id/preview Gets the HTML preview of a template. POST /api/templates/preview POST /api/templates Creates a template. PUT /api/templates/:template_id Modifies a template. PUT /api/templates/:template_id/default Sets a template to the default template. DELETE /api/templates/:template_id Deletes a template."},{"location":"apis/templates/#get-apitemplates","title":"GET /api/templates","text":"

    Gets all templates.

    "},{"location":"apis/templates/#example-request","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/templates'\n
    "},{"location":"apis/templates/#example-response","title":"Example Response","text":"
    {\n\"data\": [\n{\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"name\": \"Default template\",\n\"body\": \"{{ template \\\"content\\\" . }}\",\n\"type\": \"campaign\",\n\"is_default\": true\n}\n]\n}\n
    "},{"location":"apis/templates/#get-apitemplatestemplate_id","title":"GET /api/templates/:template_id","text":"

    Gets a single template.

    "},{"location":"apis/templates/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template you want to get."},{"location":"apis/templates/#example-request_1","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/templates/1'\n
    "},{"location":"apis/templates/#example-response_1","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"name\": \"Default template\",\n\"body\": \"{{ template \\\"content\\\" . }}\",\n\"type\": \"campaign\",\n\"is_default\": true\n}\n}\n
    "},{"location":"apis/templates/#get-apitemplatestemplate_idpreview","title":"GET /api/templates/:template_id/preview","text":"

    Gets the HTML preview of a template body.

    "},{"location":"apis/templates/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template whose html preview you want to get."},{"location":"apis/templates/#example-request_2","title":"Example Request","text":"
    curl -u \"username:username\" -X GET 'http://localhost:9000/api/templates/1/preview'\n
    "},{"location":"apis/templates/#example-response_2","title":"Example Response","text":"
    <p>Hi there</p>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et elit ac elit sollicitudin condimentum non a magna.\n    Sed tempor mauris in facilisis vehicula. Aenean nisl urna, accumsan ac tincidunt vitae, interdum cursus massa.\n    Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam varius turpis et turpis lacinia placerat.\n    Aenean id ligula a orci lacinia blandit at eu felis. Phasellus vel lobortis lacus. Suspendisse leo elit, luctus sed\n    erat ut, venenatis fermentum ipsum. Donec bibendum neque quis.</p>\n\n<h3>Sub heading</h3>\n<p>Nam luctus dui non placerat mattis. Morbi non accumsan orci, vel interdum urna. Duis faucibus id nunc ut euismod.\n    Curabitur et eros id erat feugiat fringilla in eget neque. Aliquam accumsan cursus eros sed faucibus.</p>\n\n<p>Here is a link to <a href=\"https://listmonk.app\" target=\"_blank\">listmonk</a>.</p>\n
    "},{"location":"apis/templates/#put-apitemplatestemplate_iddefault","title":"PUT /api/templates/:template_id/default","text":"

    Sets a template to the default template.

    "},{"location":"apis/templates/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template you want to set to the default template."},{"location":"apis/templates/#example-request_3","title":"Example Request","text":"
    curl -u \"username:username\" -X PUT 'http://localhost:9000/api/templates/1/default'\n
    "},{"location":"apis/templates/#example-response_3","title":"Example Response","text":"
    {\n\"data\": {\n\"id\": 1,\n\"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n\"name\": \"Default template\",\n\"body\": \"{{ template \\\"content\\\" . }}\",\n\"type\": \"campaign\",\n\"is_default\": true\n}\n}\n
    "},{"location":"apis/templates/#delete-apitemplatestemplate_id","title":"DELETE /api/templates/:template_id","text":"

    Deletes a template.

    "},{"location":"apis/templates/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description template_id Path Parameter Number Required The id value of the template you want to delete."},{"location":"apis/templates/#example-request_4","title":"Example Request","text":"
    curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/templates/35'\n
    "},{"location":"apis/templates/#example-response_4","title":"Example Response","text":"
    {\n\"data\": true\n}\n
    "},{"location":"apis/transactional/","title":"API / Transactional","text":"Method Endpoint Description POST /api/tx"},{"location":"apis/transactional/#post-apitx","title":"POST /api/tx","text":"

    Send a transactional message to a subscriber using a predefined transactional template.

    "},{"location":"apis/transactional/#parameters","title":"Parameters","text":"Name Data Type Optional Description subscriber_email String Optional E-mail of the subscriber. Either this or subscriber_id should be passed. subscriber_id Number Optional ID of the subscriber. Either this or subscriber_email should be passed. template_id Number Required ID of the transactional template to use in the message. from_email String Optional Optional from email. eg: Company <email@company.com> data Map Optional Optional data in {} nested map. Available in the template as {{ .Tx.Data.* }} headers []Map Optional Optional array of mail headers. [{\"key\": \"value\"}, {\"key\": \"value\"}] messenger String Optional Messenger to use to send the message. Default value is email. content_type String Optional html, markdown, plain"},{"location":"apis/transactional/#request","title":"Request","text":"
    curl -u \"username:password\" \"http://localhost:9000/api/tx\" -X POST \\\n-H 'Content-Type: application/json; charset=utf-8' \\\n--data-binary @- << EOF\n    {\n        \"subscriber_email\": \"user@test.com\",\n        \"template_id\": 2,\n        \"data\": {\"order_id\": \"1234\", \"date\": \"2022-07-30\", \"items\": [1, 2, 3]},\n        \"content_type\": \"html\"\n    }\nEOF\n
    "},{"location":"apis/transactional/#response","title":"Response","text":"
    {\n\"data\": true\n}\n
    "}]} \ No newline at end of file diff --git a/docs/sitemap.xml.gz b/docs/sitemap.xml.gz index 4aca31900..32e3b3e30 100644 Binary files a/docs/sitemap.xml.gz and b/docs/sitemap.xml.gz differ diff --git a/docs/templating/index.html b/docs/templating/index.html index 109737a24..f87dde70e 100644 --- a/docs/templating/index.html +++ b/docs/templating/index.html @@ -15,7 +15,7 @@ - + @@ -462,6 +462,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -1056,10 +1071,10 @@

    System e-mails - + - + diff --git a/docs/upgrade/index.html b/docs/upgrade/index.html index f12400b4d..016154337 100644 --- a/docs/upgrade/index.html +++ b/docs/upgrade/index.html @@ -15,7 +15,7 @@ - + @@ -394,6 +394,21 @@ +
  • + + Archives + +
  • + + + + + + + + + +
  • Internationalization @@ -661,10 +676,10 @@

    Railway - + - +