diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala
index 91dbac6f96e626e2cb0f37dfee8c1cd453a0fa67..a5cc4119b6bb1fcbdb3add5b91a2f9775890845d 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala
@@ -76,6 +76,10 @@ class ReceivedJsonMsgHandlerActor(
         routeGenericMsg[EjectUserFromMeetingSysMsg](envelope, jsonNode)
       case ValidateConnAuthTokenSysMsg.NAME =>
         route[ValidateConnAuthTokenSysMsg](meetingManagerChannel, envelope, jsonNode)
+      case AddPadSysMsg.NAME =>
+        routeGenericMsg[AddPadSysMsg](envelope, jsonNode)
+      case AddCaptionsPadsSysMsg.NAME =>
+        routeGenericMsg[AddCaptionsPadsSysMsg](envelope, jsonNode)
 
       // Guests
       case GetGuestsWaitingApprovalReqMsg.NAME =>
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
index b5b2fb22df505c8952a8a335d6769b68782c8800..d9a666efafd63d833fc6ea5ee9fcb62c016ab7e2 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
@@ -26,6 +26,7 @@ import org.bigbluebutton.core.models._
 import org.bigbluebutton.core2.{ MeetingStatus2x, Permissions }
 import org.bigbluebutton.core2.message.handlers._
 import org.bigbluebutton.core2.message.handlers.meeting._
+import org.bigbluebutton.core2.message.handlers.pads._
 import org.bigbluebutton.common2.msgs._
 import org.bigbluebutton.core.apps.breakout._
 import org.bigbluebutton.core.apps.polls._
@@ -84,6 +85,8 @@ class MeetingActor(
   with SyncGetMeetingInfoRespMsgHdlr
   with ClientToServerLatencyTracerMsgHdlr
   with ValidateConnAuthTokenSysMsgHdlr
+  with AddPadSysMsgHdlr
+  with AddCaptionsPadsSysMsgHdlr
   with UserActivitySignCmdMsgHdlr {
 
   object CheckVoiceRecordingInternalMsg
@@ -491,6 +494,9 @@ class MeetingActor(
 
       case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
 
+      case m: AddPadSysMsg                => handleAddPadSysMsg(m)
+      case m: AddCaptionsPadsSysMsg       => handleAddCaptionsPadsSysMsg(m)
+
       case m: UserActivitySignCmdMsg      => handleUserActivitySignCmdMsg(m)
 
       case _                              => log.warning("***** Cannot handle " + msg.envelope.name)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/pads/AddCaptionsPadsSysMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/pads/AddCaptionsPadsSysMsgHdlr.scala
new file mode 100755
index 0000000000000000000000000000000000000000..466f3a354f2dacfc07eb9ecdf6e22c8e83c1298b
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/pads/AddCaptionsPadsSysMsgHdlr.scala
@@ -0,0 +1,21 @@
+package org.bigbluebutton.core2.message.handlers.pads
+
+import org.bigbluebutton.common2.msgs._
+import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, OutMsgRouter }
+import org.bigbluebutton.core2.message.senders.MsgBuilder
+
+trait AddCaptionsPadsSysMsgHdlr {
+  this: BaseMeetingActor =>
+
+  val liveMeeting: LiveMeeting
+  val outGW: OutMsgRouter
+
+  def handleAddCaptionsPadsSysMsg(msg: AddCaptionsPadsSysMsg) {
+    val padIds = msg.body.padIds
+    val meetingId = liveMeeting.props.meetingProp.intId
+
+    log.info(s"Handling add captions pads for meetingId=${meetingId}")
+
+    outGW.send(MsgBuilder.buildAddCaptionsPadsEvtMsg(meetingId, padIds))
+  }
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/pads/AddPadSysMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/pads/AddPadSysMsgHdlr.scala
new file mode 100755
index 0000000000000000000000000000000000000000..e9a30313d1431c135a801d23473ea4d28b829ba8
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/pads/AddPadSysMsgHdlr.scala
@@ -0,0 +1,22 @@
+package org.bigbluebutton.core2.message.handlers.pads
+
+import org.bigbluebutton.common2.msgs._
+import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, OutMsgRouter }
+import org.bigbluebutton.core2.message.senders.MsgBuilder
+
+trait AddPadSysMsgHdlr {
+  this: BaseMeetingActor =>
+
+  val liveMeeting: LiveMeeting
+  val outGW: OutMsgRouter
+
+  def handleAddPadSysMsg(msg: AddPadSysMsg) {
+    val padId = msg.body.padId
+    val readOnlyId = msg.body.readOnlyId
+    val meetingId = liveMeeting.props.meetingProp.intId
+
+    log.info(s"Handling add padId=${padId} and readOnlyId=${readOnlyId} for meetingId=${meetingId}")
+
+    outGW.send(MsgBuilder.buildAddPadEvtMsg(meetingId, padId, readOnlyId))
+  }
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala
index c24d64e29f0b3995a66b20570adc3d8009d4a61d..3f079333d7f5fed63f26a32ac2bc66721d13aac3 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala
@@ -83,6 +83,26 @@ object MsgBuilder {
     BbbCommonEnvCoreMsg(envelope, event)
   }
 
+  def buildAddPadEvtMsg(meetingId: String, padId: String, readOnlyId: String): BbbCommonEnvCoreMsg = {
+    val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
+    val envelope = BbbCoreEnvelope(AddPadEvtMsg.NAME, routing)
+    val header = BbbCoreHeaderWithMeetingId(AddPadEvtMsg.NAME, meetingId)
+    val body = AddPadEvtMsgBody(padId, readOnlyId)
+    val event = AddPadEvtMsg(header, body)
+
+    BbbCommonEnvCoreMsg(envelope, event)
+  }
+
+  def buildAddCaptionsPadsEvtMsg(meetingId: String, padIds: Array[String]): BbbCommonEnvCoreMsg = {
+    val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
+    val envelope = BbbCoreEnvelope(AddCaptionsPadsEvtMsg.NAME, routing)
+    val header = BbbCoreHeaderWithMeetingId(AddCaptionsPadsEvtMsg.NAME, meetingId)
+    val body = AddCaptionsPadsEvtMsgBody(padIds)
+    val event = AddCaptionsPadsEvtMsg(header, body)
+
+    BbbCommonEnvCoreMsg(envelope, event)
+  }
+
   def buildGetUsersMeetingRespMsg(meetingId: String, userId: String, webusers: Vector[WebUser]): BbbCommonEnvCoreMsg = {
     val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
     val envelope = BbbCoreEnvelope(GetUsersMeetingRespMsg.NAME, routing)
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala
index 677c475b91c530bb8d157c38c31a38017be28070..6f8023dfd31a021cc4abc183d87babb0355bffbf 100755
--- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala
@@ -189,6 +189,22 @@ case class ValidateConnAuthTokenSysRespMsg(
 case class ValidateConnAuthTokenSysRespMsgBody(meetingId: String, userId: String,
                                                connId: String, authzed: Boolean, app: String)
 
+object AddPadSysMsg { val NAME = "AddPadSysMsg" }
+case class AddPadSysMsg(header: BbbClientMsgHeader, body: AddPadSysMsgBody) extends StandardMsg
+case class AddPadSysMsgBody(padId: String, readOnlyId: String)
+
+object AddCaptionsPadsSysMsg { val NAME = "AddCaptionsPadsSysMsg" }
+case class AddCaptionsPadsSysMsg(header: BbbClientMsgHeader, body: AddCaptionsPadsSysMsgBody) extends StandardMsg
+case class AddCaptionsPadsSysMsgBody(padIds: Array[String])
+
+object AddPadEvtMsg { val NAME = "AddPadEvtMsg" }
+case class AddPadEvtMsg(header: BbbCoreHeaderWithMeetingId, body: AddPadEvtMsgBody) extends BbbCoreMsg
+case class AddPadEvtMsgBody(padId: String, readOnlyId: String)
+
+object AddCaptionsPadsEvtMsg { val NAME = "AddCaptionsPadsEvtMsg" }
+case class AddCaptionsPadsEvtMsg(header: BbbCoreHeaderWithMeetingId, body: AddCaptionsPadsEvtMsgBody) extends BbbCoreMsg
+case class AddCaptionsPadsEvtMsgBody(padIds: Array[String])
+
 object PublishedRecordingSysMsg { val NAME = "PublishedRecordingSysMsg" }
 case class PublishedRecordingSysMsg(header: BbbCoreBaseHeader, body: PublishedRecordingSysMsgBody) extends BbbCoreMsg
 case class PublishedRecordingSysMsgBody(recordId: String)
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
index adff3ac4616bc09b4548d55046439796735269fe..71d13675136f93bffd97eac1150f21768e411c13 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
@@ -54,6 +54,8 @@ import org.bigbluebutton.api.messaging.converters.messages.EndMeetingMessage;
 import org.bigbluebutton.api.messaging.converters.messages.PublishedRecordingMessage;
 import org.bigbluebutton.api.messaging.converters.messages.UnpublishedRecordingMessage;
 import org.bigbluebutton.api.messaging.converters.messages.DeletedRecordingMessage;
+import org.bigbluebutton.api.messaging.messages.AddPad;
+import org.bigbluebutton.api.messaging.messages.AddCaptionsPads;
 import org.bigbluebutton.api.messaging.messages.CreateBreakoutRoom;
 import org.bigbluebutton.api.messaging.messages.CreateMeeting;
 import org.bigbluebutton.api.messaging.messages.EndMeeting;
@@ -161,6 +163,20 @@ public class MeetingService implements MessageListener {
     }
   }
 
+  public Boolean isPadValid(String padId, String sessionToken) {
+    UserSession us = getUserSessionWithAuthToken(sessionToken);
+    if (us == null) return false;
+
+    Meeting m = getMeeting(us.meetingID);
+    if (m == null) return false;
+
+    if (m.hasPad(padId)) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
   public UserSession getUserSessionWithUserId(String userId) {
     for (UserSession userSession : sessions.values()) {
       if (userSession.internalUserId.equals(userId)) {
@@ -1051,6 +1067,10 @@ public class MeetingService implements MessageListener {
           processGuestPolicyChanged((GuestPolicyChanged) message);
         } else if (message instanceof RecordChapterBreak) {
           processRecordingChapterBreak((RecordChapterBreak) message);
+        } else if (message instanceof AddPad) {
+          processAddPad((AddPad) message);
+        } else if (message instanceof AddCaptionsPads) {
+          processAddCaptionsPads((AddCaptionsPads) message);
         } else if (message instanceof MakePresentationDownloadableMsg) {
           processMakePresentationDownloadableMsg((MakePresentationDownloadableMsg) message);
         } else if (message instanceof UpdateRecordingStatus) {
@@ -1069,6 +1089,22 @@ public class MeetingService implements MessageListener {
     }
   }
 
+  public void processAddPad(AddPad msg) {
+    Meeting m = getMeeting(msg.meetingId);
+    if (m != null) {
+      m.addPad(msg.padId, msg.readOnlyId);
+    }
+  }
+
+  public void processAddCaptionsPads(AddCaptionsPads msg) {
+    Meeting m = getMeeting(msg.meetingId);
+    if (m != null) {
+      for (String padId : msg.padIds) {
+        m.addPad(padId, "undefined");
+      }
+    }
+  }
+
   public void processRecordingChapterBreak(RecordChapterBreak msg) {
     recordingService.kickOffRecordingChapterBreak(msg.meetingId, msg.timestamp);
   }
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
index a19b62d7732fa8201b6692d43439f20d3804fd13..9f424916eba2bfe1404ce41b34d760371a6a6cb6 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
@@ -69,6 +69,7 @@ public class Meeting {
 	private String defaultConfigToken;
 	private String guestPolicy = GuestPolicy.ASK_MODERATOR;
 	private boolean userHasJoined = false;
+	private Map<String, String> pads;
 	private Map<String, String> metadata;
 	private Map<String, Object> userCustomData;
 	private final ConcurrentMap<String, User> users;
@@ -131,6 +132,13 @@ public class Meeting {
         allowDuplicateExtUserid = builder.allowDuplicateExtUserid;
         endWhenNoModerator = builder.endWhenNoModerator;
 
+        /*
+         * A pad is a pair of padId and readOnlyId that represents
+         * valid etherpads instances for this meeting. They can be:
+         *  - shared notes
+         *  - closed captions
+         */
+        pads = new HashMap<>();
         userCustomData = new HashMap<>();
 
         users = new ConcurrentHashMap<>();
@@ -179,6 +187,10 @@ public class Meeting {
 		return configs.remove(token);
 	}
 
+	public Map<String, String> getPads() {
+		return pads;
+	}
+
 	public Map<String, String> getMetadata() {
 		return metadata;
 	}
@@ -537,6 +549,14 @@ public class Meeting {
         return sum;
     }
 	
+	public void addPad(String padId, String readOnlyId) {
+		pads.put(padId, readOnlyId);
+	}
+
+	public Boolean hasPad(String id) {
+		return pads.containsKey(id) || pads.containsValue(id);
+	}
+
 	public void addUserCustomData(String userID, Map<String, String> data) {
 		userCustomData.put(userID, data);
 	}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/AddCaptionsPads.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/AddCaptionsPads.java
new file mode 100755
index 0000000000000000000000000000000000000000..3ccd1e10d72964024d4386b6f8ef332ee6d22260
--- /dev/null
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/AddCaptionsPads.java
@@ -0,0 +1,11 @@
+package org.bigbluebutton.api.messaging.messages;
+
+public class AddCaptionsPads implements IMessage {
+    public final String meetingId;
+    public final String[] padIds;
+
+    public AddCaptionsPads(String meetingId, String[] padIds) {
+        this.meetingId = meetingId;
+        this.padIds = padIds;
+    }
+}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/AddPad.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/AddPad.java
new file mode 100755
index 0000000000000000000000000000000000000000..1f255acd7fa732dd1928cf662da183c2feef8baa
--- /dev/null
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/AddPad.java
@@ -0,0 +1,13 @@
+package org.bigbluebutton.api.messaging.messages;
+
+public class AddPad implements IMessage {
+    public final String meetingId;
+    public final String padId;
+    public final String readOnlyId;
+
+    public AddPad(String meetingId, String padId, String readOnlyId) {
+        this.meetingId = meetingId;
+        this.padId = padId;
+        this.readOnlyId = readOnlyId;
+    }
+}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java
index d02927d1e0d037d88fdc327499eb1382387db98f..9a4b3e96744392aa33f586a300ff07e97e16c22b 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java
@@ -51,4 +51,20 @@ public class ParamsUtil {
     }
     return token;
   }
+
+  public static String getPadId(String url) {
+    String padId = "undefined";
+    try {
+      String decodedURL = URLDecoder.decode(url, "UTF-8");
+      String[] splitURL = decodedURL.split("\\?");
+      // If there is no query params, it's an invalid URL already
+      if (splitURL.length == 2) {
+        String[] params = splitURL[0].split("\\/");
+        padId = params[params.length - 1];
+      }
+    } catch (UnsupportedEncodingException e) {
+      log.error(e.toString());
+    }
+    return padId;
+  }
 }
diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala
index c78d684342c844fbac7a7f30408da245607685ec..7eb216f80f497677bae82e1fa9494eb9a1f4fa27 100755
--- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala
+++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala
@@ -94,6 +94,10 @@ class ReceivedJsonMsgHdlrActor(val msgFromAkkaAppsEventBus: MsgFromAkkaAppsEvent
         route[GuestsWaitingApprovedEvtMsg](envelope, jsonNode)
       case GuestPolicyChangedEvtMsg.NAME =>
         route[GuestPolicyChangedEvtMsg](envelope, jsonNode)
+      case AddPadEvtMsg.NAME =>
+        route[AddPadEvtMsg](envelope, jsonNode)
+      case AddCaptionsPadsEvtMsg.NAME =>
+        route[AddCaptionsPadsEvtMsg](envelope, jsonNode)
       case RecordingChapterBreakSysMsg.NAME =>
         route[RecordingChapterBreakSysMsg](envelope, jsonNode)
       case SetPresentationDownloadableEvtMsg.NAME =>
diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala
index 8c70b881e6f45b461b7a738bb72e956a05b20af4..a43fbcf516824d68f3bc763c645150caa1ea273f 100755
--- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala
+++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala
@@ -39,6 +39,8 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
       case m: PresentationUploadTokenSysPubMsg  => handlePresentationUploadTokenSysPubMsg(m)
       case m: GuestsWaitingApprovedEvtMsg       => handleGuestsWaitingApprovedEvtMsg(m)
       case m: GuestPolicyChangedEvtMsg          => handleGuestPolicyChangedEvtMsg(m)
+      case m: AddCaptionsPadsEvtMsg             => handleAddCaptionsPadsEvtMsg(m)
+      case m: AddPadEvtMsg                      => handleAddPadEvtMsg(m)
       case m: RecordingChapterBreakSysMsg       => handleRecordingChapterBreakSysMsg(m)
       case m: SetPresentationDownloadableEvtMsg => handleSetPresentationDownloadableEvtMsg(m)
       case m: RecordingStatusChangedEvtMsg      => handleRecordingStatusChangedEvtMsg(m)
@@ -50,6 +52,14 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
     olgMsgGW.handle(new GuestPolicyChanged(msg.header.meetingId, msg.body.policy))
   }
 
+  def handleAddPadEvtMsg(msg: AddPadEvtMsg): Unit = {
+    olgMsgGW.handle(new AddPad(msg.header.meetingId, msg.body.padId, msg.body.readOnlyId))
+  }
+
+  def handleAddCaptionsPadsEvtMsg(msg: AddCaptionsPadsEvtMsg): Unit = {
+    olgMsgGW.handle(new AddCaptionsPads(msg.header.meetingId, msg.body.padIds))
+  }
+
   def handleRecordingChapterBreakSysMsg(msg: RecordingChapterBreakSysMsg): Unit = {
     olgMsgGW.handle(new RecordChapterBreak(msg.body.meetingId, msg.body.timestamp))
   }
diff --git a/bigbluebutton-html5/imports/api/captions/server/methods/addCaptionsPads.js b/bigbluebutton-html5/imports/api/captions/server/methods/addCaptionsPads.js
new file mode 100644
index 0000000000000000000000000000000000000000..4ca15b07f1c2ac5e3f216edd12b163415b16785a
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/captions/server/methods/addCaptionsPads.js
@@ -0,0 +1,19 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import Logger from '/imports/startup/server/logger';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+
+export default function addCaptionsPads(meetingId, padIds) {
+  const REDIS_CONFIG = Meteor.settings.private.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'AddCaptionsPadsSysMsg';
+
+  check(meetingId, String);
+  check(padIds, [String]);
+
+  const payload = {
+    padIds,
+  };
+
+  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, 'nodeJSapp', payload);
+}
diff --git a/bigbluebutton-html5/imports/api/captions/server/methods/addPad.js b/bigbluebutton-html5/imports/api/captions/server/methods/addPad.js
new file mode 100644
index 0000000000000000000000000000000000000000..e9b6df837e9dbba0eeb6781a848f919b48eab495
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/captions/server/methods/addPad.js
@@ -0,0 +1,32 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import Captions from '/imports/api/captions';
+import Logger from '/imports/startup/server/logger';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+
+export default function addPad(padId, readOnlyId) {
+  const REDIS_CONFIG = Meteor.settings.private.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'AddPadSysMsg';
+
+  check(padId, String);
+  check(readOnlyId, String);
+
+  const pad = Captions.findOne({ padId });
+
+  if (!pad) {
+    Logger.error(`Could not find closed captions pad ${padId}`);
+    return;
+  }
+
+  const { meetingId } = pad;
+
+  check(meetingId, String);
+
+  const payload = {
+    padId,
+    readOnlyId,
+  };
+
+  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, 'nodeJSapp', payload);
+}
diff --git a/bigbluebutton-html5/imports/api/captions/server/methods/createCaptions.js b/bigbluebutton-html5/imports/api/captions/server/methods/createCaptions.js
index 24215b91c52876b47814e62b33fd52687df92340..af29cb8c889a7524d9225b1ba3ddf9394e0f1810 100644
--- a/bigbluebutton-html5/imports/api/captions/server/methods/createCaptions.js
+++ b/bigbluebutton-html5/imports/api/captions/server/methods/createCaptions.js
@@ -6,6 +6,7 @@ import {
   getLocalesURL,
 } from '/imports/api/captions/server/helpers';
 import addCaption from '/imports/api/captions/server/modifiers/addCaption';
+import addCaptionsPads from '/imports/api/captions/server/methods/addCaptionsPads';
 import axios from 'axios';
 
 export default function createCaptions(meetingId) {
@@ -27,10 +28,13 @@ export default function createCaptions(meetingId) {
       Logger.error(`Could not get locales info for ${meetingId} ${status}`);
       return;
     }
+    const padIds = [];
     const locales = response.data;
     locales.forEach((locale) => {
       const padId = generatePadId(meetingId, locale.locale);
       addCaption(meetingId, padId, locale);
+      padIds.push(padId);
     });
+    addCaptionsPads(meetingId, padIds);
   }).catch(error => Logger.error(`Could not create captions for ${meetingId}: ${error}`));
 }
diff --git a/bigbluebutton-html5/imports/api/captions/server/modifiers/updateReadOnlyPadId.js b/bigbluebutton-html5/imports/api/captions/server/modifiers/updateReadOnlyPadId.js
index 8fddb6f814559a1e6a36762d2066c0439a4bc09d..fffbadf204f6e2b9218fd4756c2c6d55e9591bef 100644
--- a/bigbluebutton-html5/imports/api/captions/server/modifiers/updateReadOnlyPadId.js
+++ b/bigbluebutton-html5/imports/api/captions/server/modifiers/updateReadOnlyPadId.js
@@ -1,6 +1,7 @@
 import Captions from '/imports/api/captions';
 import Logger from '/imports/startup/server/logger';
 import { check } from 'meteor/check';
+import addPad from '/imports/api/captions/server/methods/addPad';
 
 export default function updateReadOnlyPadId(padId, readOnlyPadId) {
   check(padId, String);
@@ -20,6 +21,7 @@ export default function updateReadOnlyPadId(padId, readOnlyPadId) {
     const numberAffected = Captions.update(selector, modifier, { multi: true });
 
     if (numberAffected) {
+      addPad(padId, readOnlyPadId);
       Logger.verbose('Captions: added readOnlyPadId', { padId, readOnlyPadId });
     }
   } catch (err) {
diff --git a/bigbluebutton-html5/imports/api/note/server/methods/addPad.js b/bigbluebutton-html5/imports/api/note/server/methods/addPad.js
new file mode 100644
index 0000000000000000000000000000000000000000..45acbde7833747f08f34c2db93b22692d72a5445
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/note/server/methods/addPad.js
@@ -0,0 +1,21 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import Logger from '/imports/startup/server/logger';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+
+export default function addPad(meetingId, padId, readOnlyId) {
+  const REDIS_CONFIG = Meteor.settings.private.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'AddPadSysMsg';
+
+  check(meetingId, String);
+  check(padId, String);
+  check(readOnlyId, String);
+
+  const payload = {
+    padId,
+    readOnlyId,
+  };
+
+  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, 'nodeJSapp', payload);
+}
diff --git a/bigbluebutton-html5/imports/api/note/server/modifiers/addNote.js b/bigbluebutton-html5/imports/api/note/server/modifiers/addNote.js
index a3f8ae2f6317a9e25068a0798f1825fb01d5b980..15f8152b19ec4b00ec8ab218a4a9914dbcdb74e4 100644
--- a/bigbluebutton-html5/imports/api/note/server/modifiers/addNote.js
+++ b/bigbluebutton-html5/imports/api/note/server/modifiers/addNote.js
@@ -1,6 +1,7 @@
 import { check } from 'meteor/check';
 import Note from '/imports/api/note';
 import Logger from '/imports/startup/server/logger';
+import addPad from '/imports/api/note/server/methods/addPad';
 
 export default function addNote(meetingId, noteId, readOnlyNoteId) {
   check(meetingId, String);
@@ -23,6 +24,7 @@ export default function addNote(meetingId, noteId, readOnlyNoteId) {
     const { insertedId } = Note.upsert(selector, modifier);
 
     if (insertedId) {
+      addPad(meetingId, noteId, readOnlyNoteId);
       Logger.info(`Added note id=${noteId} readOnlyId=${readOnlyNoteId} meeting=${meetingId}`);
     } else {
       Logger.info(`Upserted note id=${noteId} readOnlyId=${readOnlyNoteId} meeting=${meetingId}`);
diff --git a/bigbluebutton-html5/imports/ui/components/note/service.js b/bigbluebutton-html5/imports/ui/components/note/service.js
index 1a6ee84ea198bfda5b2388c1f9c1aa5fe496f1a3..6920998cee8b40553e51dc77de55b902e6ad0bb8 100644
--- a/bigbluebutton-html5/imports/ui/components/note/service.js
+++ b/bigbluebutton-html5/imports/ui/components/note/service.js
@@ -25,6 +25,8 @@ const getLang = () => {
 
 const getNoteParams = () => {
   let config = {};
+  const User = Users.findOne({ userId: Auth.userID }, { fields: { name: 1 } });
+  config.userName = User.name;
   config.lang = getLang();
   config.rtl = document.documentElement.getAttribute('dir') === 'rtl';
 
diff --git a/bigbluebutton-web/bbb-web.nginx b/bigbluebutton-web/bbb-web.nginx
index 120d2dcf8af99924bcea9b2e307214ab5d1e2b70..57d34ebbe4e7270ace3b9eec34728a9ea7212b48 100755
--- a/bigbluebutton-web/bbb-web.nginx
+++ b/bigbluebutton-web/bbb-web.nginx
@@ -88,6 +88,14 @@
 			proxy_set_header         X-Original-URI $request_uri;
 		}
 
+		location = /bigbluebutton/connection/validatePad {
+			internal;
+			proxy_pass               http://127.0.0.1:8090;
+			proxy_pass_request_body  off;
+			proxy_set_header         Content-Length "";
+			proxy_set_header         X-Original-URI $request_uri;
+		}
+
         location ~ "^/bigbluebutton\/textTrack\/(?<textTrackToken>[a-zA-Z0-9]+)\/(?<recordId>[a-zA-Z0-9_-]+)\/(?<textTrack>.+)$" {
             # Workaround IE refusal to set cookies in iframe
             add_header P3P 'CP="No P3P policy available"';
diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy
index 10d573ce34a7026da877fe57d62a894146f7c503..33a9343563de7a2e835e2dfbf8744eb147daaccc 100755
--- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy
+++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/UrlMappings.groovy
@@ -95,10 +95,6 @@ class UrlMappings {
       action = [POST: 'putRecordingTextTrack']
     }
 
-    "/connection/checkAuthorization"(controller:"connection") {
-      action = [GET:'checkAuthorization']
-    }
-
     "/bigbluebutton/$controller/$action?/$id?(.${format})?" {
       constraints {
         // apply constraints here
diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ConnectionController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ConnectionController.groovy
old mode 100644
new mode 100755
index b55ccd7c55932046679cd87fe92194188b3df27d..4abb86651cdd5e7bace8a26dbfef2cbea15cdad7
--- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ConnectionController.groovy
+++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ConnectionController.groovy
@@ -44,4 +44,25 @@ class ConnectionController {
       log.error("Error while authenticating connection.\n" + e.getMessage())
     }
   }
+
+  def validatePad = {
+    try {
+      String uri = request.getHeader("x-original-uri")
+      String sessionToken = ParamsUtil.getSessionToken(uri)
+      String padId = ParamsUtil.getPadId(uri)
+      Boolean valid = meetingService.isPadValid(padId, sessionToken)
+
+      response.addHeader("Cache-Control", "no-cache")
+      response.contentType = 'plain/text'
+      if (valid) {
+        response.setStatus(200)
+        response.outputStream << 'authorized'
+      } else {
+        response.setStatus(401)
+        response.outputStream << 'unauthorized'
+      }
+    } catch (IOException e) {
+      log.error("Error while authenticating connection.\n" + e.getMessage())
+    }
+  }
 }