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 03e098f5eec15887b6a59e23f519ae34201cde33..0a158a53f6609e4426df6ce49e8068608885e03d 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
@@ -680,7 +680,7 @@ public class MeetingService implements MessageListener {
         } catch (URISyntaxException e) {
             log.error("URI Syntax error in callback url=[{}]", callbackUrl, e);
         }
-        callbackUrlService.handleMessage(new MeetingEndedEvent(callbackUrl));
+        callbackUrlService.handleMessage(new MeetingEndedEvent(m.getInternalId(), m.getExternalId(), m.getName(), callbackUrl));
       }
 
       processRemoveEndedMeeting(message);
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java
index 0153043b3f61ed1febc8c7ae8dfdfd9a6dc9ff56..d55079f549b50a91c7953c07031129f0a8eae192 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java
@@ -4,11 +4,9 @@ import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.*;
 
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
@@ -17,10 +15,12 @@ import org.apache.http.impl.nio.client.HttpAsyncClients;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.gson.Gson;
+
 public class CallbackUrlService {
 	private static Logger log = LoggerFactory.getLogger(CallbackUrlService.class);
 
-	private BlockingQueue<ICallbackEvent> receivedMessages = new LinkedBlockingQueue<ICallbackEvent>();
+	private BlockingQueue<DelayCallback> receivedMessages = new DelayQueue<DelayCallback>();
 
 	private volatile boolean processMessage = false;
 	private static final int MAX_REDIRECTS = 5;
@@ -43,7 +43,7 @@ public class CallbackUrlService {
 				public void run() {
 					while (processMessage) {
 						try {
-							ICallbackEvent msg = receivedMessages.take();
+							DelayCallback msg = receivedMessages.take();
 							processMessage(msg);
 						} catch (InterruptedException e) {
 							log.warn("Error while taking received message from queue.");
@@ -58,19 +58,96 @@ public class CallbackUrlService {
 	}
 
 
-	private void processMessage(final ICallbackEvent msg) {
+	private void processMessage(final DelayCallback msg) {
 		Runnable task = new Runnable() {
 			public void run() {
-
-				fetchCallbackUrl(msg.getCallbackUrl());
+				MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;
+				if (fetchCallbackUrl(msg.callbackEvent.getCallbackUrl())) {
+					Map<String, Object> logData = new HashMap<>();
+					logData.put("meetingId", event.meetingid);
+					logData.put("externalMeetingId", event.extMeetingid);
+					logData.put("name",event.name);
+					logData.put("callback", event.getCallbackUrl());
+					logData.put("attempts", msg.numAttempts);
+					logData.put("logCode", "callback_success");
+					logData.put("description", "Callback successful.");
+
+					Gson gson = new Gson();
+					String logStr = gson.toJson(logData);
+
+					log.info(" --analytics-- data={}", logStr);
+				} else {
+					schedRetryCallback(msg);
+				}
 			}
 		};
 
 		runExec.execute(task);
 	}
 
+	private void schedCallback(final DelayCallback msg, long delayInMillis, int numAttempt) {
+		MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;
+		Map<String, Object> logData = new HashMap<>();
+		logData.put("meetingId", event.meetingid);
+		logData.put("externalMeetingId", event.extMeetingid);
+		logData.put("name",event.name);
+		logData.put("callback", event.getCallbackUrl());
+		logData.put("attempts", msg.numAttempts);
+		logData.put("retryInMs", delayInMillis);
+		logData.put("logCode", "callback_failed_retry");
+		logData.put("description", "Callback failed but retrying.");
+
+		Gson gson = new Gson();
+		String logStr = gson.toJson(logData);
+
+		log.info(" --analytics-- data={}", logStr);
+
+		DelayCallback dc = new DelayCallback(event, delayInMillis, numAttempt);
+		receivedMessages.add(dc);
+	}
+
+	private void giveupCallback(final DelayCallback msg) {
+		MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;
+		Map<String, Object> logData = new HashMap<>();
+		logData.put("meetingId", event.meetingid);
+		logData.put("externalMeetingId", event.extMeetingid);
+		logData.put("name",event.name);
+		logData.put("callback", event.getCallbackUrl());
+		logData.put("attempts", msg.numAttempts);
+		logData.put("logCode", "callback_failed_give_up");
+		logData.put("description", "Callback failed and giving up.");
+
+		Gson gson = new Gson();
+		String logStr = gson.toJson(logData);
+
+		log.info(" --analytics-- data={}", logStr);
+	}
+	private void schedRetryCallback(final DelayCallback msg) {
+		MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent;
+
+		switch (msg.numAttempts) {
+			case 1:
+				schedCallback(msg, 30_000 /** 30sec **/, 2);
+				break;
+			case 2:
+				schedCallback(msg, 60_000 /** 1min **/, 3);
+				break;
+			case 3:
+				schedCallback(msg, 120_000 /** 2min **/, 4);
+				break;
+			case 4:
+				schedCallback(msg, 300_000 /** 5min **/, 5);
+				break;
+			default:
+				giveupCallback(msg);
+			}
+	}
+
 	public void handleMessage(ICallbackEvent message) {
-		receivedMessages.add(message);
+		long delayInMillis = -1000 /**Send right away **/;
+		int numAttempt = 1;
+		DelayCallback dc = new DelayCallback(message, delayInMillis, numAttempt);
+		receivedMessages.add(dc);
 	}
 
 	private String followRedirect(String redirectUrl, int redirectCount, String origUrl) {
@@ -133,7 +210,8 @@ public class CallbackUrlService {
 
 			Future<HttpResponse> future = httpclient.execute(request, null);
 			HttpResponse response = future.get();
-			success = response.getStatusLine().getStatusCode() == 200;
+			// Consider 2xx response code as success.
+			success = (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300);
 		} catch (java.lang.InterruptedException ex) {
 			log.error("Interrupted exception while calling url {}", callbackUrl);
 		} catch (java.util.concurrent.ExecutionException ex) {
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/DelayCallback.java b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/DelayCallback.java
new file mode 100755
index 0000000000000000000000000000000000000000..f72aab3b847a9c3ad09469e9f19ffb6593ec6449
--- /dev/null
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/DelayCallback.java
@@ -0,0 +1,27 @@
+package org.bigbluebutton.web.services.callback;
+
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+public class DelayCallback implements Delayed{
+  public final ICallbackEvent callbackEvent;
+  public final long callTime;
+  public final int numAttempts;
+
+  public DelayCallback(ICallbackEvent event, long delayInMillis, int numAttempts) {
+    this.callbackEvent = event;
+    this.callTime = System.currentTimeMillis() + delayInMillis;
+    this.numAttempts = numAttempts;
+  }
+
+  @Override
+  public long getDelay(TimeUnit unit) {
+    long diff = callTime - System.currentTimeMillis();
+    return unit.convert(diff, TimeUnit.MILLISECONDS);
+  }
+
+  @Override
+  public int compareTo(Delayed o) {
+    return new Long(this.callTime - ((DelayCallback) o).callTime).intValue();
+  }
+}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java
index 3eb9b13784f44402cb182fce821e18df37c6ac2c..807b03a118030a94ae7207a5059a6457881515d4 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java
@@ -2,9 +2,15 @@ package org.bigbluebutton.web.services.callback;
 
 public class MeetingEndedEvent implements ICallbackEvent {
 	private final String callbackUrl;
+	public final String meetingid;
+	public final String extMeetingid;
+	public final String name;
 
-	public MeetingEndedEvent(String callbackUrl) {
+	public MeetingEndedEvent(String mid, String extMid, String name, String callbackUrl) {
 		this.callbackUrl = callbackUrl;
+		this.meetingid = mid;
+		this.extMeetingid = extMid;
+		this.name = name;
 	}
 
 	public String getCallbackUrl() {