diff --git a/bfa_client/src/client/bfa_client.go b/bfa_client/src/client/bfa_client.go
index 76ba39e9b991a9b9124b067c06cebece7ecd73ae..44b8bfff4bd10faf78eae4f8510ab2f6bd0eb3e1 100644
--- a/bfa_client/src/client/bfa_client.go
+++ b/bfa_client/src/client/bfa_client.go
@@ -15,8 +15,9 @@ import (
 )
 
 const (
-	latest     = -1
-	minSigners = 5
+	latest            = -1
+	minSigners        = 5 // We don't want to have less sealers than this number
+	voteThreshold int = 3 // Number of manual votes needed to enable autovote
 )
 
 type Node = bfa.Node
@@ -59,13 +60,14 @@ func updateURL(url string) (updated string) {
 }
 
 func usage(errorCode int) {
-	fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones] %v\n%v\n", os.Args[0], command, otherArgs, description)
+	_, _ = fmt.Fprintf(os.Stderr, "Uso: %v %v [opciones] %v\n%v\n", os.Args[0], command, otherArgs, description)
 	flags.PrintDefaults()
 	os.Exit(errorCode)
 }
 
 func parseFlags() {
-	flags.Parse(os.Args[2:])
+	err := flags.Parse(os.Args[2:])
+	util.Check(err)
 	if help {
 		usage(0)
 	}
@@ -205,63 +207,107 @@ func sealers() {
 	}
 }
 
+func autovote() {
+	var (
+		removedSealers = 0
+		threshold      = voteThreshold
+		voted          = make(map[string]bool)
+	)
+	description = fmt.Sprintf(`Vota automáticamente en todas las propuestas activas, en el sentido que corresponda, con ciertas restricciones.
+  - No deja votar si el voto puede reducir la cantidad de selladores a menos de %v.
+  - No permite eliminar a los selladores establecidos en el bloque génesis.
+  - Requiere que la propuesta tenga al menos %v votos.
+  - No permite votar para eliminarse a uno mismo de la lista de selladores.`, minSigners, threshold)
+	flags.IntVar(&threshold, "threshold", voteThreshold, "Cantidad mínima de votos en una propuesta para habilitar el voto automático.")
+	setFlags()
+	parseFlags()
+	util.PanicIf(threshold < voteThreshold, "No se puede especificar una cantidad de votos inferior a %v.", voteThreshold)
+	url = updateURL(url)
+	node, err := bfa.Dial(url)
+	util.Check(err)
+	defer node.Close()
+	util.PanicIf(!node.IsSealer(bfa.Self), "Solo los selladores pueden votar")
+	votes := node.GetVotes(latest)
+	genesisSigners := node.GetSignersAtBlock(0)
+	self, err := node.Coinbase()
+	util.Check(err)
+	for _, tally := range votes.Tally {
+		if tally.False >= threshold { // We are trying to remove a sealer
+			removedSealers += 1
+		}
+	}
+	util.PanicIf(len(votes.Signers)-removedSealers < minSigners, "No se puede emitir un voto automático que reduzca la cantidad de selladores por debajo de %v.", minSigners)
+	for _, proposal := range votes.Proposals {
+		isSealer := util.Contains(votes.Signers, proposal)
+		switch {
+		case votes.Votes[proposal][self] != nil:
+			continue // Already voted
+		case isSealer && votes.Tally[proposal].False < threshold:
+			continue // There aren't enough votes to enable autovote
+		case !isSealer && votes.Tally[proposal].True < threshold:
+			continue // There aren't enough votes to enable autovote
+		case util.Contains(genesisSigners, proposal) && isSealer:
+			log.Printf("No se puede quitar en forma automática a un sellador del bloque génesis: %v.\n", proposal)
+			continue
+		case proposal == self:
+			log.Println("No se puede votar para eliminarse uno mismo de la lista de selladores.")
+			continue
+		}
+		node.Propose(proposal, !isSealer)
+		if json {
+			voted[proposal] = !isSealer
+		} else {
+			fmt.Printf("Voto por %v: %v\n", proposal, !isSealer)
+		}
+	}
+	if json {
+		util.PrintJson(voted)
+	}
+}
+
 func propose() {
 	var (
-		auto      bool
 		authorize bool
-		proposals []string
+		voted     = make(map[string]bool)
 	)
 	description = "Vota por una propuesta."
 	otherArgs = "[propuesta...]"
 	setFlags()
-	flags.BoolVar(&auto, "auto", false, "Vota en todas las propuestas activas, en el sentido que corresponda.")
 	flags.BoolVar(&authorize, "authorize", true, "Sentido del voto (true: a favor, false: en contra).")
 	parseFlags()
 	url = updateURL(url)
 	node, err := bfa.Dial(url)
 	util.Check(err)
 	defer node.Close()
-	genesisSigners := node.GetSignersAtBlock(0)
 	util.PanicIf(!node.IsSealer(bfa.Self), "Solo los selladores pueden votar")
 	votes := node.GetVotes(latest)
-	if auto {
-		for _, proposal := range votes.Proposals {
-			util.PanicIf(util.Contains(genesisSigners, proposal) && node.IsSealer(proposal), "No se puede quitar en forma automática a un sellador del bloque génesis.")
-			proposals = append(proposals, proposal)
+	if flags.NArg() == 0 {
+		panic("No se especificaron candidatos por los cuales votar")
+	}
+	for i := 0; i < flags.NArg(); i++ {
+		address := flags.Arg(i)
+		if !util.IsAddress(address) {
+			panic(fmt.Sprintf("'%v' no es una dirección válida", address))
 		}
-		if flags.NArg() != 0 {
-			fmt.Fprintf(os.Stderr, "Se especificó -auto. Ignorando argumentos adicionales.")
+		if _, ok := votes.Tally[address]; ok {
+			continue // address is in a proposal, so we allow voting either way
 		}
-	} else {
-		if flags.NArg() == 0 {
-			panic("No se especificaron candidatos por los cuales votar")
+		isSealer := util.Contains(votes.Signers, address)
+		switch { // address is not in a proposal, we allow removing signers or adding non signers
+		case isSealer && authorize:
+			panic(fmt.Sprintf("'%v' ya es un sellador", address))
+		case !isSealer && !authorize:
+			panic(fmt.Sprintf("'%v' no es un sellador", address))
 		}
-		for i := 0; i < flags.NArg(); i++ {
-			address := flags.Arg(i)
-			if !util.IsAddress(address) {
-				panic(fmt.Sprintf("'%v' no es una dirección válida", address))
-			}
-			if _, ok := votes.Tally[address]; ok {
-				continue // address is in a proposal, so we allow voting either way
-			}
-			isSealer := node.IsSealer(address)
-			switch { // address is not in a proposal, we allow removing signers or adding non signers
-			case isSealer && authorize:
-				panic(fmt.Sprintf("'%v' ya es un sellador", address))
-			case !isSealer && !authorize:
-				panic(fmt.Sprintf("'%v' no es un sellador", address))
-			}
-			proposals = append(proposals, flags.Arg(i))
+		node.Propose(address, authorize)
+		if json {
+			voted[address] = authorize
+		} else {
+			fmt.Printf("Voto por %v: %v\n", address, authorize)
 		}
 	}
-	self, err := node.Coinbase()
-	util.Check(err)
-	for _, proposal := range proposals {
-		util.PanicIf(proposal == self, "No es válido votarse a sí mismo")
-		authorize = !node.IsSealer(proposal)
-		util.PanicIf(len(node.GetSigners()) <= minSigners && !authorize, "Hay sólo %v selladores. No se permite eliminar más selladores.", minSigners)
-		node.Propose(proposal, authorize)
-		fmt.Printf("Voto por %v: %v\n", proposal, authorize)
+	if json {
+		util.PrintJson(voted)
 	}
 }
 
@@ -271,6 +317,7 @@ func main() {
 			"proposals": proposals,
 			"sealers":   sealers,
 			"vote":      propose,
+			"autovote":  autovote,
 		}
 		validCommands []string
 	)
@@ -287,8 +334,8 @@ func main() {
 		command = os.Args[1]
 	}
 	if commands[command] == nil {
-		fmt.Fprintf(os.Stderr, "Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(validCommands, "|"))
-		fmt.Fprintf(os.Stderr, "Para ayuda: %v <command> -h\n", path.Base(os.Args[0]))
+		log.Printf("Uso: %v <%v> [opciones]\n", path.Base(os.Args[0]), strings.Join(validCommands, "|"))
+		log.Printf("Para ayuda: %v <command> -h\n", path.Base(os.Args[0]))
 		os.Exit(1)
 	}
 	commands[command]()