def run(self, message_id, **kwargs):
        """
        Load and contruct message and send them off
        """
        log = self.get_logger(**kwargs)

        error_retry_count = kwargs.get("error_retry_count", 0)
        if error_retry_count >= self.max_error_retries:
            raise MaxRetriesExceededError(
                "Can't retry {0}[{1}] args:{2} kwargs:{3}".format(
                    self.name, self.request.id, self.request.args, kwargs
                )
            )

        log.info("Loading Outbound Message <%s>" % message_id)
        try:
            message = Outbound.objects.select_related("channel").get(id=message_id)
        except ObjectDoesNotExist:
            logger.error("Missing Outbound message", exc_info=True)
            return

        if message.attempts < settings.MESSAGE_SENDER_MAX_RETRIES:
            if error_retry_count > 0:
                retry_delay = calculate_retry_delay(error_retry_count)
            else:
                retry_delay = self.default_retry_delay
            log.info("Attempts: %s" % message.attempts)
            # send or resend
            try:
                if not message.channel:
                    channel = Channel.objects.get(default=True)
                else:
                    channel = message.channel

                sender = self.get_client(channel)
                ConcurrencyLimiter.manage_limit(self, channel)

                if not message.to_addr and message.to_identity:
                    message.to_addr = get_identity_address(
                        message.to_identity, use_communicate_through=True
                    )
                    if not message.to_addr:
                        self.fire_failed_msisdn_lookup(message.to_identity)
                        return

                if message.to_addr and not message.to_identity:
                    result = get_identity_by_address(message.to_addr)

                    if result:
                        message.to_identity = result[0]["id"]
                    else:
                        identity = {
                            "details": {
                                "default_addr_type": "msisdn",
                                "addresses": {
                                    "msisdn": {message.to_addr: {"default": True}}
                                },
                            }
                        }
                        identity = create_identity(identity)
                        message.to_identity = identity["id"]

                if "voice_speech_url" in message.metadata:
                    # OBD number of tries metric
                    fire_metric.apply_async(
                        kwargs={
                            "metric_name": "vumimessage.obd.tries.sum",
                            "metric_value": 1.0,
                        }
                    )

                    # Voice message
                    speech_url = message.metadata["voice_speech_url"]
                    vumiresponse = sender.send_voice(
                        voice_to_addr_formatter(message.to_addr),
                        message.content,
                        speech_url=speech_url,
                        session_event="new",
                    )
                    log.info("Sent voice message to <%s>" % message.to_addr)

                elif "image_url" in message.metadata:
                    # Image message
                    image_url = message.metadata["image_url"]
                    vumiresponse = sender.send_image(
                        text_to_addr_formatter(message.to_addr),
                        message.content,
                        image_url=image_url,
                    )
                    log.info("Sent image message to <%s>" % (message.to_addr,))

                else:
                    # Plain content
                    vumiresponse = sender.send_text(
                        text_to_addr_formatter(message.to_addr),
                        message.content,
                        metadata=message.metadata,
                        session_event="new",
                    )
                    log.info("Sent text message to <%s>" % (message.to_addr,))

                message.last_sent_time = timezone.now()
                message.attempts += 1
                message.vumi_message_id = vumiresponse["message_id"]
                message.save()
                fire_metric.apply_async(
                    kwargs={"metric_name": "vumimessage.tries.sum", "metric_value": 1.0}
                )
            except requests_exceptions.ConnectionError as exc:
                log.info("Connection Error sending message")
                fire_metric.delay("sender.send_message.connection_error.sum", 1)
                kwargs["error_retry_count"] = error_retry_count + 1
                self.retry(
                    exc=exc, countdown=retry_delay, args=(message_id,), kwargs=kwargs
                )
            except requests_exceptions.Timeout as exc:
                log.info("Sending message failed due to timeout")
                fire_metric.delay("sender.send_message.timeout.sum", 1)
                kwargs["error_retry_count"] = error_retry_count + 1
                self.retry(
                    exc=exc, countdown=retry_delay, args=(message_id,), kwargs=kwargs
                )
            except (requests_exceptions.HTTPError, HTTPServiceError) as exc:
                # retry message sending if in 500 range (3 default
                # retries)
                log.info(
                    "Sending message failed due to status: %s"
                    % exc.response.status_code
                )
                metric_name = (
                    "sender.send_message.http_error.%s.sum" % exc.response.status_code
                )
                fire_metric.delay(metric_name, 1)
                kwargs["error_retry_count"] = error_retry_count + 1
                self.retry(
                    exc=exc, countdown=retry_delay, args=(message_id,), kwargs=kwargs
                )

            # If we've gotten this far the message send was successful.
            fire_metric.apply_async(
                kwargs={"metric_name": "message.sent.sum", "metric_value": 1.0}
            )
            return vumiresponse

        else:
            # This is for retries based on async nacks from the transport.
            log.info("Message <%s> at max retries." % str(message_id))
            message.to_addr = ""
            message.save(update_fields=["to_addr"])
            fire_metric.apply_async(
                kwargs={
                    "metric_name": "vumimessage.maxretries.sum",
                    "metric_value": 1.0,
                }
            )
            # Count failures on exhausted tries.
            fire_metric.apply_async(
                kwargs={"metric_name": "message.failures.sum", "metric_value": 1.0}
            )