diff --git a/cmd/params/params.go b/cmd/params/params.go
index eee50074..c9a971fc 100644
--- a/cmd/params/params.go
+++ b/cmd/params/params.go
@@ -226,8 +226,11 @@ func LoadAPIParams(v *viper.Viper) (API, error) {
 	backoffAtStr := vipertools.GetString(v, "internal.backoff_at")
 	if backoffAtStr != "" {
 		parsed, err := time.Parse(ini.DateFormat, backoffAtStr)
+		// nolint:gocritic
 		if err != nil {
 			log.Warnf("failed to parse backoff_at: %s", err)
+		} else if parsed.After(time.Now()) {
+			backoffAt = time.Now()
 		} else {
 			backoffAt = parsed
 		}
@@ -664,8 +667,11 @@ func LoadOfflineParams(v *viper.Viper) Offline {
 	lastSentAtStr := vipertools.GetString(v, "internal.heartbeats_last_sent_at")
 	if lastSentAtStr != "" {
 		parsed, err := time.Parse(ini.DateFormat, lastSentAtStr)
+		// nolint:gocritic
 		if err != nil {
 			log.Warnf("failed to parse heartbeats_last_sent_at: %s", err)
+		} else if parsed.After(time.Now()) {
+			lastSentAt = time.Now()
 		} else {
 			lastSentAt = parsed
 		}
diff --git a/cmd/params/params_test.go b/cmd/params/params_test.go
index 60ff54d8..9d29ce74 100644
--- a/cmd/params/params_test.go
+++ b/cmd/params/params_test.go
@@ -1771,6 +1771,16 @@ func TestLoadOfflineParams_LastSentAt_Err(t *testing.T) {
 	assert.Zero(t, params.LastSentAt)
 }
 
+func TestLoadOfflineParams_LastSentAtFuture(t *testing.T) {
+	v := viper.New()
+	lastSentAt := time.Now().Add(time.Duration(2) * time.Hour)
+	v.Set("internal.heartbeats_last_sent_at", lastSentAt.Format(inipkg.DateFormat))
+
+	params := cmdparams.LoadOfflineParams(v)
+
+	assert.LessOrEqual(t, params.LastSentAt, time.Now())
+}
+
 func TestLoadOfflineParams_SyncMax(t *testing.T) {
 	v := viper.New()
 	v.Set("sync-offline-activity", 42)
@@ -2148,6 +2158,22 @@ func TestLoadAPIParams_BackoffAtErr(t *testing.T) {
 	}, params)
 }
 
+func TestLoadAPIParams_BackoffAtFuture(t *testing.T) {
+	v := viper.New()
+	backoff := time.Now().Add(time.Duration(2) * time.Hour)
+
+	v.Set("hostname", "my-computer")
+	v.Set("key", "00000000-0000-4000-8000-000000000000")
+	v.Set("internal.backoff_at", backoff.Format(inipkg.DateFormat))
+	v.Set("internal.backoff_retries", "3")
+
+	params, err := cmdparams.LoadAPIParams(v)
+	require.NoError(t, err)
+
+	assert.Equal(t, 3, params.BackoffRetries)
+	assert.LessOrEqual(t, params.BackoffAt, time.Now())
+}
+
 func TestLoadAPIParams_DisableSSLVerify_FlagTakesPrecedence(t *testing.T) {
 	v := viper.New()
 	v.Set("key", "00000000-0000-4000-8000-000000000000")
diff --git a/cmd/root.go b/cmd/root.go
index 516dfd06..92373ebd 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -100,7 +100,7 @@ func setFlags(cmd *cobra.Command, v *viper.Viper) {
 	flags.String(
 		"entity-type",
 		"",
-		"Entity type for this heartbeat. Can be \"file\", \"domain\" or \"app\". Defaults to \"file\".",
+		"Entity type for this heartbeat. Can be \"file\", \"domain\", \"url\", or \"app\". Defaults to \"file\".",
 	)
 	flags.StringSlice(
 		"exclude",
diff --git a/pkg/filter/filter.go b/pkg/filter/filter.go
index ab274459..cf6eb8a1 100644
--- a/pkg/filter/filter.go
+++ b/pkg/filter/filter.go
@@ -63,12 +63,12 @@ func WithLengthValidator() heartbeat.HandleOption {
 func Filter(h heartbeat.Heartbeat, config Config) error {
 	// filter by pattern
 	if err := filterByPattern(h.Entity, config.Include, config.Exclude); err != nil {
-		return fmt.Errorf(fmt.Sprintf("filter by pattern: %s", err))
+		return fmt.Errorf("filter by pattern: %s", err)
 	}
 
 	err := filterFileEntity(h, config)
 	if err != nil {
-		return fmt.Errorf(fmt.Sprintf("filter file: %s", err))
+		return fmt.Errorf("filter file: %s", err)
 	}
 
 	return nil
@@ -92,7 +92,7 @@ func filterByPattern(entity string, include, exclude []regex.Regex) error {
 	// filter by  exclude pattern
 	for _, pattern := range exclude {
 		if pattern.MatchString(entity) {
-			return fmt.Errorf(fmt.Sprintf("skipping because matches exclude pattern %q", pattern.String()))
+			return fmt.Errorf("skipping because matches exclude pattern %q", pattern.String())
 		}
 	}
 
@@ -123,7 +123,7 @@ func filterFileEntity(h heartbeat.Heartbeat, config Config) error {
 
 	// skip files that don't exist on disk
 	if _, err := os.Stat(entity); os.IsNotExist(err) {
-		return fmt.Errorf(fmt.Sprintf("skipping because of non-existing file %q", entity))
+		return fmt.Errorf("skipping because of non-existing file %q", entity)
 	}
 
 	// when including only with project file, skip files when the project doesn't have a .wakatime-project file
diff --git a/pkg/heartbeat/entity.go b/pkg/heartbeat/entity.go
index 4b64bbf9..17e0714c 100644
--- a/pkg/heartbeat/entity.go
+++ b/pkg/heartbeat/entity.go
@@ -13,6 +13,10 @@ const (
 	FileType EntityType = iota
 	// DomainType represents a domain entity.
 	DomainType
+	// URLType represents a url entity without the url params.
+	URLType
+	// EventType represents a meeting or calendar event.
+	EventType
 	// AppType represents an app entity.
 	AppType
 )
@@ -20,6 +24,8 @@ const (
 const (
 	fileTypeString   = "file"
 	domainTypeString = "domain"
+	urlTypeString    = "url"
+	eventTypeString  = "event"
 	appTypeString    = "app"
 )
 
@@ -30,6 +36,10 @@ func ParseEntityType(s string) (EntityType, error) {
 		return FileType, nil
 	case domainTypeString:
 		return DomainType, nil
+	case urlTypeString:
+		return URLType, nil
+	case eventTypeString:
+		return EventType, nil
 	case appTypeString:
 		return AppType, nil
 	default:
@@ -68,6 +78,10 @@ func (t EntityType) String() string {
 		return fileTypeString
 	case DomainType:
 		return domainTypeString
+	case URLType:
+		return urlTypeString
+	case EventType:
+		return eventTypeString
 	case AppType:
 		return appTypeString
 	default:
diff --git a/pkg/heartbeat/entity_modifier_internal_test.go b/pkg/heartbeat/entity_modifier_internal_test.go
index 6b31371b..a528d652 100644
--- a/pkg/heartbeat/entity_modifier_internal_test.go
+++ b/pkg/heartbeat/entity_modifier_internal_test.go
@@ -41,6 +41,30 @@ func TestIsXCodePlayground(t *testing.T) {
 	}
 }
 
+func TestIsXCodeProject(t *testing.T) {
+	tests := map[string]struct {
+		Dir      string
+		Expected bool
+	}{
+		"project directory": {
+			Dir:      setupTestXCodePlayground(t, "wakatime.xcodeproj"),
+			Expected: true,
+		},
+		"not project": {
+			Dir:      setupTestXCodePlayground(t, "wakatime"),
+			Expected: false,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			ret := isXCodeProject(test.Dir)
+
+			assert.Equal(t, test.Expected, ret)
+		})
+	}
+}
+
 func setupTestXCodePlayground(t *testing.T, dir string) string {
 	tmpDir := t.TempDir()
 
diff --git a/pkg/heartbeat/entity_modify.go b/pkg/heartbeat/entity_modify.go
index 7977995f..f25a7d53 100644
--- a/pkg/heartbeat/entity_modify.go
+++ b/pkg/heartbeat/entity_modify.go
@@ -19,6 +19,10 @@ func WithEntityModifier() HandleOption {
 				if h.EntityType == FileType && isXCodePlayground(h.Entity) {
 					hh[n].Entity = filepath.Join(h.Entity, "Contents.swift")
 				}
+				// Support XCode projects
+				if h.EntityType == FileType && isXCodeProject(h.Entity) {
+					hh[n].Entity = filepath.Join(h.Entity, "project.pbxproj")
+				}
 			}
 
 			return next(hh)
@@ -35,3 +39,11 @@ func isXCodePlayground(fp string) bool {
 
 	return isDir(fp)
 }
+
+func isXCodeProject(fp string) bool {
+	if !(strings.HasSuffix(fp, ".xcodeproj")) {
+		return false
+	}
+
+	return isDir(fp)
+}
diff --git a/pkg/heartbeat/entity_test.go b/pkg/heartbeat/entity_test.go
index 7922c279..2d61afa8 100644
--- a/pkg/heartbeat/entity_test.go
+++ b/pkg/heartbeat/entity_test.go
@@ -14,6 +14,8 @@ func typeTests() map[string]heartbeat.EntityType {
 	return map[string]heartbeat.EntityType{
 		"file":   heartbeat.FileType,
 		"domain": heartbeat.DomainType,
+		"url":    heartbeat.URLType,
+		"event":  heartbeat.EventType,
 		"app":    heartbeat.AppType,
 	}
 }