diff --git a/README.md b/README.md
index fe9748e5..a412f297 100644
--- a/README.md
+++ b/README.md
@@ -40,3 +40,15 @@ Once a device has been created thorugh the provisioning procedure, it can be del
 
 Devices currently present on Arduino IoT Cloud can be retrieved by using this command:
 `$ iot-cloud-cli device list`
+
+## Thing commands
+
+Things can be created starting from a template or by cloning another thing. Additionally, a thing name should be specified.
+
+Create a thing from a template:
+
+`$ iot-cloud-cli thing create --name <thingName> --template <template.json>`
+
+Create a thing by cloning another thing:
+
+`$ iot-cloud-cli thing create --name <thingName> --clone-id <thingToCloneID>`
diff --git a/cli/root.go b/cli/root.go
index a999a1b8..ccc75821 100644
--- a/cli/root.go
+++ b/cli/root.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/arduino/iot-cloud-cli/cli/config"
 	"github.com/arduino/iot-cloud-cli/cli/device"
+	"github.com/arduino/iot-cloud-cli/cli/thing"
 	"github.com/spf13/cobra"
 )
 
@@ -13,6 +14,7 @@ func Execute() {
 	rootCmd := &cobra.Command{}
 	rootCmd.AddCommand(config.NewCommand())
 	rootCmd.AddCommand(device.NewCommand())
+	rootCmd.AddCommand(thing.NewCommand())
 
 	if err := rootCmd.Execute(); err != nil {
 		fmt.Fprintln(os.Stderr, err)
diff --git a/cli/thing/create.go b/cli/thing/create.go
new file mode 100644
index 00000000..1f48c44f
--- /dev/null
+++ b/cli/thing/create.go
@@ -0,0 +1,49 @@
+package thing
+
+import (
+	"fmt"
+
+	"github.com/arduino/iot-cloud-cli/command/thing"
+	"github.com/spf13/cobra"
+)
+
+var createFlags struct {
+	name     string
+	deviceID string
+	template string
+	cloneID  string
+}
+
+func initCreateCommand() *cobra.Command {
+	createCommand := &cobra.Command{
+		Use:   "create",
+		Short: "Create a thing",
+		Long:  "Create a thing for Arduino IoT Cloud",
+		RunE:  runCreateCommand,
+	}
+	createCommand.Flags().StringVarP(&createFlags.name, "name", "n", "", "Thing name")
+	createCommand.Flags().StringVarP(&createFlags.deviceID, "device-id", "d", "", "ID of Device to bind to the new thing")
+	createCommand.Flags().StringVarP(&createFlags.cloneID, "clone-id", "c", "", "ID of Thing to be cloned")
+	createCommand.Flags().StringVarP(&createFlags.template, "template", "t", "", "File containing a thing template")
+	createCommand.MarkFlagRequired("name")
+	return createCommand
+}
+
+func runCreateCommand(cmd *cobra.Command, args []string) error {
+	fmt.Printf("Creating thing with name %s\n", createFlags.name)
+
+	params := &thing.CreateParams{
+		Name:     createFlags.name,
+		DeviceID: createFlags.deviceID,
+		Template: createFlags.template,
+		CloneID:  createFlags.cloneID,
+	}
+
+	thingID, err := thing.Create(params)
+	if err != nil {
+		return err
+	}
+
+	fmt.Printf("IoT Cloud thing created with ID: %s\n", thingID)
+	return nil
+}
diff --git a/cli/thing/thing.go b/cli/thing/thing.go
new file mode 100644
index 00000000..a1ccce9e
--- /dev/null
+++ b/cli/thing/thing.go
@@ -0,0 +1,17 @@
+package thing
+
+import (
+	"github.com/spf13/cobra"
+)
+
+func NewCommand() *cobra.Command {
+	thingCommand := &cobra.Command{
+		Use:   "thing",
+		Short: "Thing commands.",
+		Long:  "Thing commands.",
+	}
+
+	thingCommand.AddCommand(initCreateCommand())
+
+	return thingCommand
+}
diff --git a/command/thing/create.go b/command/thing/create.go
new file mode 100644
index 00000000..b4537154
--- /dev/null
+++ b/command/thing/create.go
@@ -0,0 +1,125 @@
+package thing
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"errors"
+
+	iotclient "github.com/arduino/iot-client-go"
+	"github.com/arduino/iot-cloud-cli/internal/config"
+	"github.com/arduino/iot-cloud-cli/internal/iot"
+)
+
+// CreateParams contains the parameters needed to create a new thing.
+type CreateParams struct {
+	// Mandatory - contains the name of the thing
+	Name string
+	// Optional - contains the ID of the device to be bound to the thing
+	DeviceID string
+	// Mandatory if device is empty - contains the path of the template file
+	Template string
+	// Mandatory if template is empty- name of things to be cloned
+	CloneID string
+}
+
+// Create allows to create a new thing
+func Create(params *CreateParams) (string, error) {
+	if params.Template == "" && params.CloneID == "" {
+		return "", fmt.Errorf("%s", "provide either a thing(ID) to clone (--clone) or a thing template file (--template)\n")
+	}
+
+	conf, err := config.Retrieve()
+	if err != nil {
+		return "", err
+	}
+	iotClient, err := iot.NewClient(conf.Client, conf.Secret)
+	if err != nil {
+		return "", err
+	}
+
+	var thing *iotclient.Thing
+
+	if params.CloneID != "" {
+		thing, err = clone(iotClient, params.CloneID)
+		if err != nil {
+			return "", err
+		}
+
+	} else if params.Template != "" {
+		thing, err = loadTemplate(params.Template)
+		if err != nil {
+			return "", err
+		}
+
+	} else {
+		return "", errors.New("provide either a thing(ID) to clone (--clone) or a thing template file (--template)")
+	}
+
+	thing.Name = params.Name
+	force := true
+	if params.DeviceID != "" {
+		thing.DeviceId = params.DeviceID
+	}
+	thingID, err := iotClient.AddThing(thing, force)
+	if err != nil {
+		return "", err
+	}
+
+	return thingID, nil
+}
+
+func clone(client iot.Client, thingID string) (*iotclient.Thing, error) {
+	clone, err := client.GetThing(thingID)
+	if err != nil {
+		return nil, fmt.Errorf("%s: %w", "retrieving the thing to be cloned", err)
+	}
+
+	thing := &iotclient.Thing{}
+
+	// Copy device id
+	if clone.DeviceId != "" {
+		thing.DeviceId = clone.DeviceId
+	}
+
+	// Copy properties
+	for _, p := range clone.Properties {
+		thing.Properties = append(thing.Properties, iotclient.Property{
+			Name:            p.Name,
+			MinValue:        p.MinValue,
+			MaxValue:        p.MaxValue,
+			Permission:      p.Permission,
+			UpdateParameter: p.UpdateParameter,
+			UpdateStrategy:  p.UpdateStrategy,
+			Type:            p.Type,
+			VariableName:    p.VariableName,
+			Persist:         p.Persist,
+			Tag:             p.Tag,
+		})
+	}
+
+	return thing, nil
+}
+
+func loadTemplate(file string) (*iotclient.Thing, error) {
+	templateFile, err := os.Open(file)
+	if err != nil {
+		return nil, err
+	}
+	defer templateFile.Close()
+
+	templateBytes, err := ioutil.ReadAll(templateFile)
+	if err != nil {
+		return nil, err
+	}
+
+	thing := &iotclient.Thing{}
+	err = json.Unmarshal([]byte(templateBytes), thing)
+	if err != nil {
+		return nil, fmt.Errorf("%s: %w", "reading template file: template not valid: ", err)
+	}
+
+	return thing, nil
+}
diff --git a/go.mod b/go.mod
index 2c0205c1..87b09bd8 100644
--- a/go.mod
+++ b/go.mod
@@ -3,18 +3,21 @@ module github.com/arduino/iot-cloud-cli
 go 1.15
 
 require (
+	github.com/antihax/optional v1.0.0 // indirect
 	github.com/arduino/arduino-cli v0.0.0-20210607095659-16f41352eac3
 	github.com/arduino/go-paths-helper v1.6.0
-	github.com/arduino/iot-client-go v1.3.3
+	github.com/arduino/iot-client-go v1.3.4-0.20210824101852-4a44149473c1
 	github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6
 	github.com/sirupsen/logrus v1.4.2
 	github.com/spf13/cobra v1.1.3
 	github.com/spf13/viper v1.7.0
 	github.com/stretchr/testify v1.6.1
 	go.bug.st/serial v1.3.0
-	golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect
-	golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
+	golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
+	golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
 	golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
+	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto v0.0.0-20210504143626-3b2ad6ccc450 // indirect
 	google.golang.org/grpc v1.39.0
+	google.golang.org/protobuf v1.27.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 320ae417..89529be4 100644
--- a/go.sum
+++ b/go.sum
@@ -59,8 +59,8 @@ github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4l
 github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ=
 github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b h1:3PjgYG5gVPA7cipp7vIR2lF96KkEJIFBJ+ANnuv6J20=
 github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8=
-github.com/arduino/iot-client-go v1.3.3 h1:W+92osS+WcdVpePdPmj/BtupM+xV6DOJlI0HGpKrTX4=
-github.com/arduino/iot-client-go v1.3.3/go.mod h1:gYvpMt7Qw+OSScTLyIlCnpbvy9y96ey/2zhB4w6FoK0=
+github.com/arduino/iot-client-go v1.3.4-0.20210824101852-4a44149473c1 h1:tgVUBPbqkyd3KHTs+gweP5t9KAnkLbAsAMrHvu9jZSg=
+github.com/arduino/iot-client-go v1.3.4-0.20210824101852-4a44149473c1/go.mod h1:gYvpMt7Qw+OSScTLyIlCnpbvy9y96ey/2zhB4w6FoK0=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -494,15 +494,15 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8=
-golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
+golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -638,8 +638,9 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
 google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -701,8 +702,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/iot/client.go b/internal/iot/client.go
index a48a308f..c8ee311c 100644
--- a/internal/iot/client.go
+++ b/internal/iot/client.go
@@ -2,8 +2,10 @@ package iot
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 
+	"github.com/antihax/optional"
 	iotclient "github.com/arduino/iot-client-go"
 )
 
@@ -13,6 +15,8 @@ type Client interface {
 	DeleteDevice(id string) error
 	ListDevices() ([]iotclient.ArduinoDevicev2, error)
 	AddCertificate(id, csr string) (*iotclient.ArduinoCompressedv2, error)
+	AddThing(thing *iotclient.Thing, force bool) (string, error)
+	GetThing(id string) (*iotclient.ArduinoThing, error)
 }
 
 type client struct {
@@ -89,6 +93,30 @@ func (cl *client) AddCertificate(id, csr string) (*iotclient.ArduinoCompressedv2
 	return &newCert.Compressed, nil
 }
 
+// AddThing adds a new thing on Arduino IoT Cloud.
+func (cl *client) AddThing(thing *iotclient.Thing, force bool) (string, error) {
+	opt := &iotclient.ThingsV2CreateOpts{Force: optional.NewBool(force)}
+	newThing, resp, err := cl.api.ThingsV2Api.ThingsV2Create(cl.ctx, *thing, opt)
+	if err != nil {
+		var respObj map[string]interface{}
+		json.NewDecoder(resp.Body).Decode(&respObj)
+		resp.Body.Close()
+		return "", fmt.Errorf("%s: %s: %v", "adding new thing", err, respObj)
+	}
+	return newThing.Id, nil
+}
+
+// GetThing allows to retrieve a specific thing, given its id,
+// from Arduino IoT Cloud.
+func (cl *client) GetThing(id string) (*iotclient.ArduinoThing, error) {
+	thing, _, err := cl.api.ThingsV2Api.ThingsV2Show(cl.ctx, id, nil)
+	if err != nil {
+		err = fmt.Errorf("retrieving thing, %w", err)
+		return nil, err
+	}
+	return &thing, nil
+}
+
 func (cl *client) setup(client, secret string) error {
 	// Get the access token in exchange of client_id and client_secret
 	tok, err := token(client, secret)