New Upstream Snapshot - golang-github-hetznercloud-hcloud-go

Ready changes

Summary

Merged new upstream version: 1.39.0+git20230208.1.44af6e5+ds (was: 1.35.3).

Diff

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 87533d9..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: Continuous Integration
-on: [push, pull_request]
-jobs:
-  build:
-    name: Build
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        go-version: [1.17, 1.18, 1.19]
-    steps:
-    - name: Set up Go ${{ matrix.go-version }}
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-    - name: Check out code into the Go module directory
-      uses: actions/checkout@v1
-    - name: Run tests
-      run: go test -v ./...
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
deleted file mode 100644
index 88bb867..0000000
--- a/.github/workflows/golangci-lint.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: golangci-lint
-on:
-  push:
-    tags:
-      - v*
-    branches:
-      - master
-      - main
-  pull_request:
-jobs:
-  golangci:
-    name: lint
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - name: golangci-lint
-        uses: golangci/golangci-lint-action@v2
-        with:
-          # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
-          version: latest
-
-          # Optional: working directory, useful for monorepos
-          # working-directory: somedir
-
-          # Optional: golangci-lint command line arguments.
-          # args: --issues-exit-code=0
-
-          # Optional: show only new issues if it's a pull request. The default value is `false`.
-          # only-new-issues: true
-
-          # Optional: if set to true then the action will use pre-installed Go
-          # skip-go-installation: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 69e16b3..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: Release Changelog
-on:
-  push:
-    tags:
-      - '*'
-jobs:
-  build:
-    name: Build
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-        with:
-          fetch-depth: 0
-      - name: Set up Go
-        uses: actions/setup-go@v2
-        with:
-          go-version: 1.19
-      - name: Run GoReleaser
-        uses: goreleaser/goreleaser-action@v2
-        with:
-          distribution: goreleaser
-          version: latest
-          args: release --rm-dist
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 849ddff..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-dist/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index f95e854..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-stages:
-  - test
-
-variables:
-  GIT_SUBMODULE_STRATEGY: normal
-
-test:golangci-lint:
-  stage: test
-  image: golangci/golangci-lint:latest
-  script:
-    - golangci-lint run -v
-  except:
-    - tags
-    - master
-  tags:
-    - hc-bladerunner
-
-test:tests:
-  stage: test
-  image: golang:1.18
-  script:
-    - go test -v -race ./...
-  except:
-    - tags
-    - master
-  tags:
-    - hc-bladerunner
diff --git a/.golangci.yaml b/.golangci.yaml
index 3e86fc8..ef87e03 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -4,6 +4,11 @@ linters-settings:
     exclude: ./.errcheck_excludes.txt
   exhaustive:
     default-signifies-exhaustive: true
+  gci:
+    sections:
+      - standard
+      - default
+      - prefix(github.com/hetznercloud)
   gomodguard:
     blocked:
       modules:
@@ -27,13 +32,15 @@ linters:
   disable-all: true
   enable:
     - bodyclose
-    - deadcode
     - depguard
     - errcheck
+    - errname
     - exhaustive
+    - exportloopref
+    - gci
     - gocritic
+    - godot
     - goimports
-    - golint
     - gomodguard
     - gosec
     - gosimple
@@ -41,14 +48,11 @@ linters:
     - ineffassign
     - misspell
     - prealloc
-    - rowserrcheck
-    - scopelint
+    - revive
     - staticcheck
-    - structcheck
     - typecheck
     - unparam
     - unused
-    - varcheck
     - whitespace
 
 issues:
@@ -56,8 +60,4 @@ issues:
     - path: _test\.go
       linters:
         - gosec
-        - scopelint
         - errcheck
-    - linters:
-        - gosec
-      text: "G204:"
diff --git a/.goreleaser.yml b/.goreleaser.yml
deleted file mode 100644
index f1f8e5d..0000000
--- a/.goreleaser.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-builds:
-  - skip: true
-changelog:
-  sort: asc
-  filters:
-    exclude:
-      - '^docs:'
-      - '^test:'
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c4281bf
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,337 @@
+# Changelog
+
+## [1.39.0](https://github.com/hetznercloud/hcloud-go/compare/v1.38.0...v1.39.0) (2022-12-29)
+
+
+### Features
+
+* Use generics to get pointers to types ([#219](https://github.com/hetznercloud/hcloud-go/issues/219)) ([a5cd797](https://github.com/hetznercloud/hcloud-go/commit/a5cd79782dc849b3137e46ada2da6b319d4093c8))
+
+
+### Bug Fixes
+
+* deprecate PricingPrimaryIPTypePrice.Datacenter for Location ([#222](https://github.com/hetznercloud/hcloud-go/issues/222)) ([e0e5a1e](https://github.com/hetznercloud/hcloud-go/commit/e0e5a1e08fd7c0864fd94a787ee86714b5e9afc5))
+
+## v1.38.0
+
+### What's Changed
+* feat(network): add new Network Zone us-west by @apricote in https://github.com/hetznercloud/hcloud-go/pull/217
+* chore: prepare v1.38.0 by @apricote in https://github.com/hetznercloud/hcloud-go/pull/218
+
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.37.0...v1.38.0
+
+## v1.37.0
+
+### What's Changed
+* PrimaryIPClient Add AllWithOpts by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/214
+* fix: error when updating IPv6 Primary IP by @apricote in https://github.com/hetznercloud/hcloud-go/pull/215
+
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.36.0...v1.37.0
+
+## v1.36.0
+
+### What's Changed
+* feat: add ServerClient.DeleteWithResult method by @apricote in https://github.com/hetznercloud/hcloud-go/pull/213
+
+### New Contributors
+* @apricote made their first contribution in https://github.com/hetznercloud/hcloud-go/pull/213
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.35.3...v1.36.0
+
+## v1.35.3
+
+### What's Changed
+* Drop support for Go < 1.17 and add official tests on go 1.19 by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/211
+* Stop automatic retrying on RateLimitExceeded by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/210
+
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.35.2...v1.35.3
+
+## v1.35.2
+
+### What's Changed
+* Allow empty labels by @4ND3R50N in https://github.com/hetznercloud/hcloud-go/pull/207
+
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.35.1...v1.35.2
+
+## v1.35.1
+
+### What's Changed
+* Accept no primary IPs with server create with StartAfterCreate = false by @4ND3R50N in https://github.com/hetznercloud/hcloud-go/pull/205
+
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.35.0...v1.35.1
+
+## v1.35.0
+
+### What's Changed
+* Catch invalid token values and error out without value exposure by @NotTheEvilOne in https://github.com/hetznercloud/hcloud-go/pull/194
+* Remove ServerRescueTypeFreeBSD64 by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/203
+* Add Primary IP Support by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/204
+
+### New Contributors
+* @NotTheEvilOne made their first contribution in https://github.com/hetznercloud/hcloud-go/pull/194
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.34.0...v1.35.0
+
+## v1.34.0
+
+### What's Changed
+* Test on Go 1.18 by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/202
+* Add support for sorting the response of all list calls by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/201
+* Set UsePrivateIP for targets when creating a LoadBalancer by @hakman in https://github.com/hetznercloud/hcloud-go/pull/198
+
+### New Contributors
+* @hakman made their first contribution in https://github.com/hetznercloud/hcloud-go/pull/198
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.33.2...v1.34.0
+
+## v1.33.2
+
+### What's Changed
+* Add constant for resource locked error code by @patrickschaffrath in https://github.com/hetznercloud/hcloud-go/pull/189
+* Fix metadata client error detection by @choffmeister in https://github.com/hetznercloud/hcloud-go/pull/193
+* Add labels.go to validate resource labels by @4ND3R50N in https://github.com/hetznercloud/hcloud-go/pull/197
+
+### New Contributors
+* @patrickschaffrath made their first contribution in https://github.com/hetznercloud/hcloud-go/pull/189
+* @choffmeister made their first contribution in https://github.com/hetznercloud/hcloud-go/pull/193
+* @4ND3R50N made their first contribution in https://github.com/hetznercloud/hcloud-go/pull/197
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.33.1...v1.33.2
+
+## v1.33.1
+
+### Changelog
+
+41fef2f Add constants for new firewall error code
+
+## v1.33.0
+
+### What's Changed
+* Add us-east network zone by @LKaemmerling in https://github.com/hetznercloud/hcloud-go/pull/187
+
+
+**Full Changelog**: https://github.com/hetznercloud/hcloud-go/compare/v1.32.0...v1.33.0
+
+## v1.32.0
+
+### Changelog
+
+785896c Add Metadata Client (#184)
+7778143 Bump version to 1.32.0
+eabe683 Make it possible to instrument the hcloud-go (#185)
+
+## v1.31.1
+
+### Changelog
+
+1cde0d7 Add RDNS client (#183)
+
+## v1.31.0
+
+### Changelog
+
+0656bf9 Add support for Load Balancer DNS PTRs (#182)
+9218970 Bump hcloud.Version
+ebf9f25 Test on/with go 1.17 (#181)
+
+## v1.30.0
+
+### Changelog
+
+592b198 Add new Floating IP Pricing structure (#180)
+c43897a Bump hcloud.Version
+ab0ebb2 Placement groups (#179)
+
+## v1.29.1
+
+### Changelog
+
+e951e99 Fix firewall rule description
+
+## v1.29.0
+
+### Changelog
+
+e3eca12 Add description field to firewall rules (#177)
+
+## v1.28.0
+
+### Changelog
+
+ae5e1b8 Add goreleaser (#175)
+4cf75f4 Add support for app images (#176)
+
+## v1.27.0
+
+Add support for firewall protocols ESP and GRE (#173) 
+
+## v1.26.2
+
+- Fix AppliedTo Field in FirewallCreateRequest
+- Add `deleted` and `IsDeleted()` to `Image` (https://github.com/hetznercloud/hcloud-go/pull/172)
+
+## v1.26.1
+
+- Fix validation error caused by firewall label selectors (https://github.com/hetznercloud/hcloud-go/pull/171)
+
+## v1.26.0
+
+- Add Firewall Resource Label Selector (#169)
+
+## v1.25.0
+
+* Support Hetzner Cloud managed Certificates (#167)
+
+## v1.23.1
+* Add removed `ErrorCodeServerAlreadyAttached` again
+
+## v1.23.0
+
+* Add missing constants for all resource specific error codes
+* Expose metrics for Servers and Load Balancers
+* Add support for vSwitch Subnetworks
+
+## v1.22.0
+
+* Add `PrimaryDiskSize` Field to `Server`
+
+## v1.21.1
+
+* Don't send `Authorization` Header when `WithToken` was not called
+
+## v1.21.0
+
+* Add `IncludeDeprecated` Field to `ImageListOpts`
+
+## v1.20.0
+
+* Add support for Load Balancer Label Selector targets
+* Add support for Load Balancer IP targets
+
+## v1.19.0
+
+* Fix nil pointer dereference when creating a Load Balancer with HTTP(S)
+  service and not providing HTTP-specific options
+* Add `IncludedTraffic`, `OutgoingTraffic` and `IngoingTraffic` fields to `LoadBalancer`
+* Add `ChangeType()` method to the Load Balancer client
+* Fix retrying of requests that contain a body
+
+## v1.18.2
+
+* Retry API requests on conflict error
+
+## v1.18.1
+
+* Make all `GetByName` methods return `nil` when an empty name is provided
+* Clarify that filters specified in options for List() calls are not taken
+  into account when their value corresponds to their zero value or when
+  they are empty.
+
+## v1.18.0
+
+* Add `Status` field to `Volume`
+* Add subnet type `cloud`
+* Add `WithHTTPClient` option to specify a custom `http.Client`
+* Add API for requesting a VNC console
+* Add support for load balancers and certificates (beta)
+
+## v1.17.0
+
+* Add `Created` field to `SSHKey`
+
+## v1.16.0
+
+* Make IP range optional when adding a subnet to a network
+* Add support for names to Floating IPs
+
+## v1.15.1
+
+* Rename `MacAddress` to `MACAddress` on `ServerPrivateNet`
+
+## v1.15.0
+
+* Add `MacAddress` field to `ServerPrivateNet`
+* Add `WithDebugWriter()` client option to provide an `io.Writer` to write debug output to
+
+## v1.14.0
+
+* Add `Created` field to `FloatingIP`
+* Add support for networks
+
+## v1.13.0
+
+* Add missing fields to `*ListOpts` structs
+* Fix error handling in `WatchProgress()`
+* Add support for filtering volumes, images, and servers by status
+
+## v1.12.0
+
+* Add missing constants for all [documented error codes](https://docs.hetzner.cloud/#overview-errors)
+* Add support for automounting volumes
+* Add support for attaching volumes when creating a server
+
+## v1.11.0
+
+* Add `NextActions` to `ServerCreateResult` and `VolumeCreateResult`
+
+## v1.10.0
+
+* Add `WithApplication()` client option to provide an application name and version
+  that will be included in the `User-Agent` HTTP header
+* Add support for volumes
+
+## v1.9.0
+
+* Add `AllWithOpts()` to server, Floating IP, image, and SSH key client
+* Expose labels of servers, Floating IPs, images, and SSH Keys
+
+## v1.8.0
+
+* Add `WithPollInterval()` option to `Client` which allows to specify the polling interval
+  ([issue #92](https://github.com/hetznercloud/hcloud-go/issues/92))
+* Add `CPUType` field to `ServerType` ([issue #91](https://github.com/hetznercloud/hcloud-go/pull/91))
+
+## v1.7.0
+
+* Add `Deprecated ` field to `Image` ([issue #88](https://github.com/hetznercloud/hcloud-go/issues/88))
+* Add `StartAfterCreate` flag to `ServerCreateOpts` ([issue #87](https://github.com/hetznercloud/hcloud-go/issues/87))
+* Fix enum types ([issue #89](https://github.com/hetznercloud/hcloud-go/issues/89))
+
+## v1.6.0
+
+* Add `ChangeProtection()` to server, Floating IP, and image client
+* Expose protection of servers, Floating IPs, and images
+
+## v1.5.0
+
+* Add `GetByFingerprint()` to SSH key client
+
+## v1.4.0
+
+* Retry all calls that triggered the API ratelimit
+* Slow down `WatchProgress()` in action client from 100ms polling interval to 500ms
+
+## v1.3.1
+
+* Make clients using the old error code for ratelimiting work as expected
+  ([issue #73](https://github.com/hetznercloud/hcloud-go/issues/73))
+
+## v1.3.0
+
+* Support passing user data on server creation ([issue #70](https://github.com/hetznercloud/hcloud-go/issues/70))
+* Fix leaking response body by not closing it ([issue #68](https://github.com/hetznercloud/hcloud-go/issues/68))
+
+## v1.2.0
+
+* Add `WatchProgress()` to action client
+* Use correct error code for ratelimit error (deprecated
+  `ErrorCodeLimitReached`, added `ErrorCodeRateLimitExceeded`)
+
+## v1.1.0
+
+* Add `Image` field to `Server`
diff --git a/CHANGES.md b/CHANGES.md
deleted file mode 100644
index efa8015..0000000
--- a/CHANGES.md
+++ /dev/null
@@ -1,152 +0,0 @@
-# Changes
-
-As of release v1.24.0 we moved the release notes to Github Releases: https://github.com/hetznercloud/hcloud-go/releases
-
-## v1.23.1
-* Add removed `ErrorCodeServerAlreadyAttached` again
-
-## v1.23.0
-
-* Add missing constants for all resource specific error codes
-* Expose metrics for Servers and Load Balancers
-* Add support for vSwitch Subnetworks
-
-## v1.22.0
-
-* Add `PrimaryDiskSize` Field to `Server`
-
-## v1.21.1
-
-* Don't send `Authorization` Header when `WithToken` was not called
-
-## v1.21.0
-
-* Add `IncludeDeprecated` Field to `ImageListOpts`
-
-## v1.20.0
-
-* Add support for Load Balancer Label Selector targets
-* Add support for Load Balancer IP targets
-
-## v1.19.0
-
-* Fix nil pointer dereference when creating a Load Balancer with HTTP(S)
-  service and not providing HTTP-specific options
-* Add `IncludedTraffic`, `OutgoingTraffic` and `IngoingTraffic` fields to `LoadBalancer`
-* Add `ChangeType()` method to the Load Balancer client
-* Fix retrying of requests that contain a body
-
-## v1.18.2
-
-* Retry API requests on conflict error
-
-## v1.18.1
-
-* Make all `GetByName` methods return `nil` when an empty name is provided
-* Clarify that filters specified in options for List() calls are not taken
-  into account when their value corresponds to their zero value or when
-  they are empty.
-
-## v1.18.0
-
-* Add `Status` field to `Volume`
-* Add subnet type `cloud`
-* Add `WithHTTPClient` option to specify a custom `http.Client`
-* Add API for requesting a VNC console
-* Add support for load balancers and certificates (beta)
-
-## v1.17.0
-
-* Add `Created` field to `SSHKey`
-
-## v1.16.0
-
-* Make IP range optional when adding a subnet to a network
-* Add support for names to Floating IPs
-
-## v1.15.1
-
-* Rename `MacAddress` to `MACAddress` on `ServerPrivateNet`
-
-## v1.15.0
-
-* Add `MacAddress` field to `ServerPrivateNet`
-* Add `WithDebugWriter()` client option to provide an `io.Writer` to write debug output to
-
-## v1.14.0
-
-* Add `Created` field to `FloatingIP`
-* Add support for networks
-
-## v1.13.0
-
-* Add missing fields to `*ListOpts` structs
-* Fix error handling in `WatchProgress()`
-* Add support for filtering volumes, images, and servers by status
-
-## v1.12.0
-
-* Add missing constants for all [documented error codes](https://docs.hetzner.cloud/#overview-errors)
-* Add support for automounting volumes
-* Add support for attaching volumes when creating a server
-
-## v1.11.0
-
-* Add `NextActions` to `ServerCreateResult` and `VolumeCreateResult`
-
-## v1.10.0
-
-* Add `WithApplication()` client option to provide an application name and version
-  that will be included in the `User-Agent` HTTP header
-* Add support for volumes
-
-## v1.9.0
-
-* Add `AllWithOpts()` to server, Floating IP, image, and SSH key client
-* Expose labels of servers, Floating IPs, images, and SSH Keys
-
-## v1.8.0
-
-* Add `WithPollInterval()` option to `Client` which allows to specify the polling interval
-  ([issue #92](https://github.com/hetznercloud/hcloud-go/issues/92))
-* Add `CPUType` field to `ServerType` ([issue #91](https://github.com/hetznercloud/hcloud-go/pull/91))
-
-## v1.7.0
-
-* Add `Deprecated ` field to `Image` ([issue #88](https://github.com/hetznercloud/hcloud-go/issues/88))
-* Add `StartAfterCreate` flag to `ServerCreateOpts` ([issue #87](https://github.com/hetznercloud/hcloud-go/issues/87))
-* Fix enum types ([issue #89](https://github.com/hetznercloud/hcloud-go/issues/89))
-
-## v1.6.0
-
-* Add `ChangeProtection()` to server, Floating IP, and image client
-* Expose protection of servers, Floating IPs, and images
-
-## v1.5.0
-
-* Add `GetByFingerprint()` to SSH key client
-
-## v1.4.0
-
-* Retry all calls that triggered the API ratelimit
-* Slow down `WatchProgress()` in action client from 100ms polling interval to 500ms
-
-## v1.3.1
-
-* Make clients using the old error code for ratelimiting work as expected
-  ([issue #73](https://github.com/hetznercloud/hcloud-go/issues/73))
-
-## v1.3.0
-
-* Support passing user data on server creation ([issue #70](https://github.com/hetznercloud/hcloud-go/issues/70))
-* Fix leaking response body by not closing it ([issue #68](https://github.com/hetznercloud/hcloud-go/issues/68))
-
-## v1.2.0
-
-* Add `WatchProgress()` to action client
-* Use correct error code for ratelimit error (deprecated
-  `ErrorCodeLimitReached`, added `ErrorCodeRateLimitExceeded`)
-
-## v1.1.0
-
-* Add `Image` field to `Server`
diff --git a/README.md b/README.md
index 2263cb9..238c6ec 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,14 @@ func main() {
 }
 ```
 
+## Go Version Support
+
+The library supports the latest two Go minor versions, e.g. at the time Go 1.19 is released, it supports Go 1.18 and 1.19.
+
+This matches the official [Go Release Policy](https://go.dev/doc/devel/release#policy).
+
+When the minimum required Go version is changed, it is announced in the release notes for that version.
+
 ## License
 
 MIT license
diff --git a/debian/changelog b/debian/changelog
index a894207..1e29ea4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-hetznercloud-hcloud-go (1.39.0+git20230208.1.44af6e5+ds-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 08 Feb 2023 23:46:47 -0000
+
 golang-github-hetznercloud-hcloud-go (1.35.3-1) unstable; urgency=medium
 
   * New upstream release
diff --git a/go.mod b/go.mod
index 33a3399..d685473 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/hetznercloud/hcloud-go
 
-go 1.18
+go 1.19
 
 require (
 	github.com/google/go-cmp v0.5.5
diff --git a/hcloud/action.go b/hcloud/action.go
index 622f7bf..e915d75 100644
--- a/hcloud/action.go
+++ b/hcloud/action.go
@@ -181,7 +181,24 @@ func (c *ActionClient) AllWithOpts(ctx context.Context, opts ActionListOpts) ([]
 	return allActions, nil
 }
 
-// WatchOverallProgress watches several actions' progress until they complete with success or error.
+// WatchOverallProgress watches several actions' progress until they complete
+// with success or error. This watching happens in a goroutine and updates are
+// provided through the two returned channels:
+//
+//   - The first channel receives percentage updates of the progress, based on
+//     the number of completed versus total watched actions. The return value
+//     is an int between 0 and 100.
+//   - The second channel returned receives errors for actions that did not
+//     complete successfully, as well as any errors that happened while
+//     querying the API.
+//
+// By default the method keeps watching until all actions have finished
+// processing. If you want to be able to cancel the method or configure a
+// timeout, use the [context.Context]. Once the method has stopped watching,
+// both returned channels are closed.
+//
+// WatchOverallProgress uses the [WithPollBackoffFunc] of the [Client] to wait
+// until sending the next request.
 func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Action) (<-chan int, <-chan error) {
 	errCh := make(chan error, len(actions))
 	progressCh := make(chan int)
@@ -196,15 +213,15 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
 			watchIDs[action.ID] = struct{}{}
 		}
 
-		ticker := time.NewTicker(c.client.pollInterval)
-		defer ticker.Stop()
+		retries := 0
+
 		for {
 			select {
 			case <-ctx.Done():
 				errCh <- ctx.Err()
 				return
-			case <-ticker.C:
-				break
+			case <-time.After(c.client.pollBackoffFunc(retries)):
+				retries++
 			}
 
 			opts := ActionListOpts{}
@@ -241,7 +258,24 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
 	return progressCh, errCh
 }
 
-// WatchProgress watches one action's progress until it completes with success or error.
+// WatchProgress watches one action's progress until it completes with success
+// or error. This watching happens in a goroutine and updates are provided
+// through the two returned channels:
+//
+//   - The first channel receives percentage updates of the progress, based on
+//     the progress percentage indicated by the API. The return value is an int
+//     between 0 and 100.
+//   - The second channel receives any errors that happened while querying the
+//     API, as well as the error of the action if it did not complete
+//     successfully, or nil if it did.
+//
+// By default the method keeps watching until the action has finished
+// processing. If you want to be able to cancel the method or configure a
+// timeout, use the [context.Context]. Once the method has stopped watching,
+// both returned channels are closed.
+//
+// WatchProgress uses the [WithPollBackoffFunc] of the [Client] to wait until
+// sending the next request.
 func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-chan int, <-chan error) {
 	errCh := make(chan error, 1)
 	progressCh := make(chan int)
@@ -250,16 +284,15 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha
 		defer close(errCh)
 		defer close(progressCh)
 
-		ticker := time.NewTicker(c.client.pollInterval)
-		defer ticker.Stop()
+		retries := 0
 
 		for {
 			select {
 			case <-ctx.Done():
 				errCh <- ctx.Err()
 				return
-			case <-ticker.C:
-				break
+			case <-time.After(c.client.pollBackoffFunc(retries)):
+				retries++
 			}
 
 			a, _, err := c.GetByID(ctx, action.ID)
diff --git a/hcloud/action_test.go b/hcloud/action_test.go
index 28e32f7..660f3de 100644
--- a/hcloud/action_test.go
+++ b/hcloud/action_test.go
@@ -3,7 +3,9 @@ package hcloud
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"net/http"
+	"reflect"
 	"testing"
 	"time"
 
@@ -164,6 +166,126 @@ func TestActionClientAll(t *testing.T) {
 	}
 }
 
+func TestActionClientWatchOverallProgress(t *testing.T) {
+	env := newTestEnv()
+	defer env.Teardown()
+
+	callCount := 0
+
+	env.Mux.HandleFunc("/actions", func(w http.ResponseWriter, r *http.Request) {
+		callCount++
+		var actions []schema.Action
+
+		switch callCount {
+		case 1:
+			actions = []schema.Action{
+				{
+					ID:       1,
+					Status:   "running",
+					Progress: 50,
+				},
+				{
+					ID:       2,
+					Status:   "running",
+					Progress: 50,
+				},
+			}
+		case 2:
+			actions = []schema.Action{
+				{
+					ID:       1,
+					Status:   "running",
+					Progress: 75,
+				},
+				{
+					ID:       2,
+					Status:   "error",
+					Progress: 100,
+					Error: &schema.ActionError{
+						Code:    "action_failed",
+						Message: "action failed",
+					},
+				},
+			}
+		case 3:
+			actions = []schema.Action{
+				{
+					ID:       1,
+					Status:   "success",
+					Progress: 100,
+				},
+			}
+		default:
+			t.Errorf("unexpected number of calls to the test server: %v", callCount)
+		}
+
+		w.Header().Set("Content-Type", "application/json")
+		_ = json.NewEncoder(w).Encode(struct {
+			Actions []schema.Action `json:"actions"`
+			Meta    schema.Meta     `json:"meta"`
+		}{
+			Actions: actions,
+			Meta: schema.Meta{
+				Pagination: &schema.MetaPagination{
+					Page:         1,
+					LastPage:     1,
+					PerPage:      len(actions),
+					TotalEntries: len(actions),
+				},
+			},
+		})
+	})
+
+	actions := []*Action{
+		{
+			ID:     1,
+			Status: ActionStatusRunning,
+		},
+		{
+			ID:     2,
+			Status: ActionStatusRunning,
+		},
+	}
+
+	ctx := context.Background()
+	progressCh, errCh := env.Client.Action.WatchOverallProgress(ctx, actions)
+	progressUpdates := []int{}
+	errs := []error{}
+
+	moreProgress, moreErrors := true, true
+
+	for moreProgress || moreErrors {
+		var progress int
+		var err error
+
+		select {
+		case progress, moreProgress = <-progressCh:
+			if moreProgress {
+				progressUpdates = append(progressUpdates, progress)
+			}
+		case err, moreErrors = <-errCh:
+			if moreErrors {
+				errs = append(errs, err)
+			}
+		}
+	}
+
+	if len(errs) != 1 {
+		t.Fatalf("expected to receive one error: %v", errs)
+	}
+
+	err := errs[0]
+
+	if e, ok := errors.Unwrap(err).(ActionError); !ok || e.Code != "action_failed" {
+		t.Fatalf("expected hcloud.Error, but got: %#v", err)
+	}
+
+	expectedProgressUpdates := []int{50}
+	if !reflect.DeepEqual(progressUpdates, expectedProgressUpdates) {
+		t.Fatalf("expected progresses %v but received %v", expectedProgressUpdates, progressUpdates)
+	}
+}
+
 func TestActionClientWatchProgress(t *testing.T) {
 	env := newTestEnv()
 	defer env.Teardown()
diff --git a/hcloud/certificate.go b/hcloud/certificate.go
index 04334de..6278552 100644
--- a/hcloud/certificate.go
+++ b/hcloud/certificate.go
@@ -31,10 +31,10 @@ const (
 	CertificateStatusTypePending CertificateStatusType = "pending"
 	CertificateStatusTypeFailed  CertificateStatusType = "failed"
 
-	// only in issuance
+	// only in issuance.
 	CertificateStatusTypeCompleted CertificateStatusType = "completed"
 
-	// only in renewal
+	// only in renewal.
 	CertificateStatusTypeScheduled   CertificateStatusType = "scheduled"
 	CertificateStatusTypeUnavailable CertificateStatusType = "unavailable"
 )
diff --git a/hcloud/certificate_test.go b/hcloud/certificate_test.go
index 055351a..003bcb5 100644
--- a/hcloud/certificate_test.go
+++ b/hcloud/certificate_test.go
@@ -405,7 +405,7 @@ func TestCertificateClientUpdate(t *testing.T) {
 			t.Fatal(err)
 		}
 		expectedReqBody := schema.CertificateUpdateRequest{
-			Name: String("test"),
+			Name: Ptr("test"),
 			Labels: func() *map[string]string {
 				labels := map[string]string{"key": "value"}
 				return &labels
diff --git a/hcloud/client.go b/hcloud/client.go
index 792be9b..f3b5a83 100644
--- a/hcloud/client.go
+++ b/hcloud/client.go
@@ -15,10 +15,11 @@ import (
 	"strings"
 	"time"
 
-	"github.com/hetznercloud/hcloud-go/hcloud/internal/instrumentation"
-	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 	"github.com/prometheus/client_golang/prometheus"
 	"golang.org/x/net/http/httpguts"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/internal/instrumentation"
+	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
 // Endpoint is the base URL of the API.
@@ -42,7 +43,10 @@ func ConstantBackoff(d time.Duration) BackoffFunc {
 }
 
 // ExponentialBackoff returns a BackoffFunc which implements an exponential
-// backoff using the formula: b^retries * d
+// backoff.
+// It uses the formula:
+//
+//	b^retries * d
 func ExponentialBackoff(b float64, d time.Duration) BackoffFunc {
 	return func(retries int) time.Duration {
 		return time.Duration(math.Pow(b, float64(retries))) * d
@@ -54,8 +58,8 @@ type Client struct {
 	endpoint                string
 	token                   string
 	tokenValid              bool
-	pollInterval            time.Duration
 	backoffFunc             BackoffFunc
+	pollBackoffFunc         BackoffFunc
 	httpClient              *http.Client
 	applicationName         string
 	applicationVersion      string
@@ -102,15 +106,31 @@ func WithToken(token string) ClientOption {
 	}
 }
 
-// WithPollInterval configures a Client to use the specified interval when polling
-// from the API.
+// WithPollInterval configures a Client to use the specified interval when
+// polling from the API.
+//
+// Deprecated: Setting the poll interval is deprecated, you can now configure
+// [WithPollBackoffFunc] with a [ConstantBackoff] to get the same results. To
+// migrate your code, replace your usage like this:
+//
+//	// before
+//	hcloud.WithPollInterval(2 * time.Second)
+//	// now
+//	hcloud.WithPollBackoffFunc(hcloud.ConstantBackoff(2 * time.Second))
 func WithPollInterval(pollInterval time.Duration) ClientOption {
+	return WithPollBackoffFunc(ConstantBackoff(pollInterval))
+}
+
+// WithPollBackoffFunc configures a Client to use the specified backoff
+// function when polling from the API.
+func WithPollBackoffFunc(f BackoffFunc) ClientOption {
 	return func(client *Client) {
-		client.pollInterval = pollInterval
+		client.backoffFunc = f
 	}
 }
 
 // WithBackoffFunc configures a Client to use the specified backoff function.
+// The backoff function is used for retrying HTTP requests.
 func WithBackoffFunc(f BackoffFunc) ClientOption {
 	return func(client *Client) {
 		client.backoffFunc = f
@@ -152,11 +172,11 @@ func WithInstrumentation(registry *prometheus.Registry) ClientOption {
 // NewClient creates a new client.
 func NewClient(options ...ClientOption) *Client {
 	client := &Client{
-		endpoint:     Endpoint,
-		tokenValid:   true,
-		httpClient:   &http.Client{},
-		backoffFunc:  ExponentialBackoff(2, 500*time.Millisecond),
-		pollInterval: 500 * time.Millisecond,
+		endpoint:        Endpoint,
+		tokenValid:      true,
+		httpClient:      &http.Client{},
+		backoffFunc:     ExponentialBackoff(2, 500*time.Millisecond),
+		pollBackoffFunc: ConstantBackoff(500 * time.Millisecond),
 	}
 
 	for _, option := range options {
diff --git a/hcloud/client_test.go b/hcloud/client_test.go
index e6d804b..e4df032 100644
--- a/hcloud/client_test.go
+++ b/hcloud/client_test.go
@@ -35,6 +35,7 @@ func newTestEnv() testEnv {
 		WithEndpoint(server.URL),
 		WithToken("token"),
 		WithBackoffFunc(func(_ int) time.Duration { return 0 }),
+		WithPollBackoffFunc(func(r int) time.Duration { return 0 }),
 	)
 	return testEnv{
 		Server: server,
diff --git a/hcloud/error.go b/hcloud/error.go
index c15581b..ff04d07 100644
--- a/hcloud/error.go
+++ b/hcloud/error.go
@@ -28,14 +28,14 @@ const (
 	ErrorCodeResourceLocked        ErrorCode = "resource_locked"         // The resource is locked. The caller should contact support
 	ErrorUnsupportedError          ErrorCode = "unsupported_error"       // The given resource does not support this
 
-	// Server related error codes
+	// Server related error codes.
 	ErrorCodeInvalidServerType     ErrorCode = "invalid_server_type"     // The server type does not fit for the given server or is deprecated
 	ErrorCodeServerNotStopped      ErrorCode = "server_not_stopped"      // The action requires a stopped server
 	ErrorCodeNetworksOverlap       ErrorCode = "networks_overlap"        // The network IP range overlaps with one of the server networks
 	ErrorCodePlacementError        ErrorCode = "placement_error"         // An error during the placement occurred
 	ErrorCodeServerAlreadyAttached ErrorCode = "server_already_attached" // The server is already attached to the resource
 
-	// Load Balancer related error codes
+	// Load Balancer related error codes.
 	ErrorCodeIPNotOwned                       ErrorCode = "ip_not_owned"                          // The IP you are trying to add as a target is not owned by the Project owner
 	ErrorCodeSourcePortAlreadyUsed            ErrorCode = "source_port_already_used"              // The source port you are trying to add is already in use
 	ErrorCodeCloudResourceIPNotAllowed        ErrorCode = "cloud_resource_ip_not_allowed"         // The IP you are trying to add as a target belongs to a Hetzner Cloud resource
@@ -46,16 +46,16 @@ const (
 	ErrorCodeTargetsWithoutUsePrivateIP       ErrorCode = "targets_without_use_private_ip"        // The Load Balancer has targets that use the public IP instead of the private IP
 	ErrorCodeLoadBalancerNotAttachedToNetwork ErrorCode = "load_balancer_not_attached_to_network" // The Load Balancer is not attached to a network
 
-	// Network related error codes
+	// Network related error codes.
 	ErrorCodeIPNotAvailable     ErrorCode = "ip_not_available"        // The provided Network IP is not available
 	ErrorCodeNoSubnetAvailable  ErrorCode = "no_subnet_available"     // No Subnet or IP is available for the Load Balancer/Server within the network
 	ErrorCodeVSwitchAlreadyUsed ErrorCode = "vswitch_id_already_used" // The given Robot vSwitch ID is already registered in another network
 
-	// Volume related error codes
+	// Volume related error codes.
 	ErrorCodeNoSpaceLeftInLocation ErrorCode = "no_space_left_in_location" // There is no volume space left in the given location
 	ErrorCodeVolumeAlreadyAttached ErrorCode = "volume_already_attached"   // Volume is already attached to a server, detach first
 
-	// Firewall related error codes
+	// Firewall related error codes.
 	ErrorCodeFirewallAlreadyApplied   ErrorCode = "firewall_already_applied"    // Firewall was already applied on resource
 	ErrorCodeFirewallAlreadyRemoved   ErrorCode = "firewall_already_removed"    // Firewall was already removed from the resource
 	ErrorCodeIncompatibleNetworkType  ErrorCode = "incompatible_network_type"   // The Network type is incompatible for the given resource
@@ -63,7 +63,7 @@ const (
 	ErrorCodeServerAlreadyAdded       ErrorCode = "server_already_added"        // Server added more than one time to resource
 	ErrorCodeFirewallResourceNotFound ErrorCode = "firewall_resource_not_found" // Resource a firewall should be attached to / detached from not found
 
-	// Certificate related error codes
+	// Certificate related error codes.
 	ErrorCodeCAARecordDoesNotAllowCA                        ErrorCode = "caa_record_does_not_allow_ca"                          // CAA record does not allow certificate authority
 	ErrorCodeCADNSValidationFailed                          ErrorCode = "ca_dns_validation_failed"                              // Certificate Authority: DNS validation failed
 	ErrorCodeCATooManyAuthorizationsFailedRecently          ErrorCode = "ca_too_many_authorizations_failed_recently"            // Certificate Authority: Too many authorizations failed recently
diff --git a/hcloud/firewall_test.go b/hcloud/firewall_test.go
index bdda89d..3353148 100644
--- a/hcloud/firewall_test.go
+++ b/hcloud/firewall_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+
 	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
@@ -281,7 +282,7 @@ func TestFirewallClientUpdate(t *testing.T) {
 			t.Fatal(err)
 		}
 		expectedReqBody := schema.FirewallUpdateRequest{
-			Name: String("test"),
+			Name: Ptr("test"),
 			Labels: func() *map[string]string {
 				labels := map[string]string{"key": "value"}
 				return &labels
diff --git a/hcloud/floating_ip.go b/hcloud/floating_ip.go
index b180409..e2643a6 100644
--- a/hcloud/floating_ip.go
+++ b/hcloud/floating_ip.go
@@ -32,7 +32,7 @@ type FloatingIP struct {
 }
 
 // DNSPtrForIP returns the reverse DNS pointer of the IP address.
-// Deprecated: Use GetDNSPtrForIP instead
+// Deprecated: Use GetDNSPtrForIP instead.
 func (f *FloatingIP) DNSPtrForIP(ip net.IP) string {
 	return f.DNSPtr[ip.String()]
 }
@@ -241,10 +241,10 @@ func (c *FloatingIPClient) Create(ctx context.Context, opts FloatingIPCreateOpts
 		Name:        opts.Name,
 	}
 	if opts.HomeLocation != nil {
-		reqBody.HomeLocation = String(opts.HomeLocation.Name)
+		reqBody.HomeLocation = Ptr(opts.HomeLocation.Name)
 	}
 	if opts.Server != nil {
-		reqBody.Server = Int(opts.Server.ID)
+		reqBody.Server = Ptr(opts.Server.ID)
 	}
 	if opts.Labels != nil {
 		reqBody.Labels = &opts.Labels
diff --git a/hcloud/floating_ip_test.go b/hcloud/floating_ip_test.go
index eb1709e..b3494a1 100644
--- a/hcloud/floating_ip_test.go
+++ b/hcloud/floating_ip_test.go
@@ -247,7 +247,7 @@ func TestFloatingIPClientCreate(t *testing.T) {
 
 	opts := FloatingIPCreateOpts{
 		Type:         FloatingIPTypeIPv4,
-		Description:  String("test"),
+		Description:  Ptr("test"),
 		HomeLocation: &Location{Name: "test"},
 		Server:       &Server{ID: 1},
 		Labels:       map[string]string{"key": "value"},
@@ -292,10 +292,10 @@ func TestFloatingIPClientCreateWithName(t *testing.T) {
 
 	opts := FloatingIPCreateOpts{
 		Type:         FloatingIPTypeIPv4,
-		Description:  String("test"),
+		Description:  Ptr("test"),
 		HomeLocation: &Location{Name: "test"},
 		Server:       &Server{ID: 1},
-		Name:         String("MyFloatingIP"),
+		Name:         Ptr("MyFloatingIP"),
 		Labels:       map[string]string{"key": "value"},
 	}
 
@@ -564,7 +564,7 @@ func TestFloatingIPClientChangeProtection(t *testing.T) {
 		})
 
 		opts := FloatingIPChangeProtectionOpts{
-			Delete: Bool(true),
+			Delete: Ptr(true),
 		}
 		action, _, err := env.Client.FloatingIP.ChangeProtection(ctx, floatingIP, opts)
 		if err != nil {
diff --git a/hcloud/hcloud.go b/hcloud/hcloud.go
index f850a35..c7bed50 100644
--- a/hcloud/hcloud.go
+++ b/hcloud/hcloud.go
@@ -2,4 +2,4 @@
 package hcloud
 
 // Version is the library's version following Semantic Versioning.
-const Version = "1.35.2"
+const Version = "1.39.0" // x-release-please-version
diff --git a/hcloud/helper.go b/hcloud/helper.go
index e8e2642..1965609 100644
--- a/hcloud/helper.go
+++ b/hcloud/helper.go
@@ -2,17 +2,27 @@ package hcloud
 
 import "time"
 
+// Ptr returns a pointer to p.
+func Ptr[T any](p T) *T {
+	return &p
+}
+
 // String returns a pointer to the passed string s.
-func String(s string) *string { return &s }
+//
+// Deprecated: Use [Ptr] instead.
+func String(s string) *string { return Ptr(s) }
 
 // Int returns a pointer to the passed integer i.
-func Int(i int) *int { return &i }
+//
+// Deprecated: Use [Ptr] instead.
+func Int(i int) *int { return Ptr(i) }
 
 // Bool returns a pointer to the passed bool b.
-func Bool(b bool) *bool { return &b }
+//
+// Deprecated: Use [Ptr] instead.
+func Bool(b bool) *bool { return Ptr(b) }
 
 // Duration returns a pointer to the passed time.Duration d.
-func Duration(d time.Duration) *time.Duration { return &d }
-
-func intSlice(is []int) *[]int          { return &is }
-func stringSlice(ss []string) *[]string { return &ss }
+//
+// Deprecated: Use [Ptr] instead.
+func Duration(d time.Duration) *time.Duration { return Ptr(d) }
diff --git a/hcloud/image.go b/hcloud/image.go
index 0867aac..6a71898 100644
--- a/hcloud/image.go
+++ b/hcloud/image.go
@@ -222,7 +222,7 @@ func (c *ImageClient) Update(ctx context.Context, image *Image, opts ImageUpdate
 		Description: opts.Description,
 	}
 	if opts.Type != "" {
-		reqBody.Type = String(string(opts.Type))
+		reqBody.Type = Ptr(string(opts.Type))
 	}
 	if opts.Labels != nil {
 		reqBody.Labels = &opts.Labels
diff --git a/hcloud/image_test.go b/hcloud/image_test.go
index 6a339c0..28fd0cd 100644
--- a/hcloud/image_test.go
+++ b/hcloud/image_test.go
@@ -338,7 +338,7 @@ func TestImageClientUpdate(t *testing.T) {
 		})
 
 		opts := ImageUpdateOpts{
-			Description: String("test"),
+			Description: Ptr("test"),
 			Type:        ImageTypeSnapshot,
 		}
 		updatedImage, _, err := env.Client.Image.Update(ctx, image, opts)
@@ -417,7 +417,7 @@ func TestImageClientChangeProtection(t *testing.T) {
 		})
 
 		opts := ImageChangeProtectionOpts{
-			Delete: Bool(true),
+			Delete: Ptr(true),
 		}
 		action, _, err := env.Client.Image.ChangeProtection(ctx, image, opts)
 		if err != nil {
diff --git a/hcloud/internal/instrumentation/metrics.go b/hcloud/internal/instrumentation/metrics.go
index d8e9a2e..69a7165 100644
--- a/hcloud/internal/instrumentation/metrics.go
+++ b/hcloud/internal/instrumentation/metrics.go
@@ -16,7 +16,7 @@ type Instrumenter struct {
 	instrumentationRegistry *prometheus.Registry
 }
 
-// New creates a new Instrumenter. The subsystemIdentifier will be used as part of the metric names (e.g. hcloud_<identifier>_requests_total)
+// New creates a new Instrumenter. The subsystemIdentifier will be used as part of the metric names (e.g. hcloud_<identifier>_requests_total).
 func New(subsystemIdentifier string, instrumentationRegistry *prometheus.Registry) *Instrumenter {
 	return &Instrumenter{subsystemIdentifier: subsystemIdentifier, instrumentationRegistry: instrumentationRegistry}
 }
@@ -58,8 +58,10 @@ func (i *Instrumenter) InstrumentedRoundTripper() http.RoundTripper {
 
 // instrumentRoundTripperEndpoint implements a hcloud specific round tripper to count requests per API endpoint
 // numeric IDs are removed from the URI Path.
+//
 // Sample:
-// /volumes/1234/actions/attach --> /volumes/actions/attach
+//
+//	/volumes/1234/actions/attach --> /volumes/actions/attach
 func (i *Instrumenter) instrumentRoundTripperEndpoint(counter *prometheus.CounterVec, next http.RoundTripper) promhttp.RoundTripperFunc {
 	return func(r *http.Request) (*http.Response, error) {
 		resp, err := next.RoundTrip(r)
@@ -67,6 +69,7 @@ func (i *Instrumenter) instrumentRoundTripperEndpoint(counter *prometheus.Counte
 			statusCode := strconv.Itoa(resp.StatusCode)
 			counter.WithLabelValues(statusCode, strings.ToLower(resp.Request.Method), preparePathForLabel(resp.Request.URL.Path)).Inc()
 		}
+
 		return resp, err
 	}
 }
diff --git a/hcloud/iso.go b/hcloud/iso.go
index 3ec4a3d..ed2825b 100644
--- a/hcloud/iso.go
+++ b/hcloud/iso.go
@@ -19,7 +19,7 @@ type ISO struct {
 	Deprecated  time.Time
 }
 
-// IsDeprecated returns true if the ISO is deprecated
+// IsDeprecated returns true if the ISO is deprecated.
 func (iso *ISO) IsDeprecated() bool {
 	return !iso.Deprecated.IsZero()
 }
diff --git a/hcloud/load_balancer.go b/hcloud/load_balancer.go
index 96aeaf4..81e2e03 100644
--- a/hcloud/load_balancer.go
+++ b/hcloud/load_balancer.go
@@ -491,7 +491,6 @@ type LoadBalancerCreateResult struct {
 func (c *LoadBalancerClient) Create(ctx context.Context, opts LoadBalancerCreateOpts) (LoadBalancerCreateResult, *Response, error) {
 	reqBody := loadBalancerCreateOptsToSchema(opts)
 	reqBodyData, err := json.Marshal(reqBody)
-
 	if err != nil {
 		return LoadBalancerCreateResult{}, nil, err
 	}
@@ -865,7 +864,7 @@ func (c *LoadBalancerClient) AttachToNetwork(ctx context.Context, loadBalancer *
 		Network: opts.Network.ID,
 	}
 	if opts.IP != nil {
-		reqBody.IP = String(opts.IP.String())
+		reqBody.IP = Ptr(opts.IP.String())
 	}
 	reqBodyData, err := json.Marshal(reqBody)
 	if err != nil {
diff --git a/hcloud/load_balancer_test.go b/hcloud/load_balancer_test.go
index e414831..7d78341 100644
--- a/hcloud/load_balancer_test.go
+++ b/hcloud/load_balancer_test.go
@@ -179,7 +179,7 @@ func TestLoadBalancerCreate(t *testing.T) {
 			Algorithm: &schema.LoadBalancerCreateRequestAlgorithm{
 				Type: "round_robin",
 			},
-			Location: String("fsn1"),
+			Location: Ptr("fsn1"),
 		}
 		if !cmp.Equal(expectedReqBody, reqBody) {
 			t.Log(cmp.Diff(expectedReqBody, reqBody))
@@ -246,7 +246,7 @@ func TestLoadBalancerClientUpdate(t *testing.T) {
 			t.Fatal(err)
 		}
 		expectedReqBody := schema.LoadBalancerUpdateRequest{
-			Name: String("test"),
+			Name: Ptr("test"),
 		}
 		if !cmp.Equal(expectedReqBody, reqBody) {
 			t.Log(cmp.Diff(expectedReqBody, reqBody))
@@ -289,7 +289,7 @@ func TestLoadBalancerClientChangeProtection(t *testing.T) {
 			t.Fatal(err)
 		}
 		expectedReqBody := schema.LoadBalancerActionChangeProtectionRequest{
-			Delete: Bool(true),
+			Delete: Ptr(true),
 		}
 		if !cmp.Equal(expectedReqBody, reqBody) {
 			t.Log(cmp.Diff(expectedReqBody, reqBody))
@@ -308,7 +308,7 @@ func TestLoadBalancerClientChangeProtection(t *testing.T) {
 	)
 
 	opts := LoadBalancerChangeProtectionOpts{
-		Delete: Bool(true),
+		Delete: Ptr(true),
 	}
 	action, _, err := env.Client.LoadBalancer.ChangeProtection(ctx, loadBalancer, opts)
 	if err != nil {
@@ -336,7 +336,7 @@ func TestLoadBalancerClientAddServerTarget(t *testing.T) {
 			Server: &schema.LoadBalancerActionAddTargetRequestServer{
 				ID: 1,
 			},
-			UsePrivateIP: Bool(true),
+			UsePrivateIP: Ptr(true),
 		}
 		if !cmp.Equal(expectedReqBody, reqBody) {
 			t.Log(cmp.Diff(expectedReqBody, reqBody))
@@ -357,7 +357,7 @@ func TestLoadBalancerClientAddServerTarget(t *testing.T) {
 
 	opts := LoadBalancerAddServerTargetOpts{
 		Server:       server,
-		UsePrivateIP: Bool(true),
+		UsePrivateIP: Ptr(true),
 	}
 	action, _, err := env.Client.LoadBalancer.AddServerTarget(ctx, loadBalancer, opts)
 	if err != nil {
@@ -426,23 +426,23 @@ func TestLoadBalancerAddService(t *testing.T) {
 		}
 		expectedReqBody := schema.LoadBalancerActionAddServiceRequest{
 			Protocol:        string(LoadBalancerServiceProtocolHTTP),
-			ListenPort:      Int(4711),
-			DestinationPort: Int(80),
+			ListenPort:      Ptr(4711),
+			DestinationPort: Ptr(80),
 			HTTP: &schema.LoadBalancerActionAddServiceRequestHTTP{
-				CookieName:     String("HCLBSTICKY"),
-				CookieLifetime: Int(5 * 60),
-				RedirectHTTP:   Bool(false),
-				StickySessions: Bool(true),
+				CookieName:     Ptr("HCLBSTICKY"),
+				CookieLifetime: Ptr(5 * 60),
+				RedirectHTTP:   Ptr(false),
+				StickySessions: Ptr(true),
 			},
 			HealthCheck: &schema.LoadBalancerActionAddServiceRequestHealthCheck{
 				Protocol: "http",
-				Port:     Int(4711),
-				Interval: Int(15),
-				Timeout:  Int(10),
-				Retries:  Int(3),
+				Port:     Ptr(4711),
+				Interval: Ptr(15),
+				Timeout:  Ptr(10),
+				Retries:  Ptr(3),
 				HTTP: &schema.LoadBalancerActionAddServiceRequestHealthCheckHTTP{
-					Domain: String("example.com"),
-					Path:   String("/"),
+					Domain: Ptr("example.com"),
+					Path:   Ptr("/"),
 				},
 			},
 		}
@@ -464,23 +464,23 @@ func TestLoadBalancerAddService(t *testing.T) {
 
 	opts := LoadBalancerAddServiceOpts{
 		Protocol:        LoadBalancerServiceProtocolHTTP,
-		ListenPort:      Int(4711),
-		DestinationPort: Int(80),
+		ListenPort:      Ptr(4711),
+		DestinationPort: Ptr(80),
 		HTTP: &LoadBalancerAddServiceOptsHTTP{
-			CookieName:     String("HCLBSTICKY"),
-			CookieLifetime: Duration(5 * time.Minute),
-			RedirectHTTP:   Bool(false),
-			StickySessions: Bool(true),
+			CookieName:     Ptr("HCLBSTICKY"),
+			CookieLifetime: Ptr(5 * time.Minute),
+			RedirectHTTP:   Ptr(false),
+			StickySessions: Ptr(true),
 		},
 		HealthCheck: &LoadBalancerAddServiceOptsHealthCheck{
 			Protocol: "http",
-			Port:     Int(4711),
-			Interval: Duration(15 * time.Second),
-			Timeout:  Duration(10 * time.Second),
-			Retries:  Int(3),
+			Port:     Ptr(4711),
+			Interval: Ptr(15 * time.Second),
+			Timeout:  Ptr(10 * time.Second),
+			Retries:  Ptr(3),
 			HTTP: &LoadBalancerAddServiceOptsHealthCheckHTTP{
-				Domain: String("example.com"),
-				Path:   String("/"),
+				Domain: Ptr("example.com"),
+				Path:   Ptr("/"),
 			},
 		},
 	}
@@ -506,24 +506,24 @@ func TestLoadBalancerUpdateService(t *testing.T) {
 			t.Fatal(err)
 		}
 		expectedReqBody := schema.LoadBalancerActionUpdateServiceRequest{
-			Protocol:        String(string(LoadBalancerServiceProtocolHTTP)),
+			Protocol:        Ptr(string(LoadBalancerServiceProtocolHTTP)),
 			ListenPort:      4711,
-			DestinationPort: Int(80),
+			DestinationPort: Ptr(80),
 			HTTP: &schema.LoadBalancerActionUpdateServiceRequestHTTP{
-				CookieName:     String("HCLBSTICKY"),
-				CookieLifetime: Int(5 * 60),
-				RedirectHTTP:   Bool(false),
-				StickySessions: Bool(true),
+				CookieName:     Ptr("HCLBSTICKY"),
+				CookieLifetime: Ptr(5 * 60),
+				RedirectHTTP:   Ptr(false),
+				StickySessions: Ptr(true),
 			},
 			HealthCheck: &schema.LoadBalancerActionUpdateServiceRequestHealthCheck{
-				Protocol: String(string(LoadBalancerServiceProtocolHTTP)),
-				Port:     Int(4711),
-				Interval: Int(15),
-				Timeout:  Int(10),
-				Retries:  Int(3),
+				Protocol: Ptr(string(LoadBalancerServiceProtocolHTTP)),
+				Port:     Ptr(4711),
+				Interval: Ptr(15),
+				Timeout:  Ptr(10),
+				Retries:  Ptr(3),
 				HTTP: &schema.LoadBalancerActionUpdateServiceRequestHealthCheckHTTP{
-					Domain: String("example.com"),
-					Path:   String("/"),
+					Domain: Ptr("example.com"),
+					Path:   Ptr("/"),
 				},
 			},
 		}
@@ -545,22 +545,22 @@ func TestLoadBalancerUpdateService(t *testing.T) {
 
 	opts := LoadBalancerUpdateServiceOpts{
 		Protocol:        LoadBalancerServiceProtocolHTTP,
-		DestinationPort: Int(80),
+		DestinationPort: Ptr(80),
 		HTTP: &LoadBalancerUpdateServiceOptsHTTP{
-			CookieName:     String("HCLBSTICKY"),
-			CookieLifetime: Duration(5 * time.Minute),
-			RedirectHTTP:   Bool(false),
-			StickySessions: Bool(true),
+			CookieName:     Ptr("HCLBSTICKY"),
+			CookieLifetime: Ptr(5 * time.Minute),
+			RedirectHTTP:   Ptr(false),
+			StickySessions: Ptr(true),
 		},
 		HealthCheck: &LoadBalancerUpdateServiceOptsHealthCheck{
 			Protocol: LoadBalancerServiceProtocolHTTP,
-			Port:     Int(4711),
-			Interval: Duration(15 * time.Second),
-			Timeout:  Duration(10 * time.Second),
-			Retries:  Int(3),
+			Port:     Ptr(4711),
+			Interval: Ptr(15 * time.Second),
+			Timeout:  Ptr(10 * time.Second),
+			Retries:  Ptr(3),
 			HTTP: &LoadBalancerUpdateServiceOptsHealthCheckHTTP{
-				Domain: String("example.com"),
-				Path:   String("/"),
+				Domain: Ptr("example.com"),
+				Path:   Ptr("/"),
 			},
 		},
 	}
@@ -670,7 +670,7 @@ func TestLoadBalancerClientAttachToNetwork(t *testing.T) {
 		}
 		expectedReqBody := schema.LoadBalancerActionAttachToNetworkRequest{
 			Network: 1,
-			IP:      String("10.0.1.1"),
+			IP:      Ptr("10.0.1.1"),
 		}
 		if !cmp.Equal(expectedReqBody, reqBody) {
 			t.Log(cmp.Diff(expectedReqBody, reqBody))
@@ -898,7 +898,7 @@ func TestLoadBalancerClientAddLabelSelectorTarget(t *testing.T) {
 	ctx := context.Background()
 	action, _, err := env.Client.LoadBalancer.AddLabelSelectorTarget(ctx, &LoadBalancer{ID: 1}, LoadBalancerAddLabelSelectorTargetOpts{
 		Selector:     "key=value",
-		UsePrivateIP: Bool(false),
+		UsePrivateIP: Ptr(false),
 	})
 	if err != nil {
 		t.Fatal(err)
diff --git a/hcloud/metadata/client.go b/hcloud/metadata/client.go
index 7bfa18e..4c173d7 100644
--- a/hcloud/metadata/client.go
+++ b/hcloud/metadata/client.go
@@ -8,8 +8,9 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/hetznercloud/hcloud-go/hcloud/internal/instrumentation"
 	"github.com/prometheus/client_golang/prometheus"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/internal/instrumentation"
 )
 
 const Endpoint = "http://169.254.169.254/hetzner/v1/metadata"
@@ -85,7 +86,7 @@ func (c *Client) get(path string) (string, error) {
 }
 
 // IsHcloudServer checks if the currently called server is a hcloud server by calling a metadata endpoint
-// if the endpoint answers with a non-empty value this method returns true, otherwise false
+// if the endpoint answers with a non-empty value this method returns true, otherwise false.
 func (c *Client) IsHcloudServer() bool {
 	hostname, err := c.Hostname()
 	if err != nil {
@@ -97,12 +98,12 @@ func (c *Client) IsHcloudServer() bool {
 	return false
 }
 
-// Hostname returns the hostname of the server that did the request to the Metadata server
+// Hostname returns the hostname of the server that did the request to the Metadata server.
 func (c *Client) Hostname() (string, error) {
 	return c.get("/hostname")
 }
 
-// InstanceID returns the ID of the server that did the request to the Metadata server
+// InstanceID returns the ID of the server that did the request to the Metadata server.
 func (c *Client) InstanceID() (int, error) {
 	resp, err := c.get("/instance-id")
 	if err != nil {
@@ -111,7 +112,7 @@ func (c *Client) InstanceID() (int, error) {
 	return strconv.Atoi(resp)
 }
 
-// PublicIPv4 returns the Public IPv4 of the server that did the request to the Metadata server
+// PublicIPv4 returns the Public IPv4 of the server that did the request to the Metadata server.
 func (c *Client) PublicIPv4() (net.IP, error) {
 	resp, err := c.get("/public-ipv4")
 	if err != nil {
@@ -120,18 +121,18 @@ func (c *Client) PublicIPv4() (net.IP, error) {
 	return net.ParseIP(resp), nil
 }
 
-// Region returns the Network Zone of the server that did the request to the Metadata server
+// Region returns the Network Zone of the server that did the request to the Metadata server.
 func (c *Client) Region() (string, error) {
 	return c.get("/region")
 }
 
-// AvailabilityZone returns the datacenter of the server that did the request to the Metadata server
+// AvailabilityZone returns the datacenter of the server that did the request to the Metadata server.
 func (c *Client) AvailabilityZone() (string, error) {
 	return c.get("/availability-zone")
 }
 
-// PrivateNetworks returns details about the private networks the server is attached to
-// Returns YAML (unparsed)
+// PrivateNetworks returns details about the private networks the server is attached to.
+// Returns YAML (unparsed).
 func (c *Client) PrivateNetworks() (string, error) {
 	return c.get("/private-networks")
 }
diff --git a/hcloud/network.go b/hcloud/network.go
index e3bf6de..5a19681 100644
--- a/hcloud/network.go
+++ b/hcloud/network.go
@@ -21,6 +21,7 @@ type NetworkZone string
 const (
 	NetworkZoneEUCentral NetworkZone = "eu-central"
 	NetworkZoneUSEast    NetworkZone = "us-east"
+	NetworkZoneUSWest    NetworkZone = "us-west"
 )
 
 // NetworkSubnetType specifies a type of a subnet.
diff --git a/hcloud/network_test.go b/hcloud/network_test.go
index 5b0855b..d5f5d00 100644
--- a/hcloud/network_test.go
+++ b/hcloud/network_test.go
@@ -622,7 +622,7 @@ func TestNetworkClientChangeProtection(t *testing.T) {
 		})
 
 		opts := NetworkChangeProtectionOpts{
-			Delete: Bool(true),
+			Delete: Ptr(true),
 		}
 		action, _, err := env.Client.Network.ChangeProtection(ctx, network, opts)
 		if err != nil {
diff --git a/hcloud/placement_group.go b/hcloud/placement_group.go
index 5056a11..07fedad 100644
--- a/hcloud/placement_group.go
+++ b/hcloud/placement_group.go
@@ -23,11 +23,11 @@ type PlacementGroup struct {
 	Type    PlacementGroupType
 }
 
-// PlacementGroupType specifies the type of a Placement Group
+// PlacementGroupType specifies the type of a Placement Group.
 type PlacementGroupType string
 
 const (
-	// PlacementGroupTypeSpread spreads all servers in the group on different vhosts
+	// PlacementGroupTypeSpread spreads all servers in the group on different vhosts.
 	PlacementGroupTypeSpread PlacementGroupType = "spread"
 )
 
@@ -158,7 +158,7 @@ type PlacementGroupCreateOpts struct {
 	Type   PlacementGroupType
 }
 
-// Validate checks if options are valid
+// Validate checks if options are valid.
 func (o PlacementGroupCreateOpts) Validate() error {
 	if o.Name == "" {
 		return errors.New("missing name")
@@ -172,7 +172,7 @@ type PlacementGroupCreateResult struct {
 	Action         *Action
 }
 
-// Create creates a new PlacementGroup
+// Create creates a new PlacementGroup.
 func (c *PlacementGroupClient) Create(ctx context.Context, opts PlacementGroupCreateOpts) (PlacementGroupCreateResult, *Response, error) {
 	if err := opts.Validate(); err != nil {
 		return PlacementGroupCreateResult{}, nil, err
diff --git a/hcloud/placement_group_test.go b/hcloud/placement_group_test.go
index 334f591..fd2caed 100644
--- a/hcloud/placement_group_test.go
+++ b/hcloud/placement_group_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+
 	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
diff --git a/hcloud/pricing.go b/hcloud/pricing.go
index 67be21f..836f391 100644
--- a/hcloud/pricing.go
+++ b/hcloud/pricing.go
@@ -56,12 +56,13 @@ type FloatingIPTypePricing struct {
 // PrimaryIPTypePricing defines the schema of pricing information for a primary IP
 // type at a datacenter.
 type PrimaryIPTypePricing struct {
-	Datacenter string
+	Datacenter string // Deprecated: the API does not return pricing for the individual DCs anymore
+	Location   string
 	Hourly     PrimaryIPPrice
 	Monthly    PrimaryIPPrice
 }
 
-// PrimaryIPTypePricing provides pricing information for PrimaryIPs
+// PrimaryIPTypePricing provides pricing information for PrimaryIPs.
 type PrimaryIPPricing struct {
 	Type     string
 	Pricings []PrimaryIPTypePricing
diff --git a/hcloud/primary_ip.go b/hcloud/primary_ip.go
index c534c89..e328b9b 100644
--- a/hcloud/primary_ip.go
+++ b/hcloud/primary_ip.go
@@ -13,7 +13,7 @@ import (
 	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
-// PrimaryIP defines a Primary IP
+// PrimaryIP defines a Primary IP.
 type PrimaryIP struct {
 	ID           int
 	IP           net.IP
@@ -90,12 +90,6 @@ type PrimaryIPUpdateOpts struct {
 	Name       string             `json:"name,omitempty"`
 }
 
-// PrimaryIPUpdateResult defines the response
-// when updating a Primary IP.
-type PrimaryIPUpdateResult struct {
-	PrimaryIP PrimaryIP `json:"primary_ip"`
-}
-
 // PrimaryIPAssignOpts defines the request to
 // assign a Primary IP to an assignee (usually a server).
 type PrimaryIPAssignOpts struct {
@@ -111,7 +105,7 @@ type PrimaryIPAssignResult struct {
 }
 
 // PrimaryIPChangeDNSPtrOpts defines the request to
-// change a DNS PTR entry from a Primary IP
+// change a DNS PTR entry from a Primary IP.
 type PrimaryIPChangeDNSPtrOpts struct {
 	ID     int
 	DNSPtr string `json:"dns_ptr"`
@@ -125,19 +119,19 @@ type PrimaryIPChangeDNSPtrResult struct {
 }
 
 // PrimaryIPChangeProtectionOpts defines the request to
-// change protection configuration of a Primary IP
+// change protection configuration of a Primary IP.
 type PrimaryIPChangeProtectionOpts struct {
 	ID     int
 	Delete bool `json:"delete"`
 }
 
 // PrimaryIPChangeProtectionResult defines the response
-// when changing a protection of a PrimaryIP
+// when changing a protection of a PrimaryIP.
 type PrimaryIPChangeProtectionResult struct {
 	Action schema.Action `json:"action"`
 }
 
-// PrimaryIPClient is a client for the Primary IP API
+// PrimaryIPClient is a client for the Primary IP API.
 type PrimaryIPClient struct {
 	client *Client
 }
@@ -240,10 +234,12 @@ func (c *PrimaryIPClient) List(ctx context.Context, opts PrimaryIPListOpts) ([]*
 
 // All returns all Primary IPs.
 func (c *PrimaryIPClient) All(ctx context.Context) ([]*PrimaryIP, error) {
-	allPrimaryIPs := []*PrimaryIP{}
+	return c.AllWithOpts(ctx, PrimaryIPListOpts{ListOpts: ListOpts{PerPage: 50}})
+}
 
-	opts := PrimaryIPListOpts{}
-	opts.PerPage = 50
+// AllWithOpts returns all Primary IPs for the given options.
+func (c *PrimaryIPClient) AllWithOpts(ctx context.Context, opts PrimaryIPListOpts) ([]*PrimaryIP, error) {
+	var allPrimaryIPs []*PrimaryIP
 
 	err := c.client.all(func(page int) (*Response, error) {
 		opts.Page = page
@@ -311,15 +307,15 @@ func (c *PrimaryIPClient) Update(ctx context.Context, primaryIP *PrimaryIP, reqB
 		return nil, nil, err
 	}
 
-	respBody := PrimaryIPUpdateResult{}
+	var respBody schema.PrimaryIPUpdateResult
 	resp, err := c.client.Do(req, &respBody)
 	if err != nil {
 		return nil, resp, err
 	}
-	return &respBody.PrimaryIP, resp, nil
+	return PrimaryIPFromSchema(respBody.PrimaryIP), resp, nil
 }
 
-// Assign a Primary IP to a resource
+// Assign a Primary IP to a resource.
 func (c *PrimaryIPClient) Assign(ctx context.Context, opts PrimaryIPAssignOpts) (*Action, *Response, error) {
 	reqBodyData, err := json.Marshal(opts)
 	if err != nil {
@@ -340,7 +336,7 @@ func (c *PrimaryIPClient) Assign(ctx context.Context, opts PrimaryIPAssignOpts)
 	return ActionFromSchema(respBody.Action), resp, nil
 }
 
-// Unassign a Primary IP from a resource
+// Unassign a Primary IP from a resource.
 func (c *PrimaryIPClient) Unassign(ctx context.Context, id int) (*Action, *Response, error) {
 	path := fmt.Sprintf("/primary_ips/%d/actions/unassign", id)
 	req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader([]byte{}))
@@ -356,7 +352,7 @@ func (c *PrimaryIPClient) Unassign(ctx context.Context, id int) (*Action, *Respo
 	return ActionFromSchema(respBody.Action), resp, nil
 }
 
-// ChangeDNSPtr Change the reverse DNS from a Primary IP
+// ChangeDNSPtr Change the reverse DNS from a Primary IP.
 func (c *PrimaryIPClient) ChangeDNSPtr(ctx context.Context, opts PrimaryIPChangeDNSPtrOpts) (*Action, *Response, error) {
 	reqBodyData, err := json.Marshal(opts)
 	if err != nil {
diff --git a/hcloud/primary_ip_test.go b/hcloud/primary_ip_test.go
index 6c517ee..d368b71 100644
--- a/hcloud/primary_ip_test.go
+++ b/hcloud/primary_ip_test.go
@@ -3,12 +3,14 @@ package hcloud
 import (
 	"context"
 	"encoding/json"
+	"net"
 	"net/http"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 	"github.com/stretchr/testify/assert"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
 func TestPrimaryIPClient(t *testing.T) {
@@ -333,8 +335,8 @@ func TestPrimaryIPClient(t *testing.T) {
 				t.Log(cmp.Diff(expectedReqBody, reqBody))
 				t.Error("unexpected request body")
 			}
-			json.NewEncoder(w).Encode(PrimaryIPUpdateResult{
-				PrimaryIP: PrimaryIP{ID: 1},
+			json.NewEncoder(w).Encode(schema.PrimaryIPUpdateResult{
+				PrimaryIP: schema.PrimaryIP{ID: 1, IP: "2001:db8::/64"},
 			})
 		})
 
@@ -347,11 +349,14 @@ func TestPrimaryIPClient(t *testing.T) {
 			Labels:     &labels,
 		}
 
-		primaryIP := PrimaryIP{ID: 1}
+		primaryIP := PrimaryIP{ID: 1, IP: net.ParseIP("2001:db8::")}
 		result, resp, err := env.Client.PrimaryIP.Update(ctx, &primaryIP, opts)
 		assert.NoError(t, err)
 		assert.NotNil(t, resp, "no response returned")
-		assert.Equal(t, *result, primaryIP, "no primary IP returned")
+		if result.ID != 1 {
+			t.Errorf("unexpected Primary IP ID: %v", result.ID)
+		}
+		assert.Equal(t, primaryIP.IP, result.IP, "parsed the wrong IP")
 	})
 	t.Run("Assign", func(t *testing.T) {
 		env := newTestEnv()
diff --git a/hcloud/rdns.go b/hcloud/rdns.go
index 70d31f9..891ea2b 100644
--- a/hcloud/rdns.go
+++ b/hcloud/rdns.go
@@ -7,7 +7,7 @@ import (
 )
 
 // RDNSSupporter defines functions to change and lookup reverse dns entries.
-// currently implemented by Server, FloatingIP and LoadBalancer
+// currently implemented by Server, FloatingIP and LoadBalancer.
 type RDNSSupporter interface {
 	// changeDNSPtr changes or resets the reverse DNS pointer for a IP address.
 	// Pass a nil ptr to reset the reverse DNS pointer to its default value.
diff --git a/hcloud/schema.go b/hcloud/schema.go
index 633a568..091426b 100644
--- a/hcloud/schema.go
+++ b/hcloud/schema.go
@@ -721,7 +721,7 @@ func PricingFromSchema(s schema.Pricing) Pricing {
 		var pricings []PrimaryIPTypePricing
 		for _, price := range primaryIPType.Prices {
 			p := PrimaryIPTypePricing{
-				Datacenter: price.Datacenter,
+				Location: price.Location,
 				Monthly: PrimaryIPPrice{
 					Net:   price.PriceMonthly.Net,
 					Gross: price.PriceMonthly.Gross,
@@ -881,19 +881,19 @@ func loadBalancerCreateOptsToSchema(opts LoadBalancerCreateOpts) schema.LoadBala
 	}
 	if opts.Location != nil {
 		if opts.Location.ID != 0 {
-			req.Location = String(strconv.Itoa(opts.Location.ID))
+			req.Location = Ptr(strconv.Itoa(opts.Location.ID))
 		} else {
-			req.Location = String(opts.Location.Name)
+			req.Location = Ptr(opts.Location.Name)
 		}
 	}
 	if opts.NetworkZone != "" {
-		req.NetworkZone = String(string(opts.NetworkZone))
+		req.NetworkZone = Ptr(string(opts.NetworkZone))
 	}
 	if opts.Labels != nil {
 		req.Labels = &opts.Labels
 	}
 	if opts.Network != nil {
-		req.Network = Int(opts.Network.ID)
+		req.Network = Ptr(opts.Network.ID)
 	}
 	for _, target := range opts.Targets {
 		schemaTarget := schema.LoadBalancerCreateRequestTarget{
@@ -927,7 +927,7 @@ func loadBalancerCreateOptsToSchema(opts LoadBalancerCreateOpts) schema.LoadBala
 			}
 			if service.HTTP.CookieLifetime != nil {
 				if sec := service.HTTP.CookieLifetime.Seconds(); sec != 0 {
-					schemaService.HTTP.CookieLifetime = Int(int(sec))
+					schemaService.HTTP.CookieLifetime = Ptr(int(sec))
 				}
 			}
 			if service.HTTP.Certificates != nil {
@@ -945,10 +945,10 @@ func loadBalancerCreateOptsToSchema(opts LoadBalancerCreateOpts) schema.LoadBala
 				Retries:  service.HealthCheck.Retries,
 			}
 			if service.HealthCheck.Interval != nil {
-				schemaHealthCheck.Interval = Int(int(service.HealthCheck.Interval.Seconds()))
+				schemaHealthCheck.Interval = Ptr(int(service.HealthCheck.Interval.Seconds()))
 			}
 			if service.HealthCheck.Timeout != nil {
-				schemaHealthCheck.Timeout = Int(int(service.HealthCheck.Timeout.Seconds()))
+				schemaHealthCheck.Timeout = Ptr(int(service.HealthCheck.Timeout.Seconds()))
 			}
 			if service.HealthCheck.HTTP != nil {
 				schemaHealthCheckHTTP := &schema.LoadBalancerCreateRequestServiceHealthCheckHTTP{
@@ -983,7 +983,7 @@ func loadBalancerAddServiceOptsToSchema(opts LoadBalancerAddServiceOpts) schema.
 			StickySessions: opts.HTTP.StickySessions,
 		}
 		if opts.HTTP.CookieLifetime != nil {
-			req.HTTP.CookieLifetime = Int(int(opts.HTTP.CookieLifetime.Seconds()))
+			req.HTTP.CookieLifetime = Ptr(int(opts.HTTP.CookieLifetime.Seconds()))
 		}
 		if opts.HTTP.Certificates != nil {
 			certificates := []int{}
@@ -1000,10 +1000,10 @@ func loadBalancerAddServiceOptsToSchema(opts LoadBalancerAddServiceOpts) schema.
 			Retries:  opts.HealthCheck.Retries,
 		}
 		if opts.HealthCheck.Interval != nil {
-			req.HealthCheck.Interval = Int(int(opts.HealthCheck.Interval.Seconds()))
+			req.HealthCheck.Interval = Ptr(int(opts.HealthCheck.Interval.Seconds()))
 		}
 		if opts.HealthCheck.Timeout != nil {
-			req.HealthCheck.Timeout = Int(int(opts.HealthCheck.Timeout.Seconds()))
+			req.HealthCheck.Timeout = Ptr(int(opts.HealthCheck.Timeout.Seconds()))
 		}
 		if opts.HealthCheck.HTTP != nil {
 			req.HealthCheck.HTTP = &schema.LoadBalancerActionAddServiceRequestHealthCheckHTTP{
@@ -1026,7 +1026,7 @@ func loadBalancerUpdateServiceOptsToSchema(opts LoadBalancerUpdateServiceOpts) s
 		Proxyprotocol:   opts.Proxyprotocol,
 	}
 	if opts.Protocol != "" {
-		req.Protocol = String(string(opts.Protocol))
+		req.Protocol = Ptr(string(opts.Protocol))
 	}
 	if opts.HTTP != nil {
 		req.HTTP = &schema.LoadBalancerActionUpdateServiceRequestHTTP{
@@ -1035,7 +1035,7 @@ func loadBalancerUpdateServiceOptsToSchema(opts LoadBalancerUpdateServiceOpts) s
 			StickySessions: opts.HTTP.StickySessions,
 		}
 		if opts.HTTP.CookieLifetime != nil {
-			req.HTTP.CookieLifetime = Int(int(opts.HTTP.CookieLifetime.Seconds()))
+			req.HTTP.CookieLifetime = Ptr(int(opts.HTTP.CookieLifetime.Seconds()))
 		}
 		if opts.HTTP.Certificates != nil {
 			certificates := []int{}
@@ -1051,13 +1051,13 @@ func loadBalancerUpdateServiceOptsToSchema(opts LoadBalancerUpdateServiceOpts) s
 			Retries: opts.HealthCheck.Retries,
 		}
 		if opts.HealthCheck.Interval != nil {
-			req.HealthCheck.Interval = Int(int(opts.HealthCheck.Interval.Seconds()))
+			req.HealthCheck.Interval = Ptr(int(opts.HealthCheck.Interval.Seconds()))
 		}
 		if opts.HealthCheck.Timeout != nil {
-			req.HealthCheck.Timeout = Int(int(opts.HealthCheck.Timeout.Seconds()))
+			req.HealthCheck.Timeout = Ptr(int(opts.HealthCheck.Timeout.Seconds()))
 		}
 		if opts.HealthCheck.Protocol != "" {
-			req.HealthCheck.Protocol = String(string(opts.HealthCheck.Protocol))
+			req.HealthCheck.Protocol = Ptr(string(opts.HealthCheck.Protocol))
 		}
 		if opts.HealthCheck.HTTP != nil {
 			req.HealthCheck.HTTP = &schema.LoadBalancerActionUpdateServiceRequestHealthCheckHTTP{
diff --git a/hcloud/schema/pricing.go b/hcloud/schema/pricing.go
index b97a2f8..0c06c73 100644
--- a/hcloud/schema/pricing.go
+++ b/hcloud/schema/pricing.go
@@ -94,15 +94,16 @@ type PricingGetResponse struct {
 	Pricing Pricing `json:"pricing"`
 }
 
-// PricingPrimaryIPTypePrice defines the schema of pricing information for a primary IP
+// PricingPrimaryIPTypePrice defines the schema of pricing information for a primary IP.
 // type at a datacenter.
 type PricingPrimaryIPTypePrice struct {
-	Datacenter   string `json:"datacenter"`
+	Datacenter   string `json:"datacenter"` // Deprecated: the API does not return pricing for the individual DCs anymore
+	Location     string `json:"location"`
 	PriceHourly  Price  `json:"price_hourly"`
 	PriceMonthly Price  `json:"price_monthly"`
 }
 
-// PricingPrimaryIP define the schema of pricing information for a primary IP at a datacenter
+// PricingPrimaryIP define the schema of pricing information for a primary IP at a datacenter.
 type PricingPrimaryIP struct {
 	Type   string                      `json:"type"`
 	Prices []PricingPrimaryIPTypePrice `json:"prices"`
diff --git a/hcloud/schema/primary_ip.go b/hcloud/schema/primary_ip.go
index b21e28b..d232a73 100644
--- a/hcloud/schema/primary_ip.go
+++ b/hcloud/schema/primary_ip.go
@@ -2,7 +2,7 @@ package schema
 
 import "time"
 
-// PrimaryIP defines a Primary IP
+// PrimaryIP defines a Primary IP.
 type PrimaryIP struct {
 	ID           int                 `json:"id"`
 	IP           string              `json:"ip"`
@@ -47,3 +47,9 @@ type PrimaryIPGetResult struct {
 type PrimaryIPListResult struct {
 	PrimaryIPs []PrimaryIP `json:"primary_ips"`
 }
+
+// PrimaryIPUpdateResult defines the response
+// when updating a Primary IP.
+type PrimaryIPUpdateResult struct {
+	PrimaryIP PrimaryIP `json:"primary_ip"`
+}
diff --git a/hcloud/schema/server.go b/hcloud/schema/server.go
index 616b2eb..654ccfc 100644
--- a/hcloud/schema/server.go
+++ b/hcloud/schema/server.go
@@ -136,6 +136,12 @@ type ServerCreateResponse struct {
 	NextActions  []Action `json:"next_actions"`
 }
 
+// ServerDeleteResponse defines the schema of the response when
+// deleting a server.
+type ServerDeleteResponse struct {
+	Action Action `json:"action"`
+}
+
 // ServerUpdateRequest defines the schema of the request to update a server.
 type ServerUpdateRequest struct {
 	Name   string             `json:"name,omitempty"`
diff --git a/hcloud/schema_test.go b/hcloud/schema_test.go
index 9301d78..fd0a364 100644
--- a/hcloud/schema_test.go
+++ b/hcloud/schema_test.go
@@ -2044,7 +2044,7 @@ func TestPricingFromSchema(t *testing.T) {
 			{
 				"prices": [
 				{
-					"datacenter": "fsn1-dc8",
+					"location": "fsn1",
 					"price_hourly": {
 					"gross": "1.1900000000000000",
 					"net": "1.0000000000"
@@ -2185,8 +2185,8 @@ func TestPricingFromSchema(t *testing.T) {
 		if len(ip.Pricings) != 1 {
 			t.Errorf("unexpected number of prices: %d", len(ip.Pricings))
 		} else {
-			if ip.Pricings[0].Datacenter != "fsn1-dc8" {
-				t.Errorf("unexpected Datacenter: %v", ip.Pricings[0].Datacenter)
+			if ip.Pricings[0].Location != "fsn1" {
+				t.Errorf("unexpected Location: %v", ip.Pricings[0].Location)
 			}
 			if ip.Pricings[0].Monthly.Net != "1.0000000000" {
 				t.Errorf("unexpected Monthly.Net: %v", ip.Pricings[0].Monthly.Net)
@@ -2346,7 +2346,7 @@ func TestLoadBalancerCreateOptsToSchema(t *testing.T) {
 				Algorithm: &schema.LoadBalancerCreateRequestAlgorithm{
 					Type: string(LoadBalancerAlgorithmTypeRoundRobin),
 				},
-				NetworkZone: String(string(NetworkZoneEUCentral)),
+				NetworkZone: Ptr(string(NetworkZoneEUCentral)),
 			},
 		},
 		"all set": {
@@ -2356,32 +2356,32 @@ func TestLoadBalancerCreateOptsToSchema(t *testing.T) {
 				Algorithm:        &LoadBalancerAlgorithm{Type: LoadBalancerAlgorithmTypeRoundRobin},
 				NetworkZone:      NetworkZoneEUCentral,
 				Labels:           map[string]string{"foo": "bar"},
-				PublicInterface:  Bool(true),
+				PublicInterface:  Ptr(true),
 				Network:          &Network{ID: 3},
 				Services: []LoadBalancerCreateOptsService{
 					{
 						Protocol:        LoadBalancerServiceProtocolHTTP,
-						DestinationPort: Int(80),
-						Proxyprotocol:   Bool(true),
+						DestinationPort: Ptr(80),
+						Proxyprotocol:   Ptr(true),
 						HTTP: &LoadBalancerCreateOptsServiceHTTP{
-							CookieName:     String("keks"),
-							CookieLifetime: Duration(5 * time.Minute),
-							RedirectHTTP:   Bool(true),
-							StickySessions: Bool(true),
+							CookieName:     Ptr("keks"),
+							CookieLifetime: Ptr(5 * time.Minute),
+							RedirectHTTP:   Ptr(true),
+							StickySessions: Ptr(true),
 							Certificates:   []*Certificate{{ID: 1}, {ID: 2}},
 						},
 						HealthCheck: &LoadBalancerCreateOptsServiceHealthCheck{
 							Protocol: LoadBalancerServiceProtocolHTTP,
-							Port:     Int(80),
-							Interval: Duration(5 * time.Second),
-							Timeout:  Duration(1 * time.Second),
-							Retries:  Int(3),
+							Port:     Ptr(80),
+							Interval: Ptr(5 * time.Second),
+							Timeout:  Ptr(1 * time.Second),
+							Retries:  Ptr(3),
 							HTTP: &LoadBalancerCreateOptsServiceHealthCheckHTTP{
-								Domain:      String("example.com"),
-								Path:        String("/health"),
-								Response:    String("ok"),
+								Domain:      Ptr("example.com"),
+								Path:        Ptr("/health"),
+								Response:    Ptr("ok"),
 								StatusCodes: []string{"2??", "3??"},
-								TLS:         Bool(true),
+								TLS:         Ptr(true),
 							},
 						},
 					},
@@ -2405,37 +2405,37 @@ func TestLoadBalancerCreateOptsToSchema(t *testing.T) {
 				Algorithm: &schema.LoadBalancerCreateRequestAlgorithm{
 					Type: string(LoadBalancerAlgorithmTypeRoundRobin),
 				},
-				NetworkZone: String(string(NetworkZoneEUCentral)),
+				NetworkZone: Ptr(string(NetworkZoneEUCentral)),
 				Labels: func() *map[string]string {
 					labels := map[string]string{"foo": "bar"}
 					return &labels
 				}(),
-				PublicInterface: Bool(true),
-				Network:         Int(3),
+				PublicInterface: Ptr(true),
+				Network:         Ptr(3),
 				Services: []schema.LoadBalancerCreateRequestService{
 					{
 						Protocol:        string(LoadBalancerServiceProtocolHTTP),
-						DestinationPort: Int(80),
-						Proxyprotocol:   Bool(true),
+						DestinationPort: Ptr(80),
+						Proxyprotocol:   Ptr(true),
 						HTTP: &schema.LoadBalancerCreateRequestServiceHTTP{
-							CookieName:     String("keks"),
-							CookieLifetime: Int(5 * 60),
-							RedirectHTTP:   Bool(true),
-							StickySessions: Bool(true),
-							Certificates:   intSlice([]int{1, 2}),
+							CookieName:     Ptr("keks"),
+							CookieLifetime: Ptr(5 * 60),
+							RedirectHTTP:   Ptr(true),
+							StickySessions: Ptr(true),
+							Certificates:   Ptr([]int{1, 2}),
 						},
 						HealthCheck: &schema.LoadBalancerCreateRequestServiceHealthCheck{
 							Protocol: string(LoadBalancerServiceProtocolHTTP),
-							Port:     Int(80),
-							Interval: Int(5),
-							Timeout:  Int(1),
-							Retries:  Int(3),
+							Port:     Ptr(80),
+							Interval: Ptr(5),
+							Timeout:  Ptr(1),
+							Retries:  Ptr(3),
 							HTTP: &schema.LoadBalancerCreateRequestServiceHealthCheckHTTP{
-								Domain:      String("example.com"),
-								Path:        String("/health"),
-								Response:    String("ok"),
-								StatusCodes: stringSlice([]string{"2??", "3??"}),
-								TLS:         Bool(true),
+								Domain:      Ptr("example.com"),
+								Path:        Ptr("/health"),
+								Response:    Ptr("ok"),
+								StatusCodes: Ptr([]string{"2??", "3??"}),
+								TLS:         Ptr(true),
 							},
 						},
 					},
@@ -2484,53 +2484,53 @@ func TestLoadBalancerAddServiceOptsToSchema(t *testing.T) {
 		"all set": {
 			Opts: LoadBalancerAddServiceOpts{
 				Protocol:        LoadBalancerServiceProtocolHTTP,
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &LoadBalancerAddServiceOptsHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Duration(5 * time.Minute),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * time.Minute),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
 					Certificates:   []*Certificate{{ID: 1}, {ID: 2}},
 				},
 				HealthCheck: &LoadBalancerAddServiceOptsHealthCheck{
 					Protocol: LoadBalancerServiceProtocolHTTP,
-					Port:     Int(80),
-					Interval: Duration(5 * time.Second),
-					Timeout:  Duration(1 * time.Second),
-					Retries:  Int(3),
+					Port:     Ptr(80),
+					Interval: Ptr(5 * time.Second),
+					Timeout:  Ptr(1 * time.Second),
+					Retries:  Ptr(3),
 					HTTP: &LoadBalancerAddServiceOptsHealthCheckHTTP{
-						Domain:      String("example.com"),
-						Path:        String("/health"),
-						Response:    String("ok"),
+						Domain:      Ptr("example.com"),
+						Path:        Ptr("/health"),
+						Response:    Ptr("ok"),
 						StatusCodes: []string{"2??", "3??"},
-						TLS:         Bool(true),
+						TLS:         Ptr(true),
 					},
 				},
 			},
 			Request: schema.LoadBalancerActionAddServiceRequest{
 				Protocol:        string(LoadBalancerServiceProtocolHTTP),
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &schema.LoadBalancerActionAddServiceRequestHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Int(5 * 60),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
-					Certificates:   intSlice([]int{1, 2}),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * 60),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
+					Certificates:   Ptr([]int{1, 2}),
 				},
 				HealthCheck: &schema.LoadBalancerActionAddServiceRequestHealthCheck{
 					Protocol: string(LoadBalancerServiceProtocolHTTP),
-					Port:     Int(80),
-					Interval: Int(5),
-					Timeout:  Int(1),
-					Retries:  Int(3),
+					Port:     Ptr(80),
+					Interval: Ptr(5),
+					Timeout:  Ptr(1),
+					Retries:  Ptr(3),
 					HTTP: &schema.LoadBalancerActionAddServiceRequestHealthCheckHTTP{
-						Domain:      String("example.com"),
-						Path:        String("/health"),
-						Response:    String("ok"),
-						StatusCodes: stringSlice([]string{"2??", "3??"}),
-						TLS:         Bool(true),
+						Domain:      Ptr("example.com"),
+						Path:        Ptr("/health"),
+						Response:    Ptr("ok"),
+						StatusCodes: Ptr([]string{"2??", "3??"}),
+						TLS:         Ptr(true),
 					},
 				},
 			},
@@ -2538,26 +2538,26 @@ func TestLoadBalancerAddServiceOptsToSchema(t *testing.T) {
 		"no health check": {
 			Opts: LoadBalancerAddServiceOpts{
 				Protocol:        LoadBalancerServiceProtocolHTTP,
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &LoadBalancerAddServiceOptsHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Duration(5 * time.Minute),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * time.Minute),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
 					Certificates:   []*Certificate{{ID: 1}, {ID: 2}},
 				},
 			},
 			Request: schema.LoadBalancerActionAddServiceRequest{
 				Protocol:        string(LoadBalancerServiceProtocolHTTP),
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &schema.LoadBalancerActionAddServiceRequestHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Int(5 * 60),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
-					Certificates:   intSlice([]int{1, 2}),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * 60),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
+					Certificates:   Ptr([]int{1, 2}),
 				},
 				HealthCheck: nil,
 			},
@@ -2586,53 +2586,53 @@ func TestLoadBalancerUpdateServiceOptsToSchema(t *testing.T) {
 		"all set": {
 			Opts: LoadBalancerUpdateServiceOpts{
 				Protocol:        LoadBalancerServiceProtocolHTTP,
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &LoadBalancerUpdateServiceOptsHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Duration(5 * time.Minute),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * time.Minute),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
 					Certificates:   []*Certificate{{ID: 1}, {ID: 2}},
 				},
 				HealthCheck: &LoadBalancerUpdateServiceOptsHealthCheck{
 					Protocol: LoadBalancerServiceProtocolHTTP,
-					Port:     Int(80),
-					Interval: Duration(5 * time.Second),
-					Timeout:  Duration(1 * time.Second),
-					Retries:  Int(3),
+					Port:     Ptr(80),
+					Interval: Ptr(5 * time.Second),
+					Timeout:  Ptr(1 * time.Second),
+					Retries:  Ptr(3),
 					HTTP: &LoadBalancerUpdateServiceOptsHealthCheckHTTP{
-						Domain:      String("example.com"),
-						Path:        String("/health"),
-						Response:    String("ok"),
+						Domain:      Ptr("example.com"),
+						Path:        Ptr("/health"),
+						Response:    Ptr("ok"),
 						StatusCodes: []string{"2??", "3??"},
-						TLS:         Bool(true),
+						TLS:         Ptr(true),
 					},
 				},
 			},
 			Request: schema.LoadBalancerActionUpdateServiceRequest{
-				Protocol:        String(string(LoadBalancerServiceProtocolHTTP)),
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				Protocol:        Ptr(string(LoadBalancerServiceProtocolHTTP)),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &schema.LoadBalancerActionUpdateServiceRequestHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Int(5 * 60),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
-					Certificates:   intSlice([]int{1, 2}),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * 60),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
+					Certificates:   Ptr([]int{1, 2}),
 				},
 				HealthCheck: &schema.LoadBalancerActionUpdateServiceRequestHealthCheck{
-					Protocol: String(string(LoadBalancerServiceProtocolHTTP)),
-					Port:     Int(80),
-					Interval: Int(5),
-					Timeout:  Int(1),
-					Retries:  Int(3),
+					Protocol: Ptr(string(LoadBalancerServiceProtocolHTTP)),
+					Port:     Ptr(80),
+					Interval: Ptr(5),
+					Timeout:  Ptr(1),
+					Retries:  Ptr(3),
 					HTTP: &schema.LoadBalancerActionUpdateServiceRequestHealthCheckHTTP{
-						Domain:      String("example.com"),
-						Path:        String("/health"),
-						Response:    String("ok"),
-						StatusCodes: stringSlice([]string{"2??", "3??"}),
-						TLS:         Bool(true),
+						Domain:      Ptr("example.com"),
+						Path:        Ptr("/health"),
+						Response:    Ptr("ok"),
+						StatusCodes: Ptr([]string{"2??", "3??"}),
+						TLS:         Ptr(true),
 					},
 				},
 			},
@@ -2640,26 +2640,26 @@ func TestLoadBalancerUpdateServiceOptsToSchema(t *testing.T) {
 		"no health check": {
 			Opts: LoadBalancerUpdateServiceOpts{
 				Protocol:        LoadBalancerServiceProtocolHTTP,
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &LoadBalancerUpdateServiceOptsHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Duration(5 * time.Minute),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * time.Minute),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
 					Certificates:   []*Certificate{{ID: 1}, {ID: 2}},
 				},
 			},
 			Request: schema.LoadBalancerActionUpdateServiceRequest{
-				Protocol:        String(string(LoadBalancerServiceProtocolHTTP)),
-				DestinationPort: Int(80),
-				Proxyprotocol:   Bool(true),
+				Protocol:        Ptr(string(LoadBalancerServiceProtocolHTTP)),
+				DestinationPort: Ptr(80),
+				Proxyprotocol:   Ptr(true),
 				HTTP: &schema.LoadBalancerActionUpdateServiceRequestHTTP{
-					CookieName:     String("keks"),
-					CookieLifetime: Int(5 * 60),
-					RedirectHTTP:   Bool(true),
-					StickySessions: Bool(true),
-					Certificates:   intSlice([]int{1, 2}),
+					CookieName:     Ptr("keks"),
+					CookieLifetime: Ptr(5 * 60),
+					RedirectHTTP:   Ptr(true),
+					StickySessions: Ptr(true),
+					Certificates:   Ptr([]int{1, 2}),
 				},
 				HealthCheck: nil,
 			},
diff --git a/hcloud/server.go b/hcloud/server.go
index 00bcb90..da708a2 100644
--- a/hcloud/server.go
+++ b/hcloud/server.go
@@ -458,13 +458,34 @@ func (c *ServerClient) Create(ctx context.Context, opts ServerCreateOpts) (Serve
 	return result, resp, nil
 }
 
+// ServerDeleteResult is the result of a delete server call.
+type ServerDeleteResult struct {
+	Action *Action
+}
+
 // Delete deletes a server.
+// This method is deprecated, use ServerClient.DeleteWithResult instead.
 func (c *ServerClient) Delete(ctx context.Context, server *Server) (*Response, error) {
+	_, resp, err := c.DeleteWithResult(ctx, server)
+	return resp, err
+}
+
+// Delete deletes a server and returns the parsed response containing the action.
+func (c *ServerClient) DeleteWithResult(ctx context.Context, server *Server) (*ServerDeleteResult, *Response, error) {
 	req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/servers/%d", server.ID), nil)
 	if err != nil {
-		return nil, err
+		return &ServerDeleteResult{}, nil, err
 	}
-	return c.client.Do(req, nil)
+
+	var respBody schema.ServerDeleteResponse
+	resp, err := c.client.Do(req, &respBody)
+	if err != nil {
+		return &ServerDeleteResult{}, resp, err
+	}
+
+	return &ServerDeleteResult{
+		Action: ActionFromSchema(respBody.Action),
+	}, resp, nil
 }
 
 // ServerUpdateOpts specifies options for updating a server.
@@ -643,7 +664,7 @@ func (c *ServerClient) CreateImage(ctx context.Context, server *Server, opts *Se
 			reqBody.Description = opts.Description
 		}
 		if opts.Type != "" {
-			reqBody.Type = String(string(opts.Type))
+			reqBody.Type = Ptr(string(opts.Type))
 		}
 		if opts.Labels != nil {
 			reqBody.Labels = &opts.Labels
@@ -686,7 +707,7 @@ type ServerEnableRescueResult struct {
 // EnableRescue enables rescue mode for a server.
 func (c *ServerClient) EnableRescue(ctx context.Context, server *Server, opts ServerEnableRescueOpts) (ServerEnableRescueResult, *Response, error) {
 	reqBody := schema.ServerActionEnableRescueRequest{
-		Type: String(string(opts.Type)),
+		Type: Ptr(string(opts.Type)),
 	}
 	for _, sshKey := range opts.SSHKeys {
 		reqBody.SSHKeys = append(reqBody.SSHKeys, sshKey.ID)
@@ -811,7 +832,7 @@ func (c *ServerClient) DetachISO(ctx context.Context, server *Server) (*Action,
 func (c *ServerClient) EnableBackup(ctx context.Context, server *Server, window string) (*Action, *Response, error) {
 	reqBody := schema.ServerActionEnableBackupRequest{}
 	if window != "" {
-		reqBody.BackupWindow = String(window)
+		reqBody.BackupWindow = Ptr(window)
 	}
 	reqBodyData, err := json.Marshal(reqBody)
 	if err != nil {
@@ -964,10 +985,10 @@ func (c *ServerClient) AttachToNetwork(ctx context.Context, server *Server, opts
 		Network: opts.Network.ID,
 	}
 	if opts.IP != nil {
-		reqBody.IP = String(opts.IP.String())
+		reqBody.IP = Ptr(opts.IP.String())
 	}
 	for _, aliasIP := range opts.AliasIPs {
-		reqBody.AliasIPs = append(reqBody.AliasIPs, String(aliasIP.String()))
+		reqBody.AliasIPs = append(reqBody.AliasIPs, Ptr(aliasIP.String()))
 	}
 	reqBodyData, err := json.Marshal(reqBody)
 	if err != nil {
diff --git a/hcloud/server_test.go b/hcloud/server_test.go
index 81681cd..3b79c30 100644
--- a/hcloud/server_test.go
+++ b/hcloud/server_test.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+
 	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
@@ -341,7 +342,7 @@ func TestServersCreateWithoutSSHKeys(t *testing.T) {
 			Server: schema.Server{
 				ID: 1,
 			},
-			RootPassword: String("test"),
+			RootPassword: Ptr("test"),
 		})
 	})
 
@@ -399,7 +400,7 @@ func TestServersCreateWithVolumes(t *testing.T) {
 			{ID: 1},
 			{ID: 2},
 		},
-		Automount: Bool(true),
+		Automount: Ptr(true),
 	})
 	if err != nil {
 		t.Fatal(err)
@@ -846,7 +847,7 @@ func TestServersCreateWithLabels(t *testing.T) {
 			Server: schema.Server{
 				ID: 1,
 			},
-			RootPassword: String("test"),
+			RootPassword: Ptr("test"),
 		})
 	})
 
@@ -892,7 +893,7 @@ func TestServersCreateWithoutStarting(t *testing.T) {
 		Name:             "test",
 		ServerType:       &ServerType{ID: 1},
 		Image:            &Image{ID: 2},
-		StartAfterCreate: Bool(false),
+		StartAfterCreate: Ptr(false),
 	})
 	if err != nil {
 		t.Fatal(err)
@@ -1022,7 +1023,16 @@ func TestServersDelete(t *testing.T) {
 	env := newTestEnv()
 	defer env.Teardown()
 
-	env.Mux.HandleFunc("/servers/1", func(w http.ResponseWriter, r *http.Request) {})
+	env.Mux.HandleFunc("/servers/1", func(w http.ResponseWriter, r *http.Request) {
+		if r.Method != "DELETE" {
+			t.Error("expected DELETE")
+		}
+		json.NewEncoder(w).Encode(schema.ServerDeleteResponse{
+			Action: schema.Action{
+				ID: 2,
+			},
+		})
+	})
 
 	var (
 		ctx    = context.Background()
@@ -1034,6 +1044,34 @@ func TestServersDelete(t *testing.T) {
 	}
 }
 
+func TestServersDeleteWithResult(t *testing.T) {
+	env := newTestEnv()
+	defer env.Teardown()
+
+	env.Mux.HandleFunc("/servers/1", func(w http.ResponseWriter, r *http.Request) {
+		if r.Method != "DELETE" {
+			t.Error("expected DELETE")
+		}
+		json.NewEncoder(w).Encode(schema.ServerDeleteResponse{
+			Action: schema.Action{
+				ID: 2,
+			},
+		})
+	})
+
+	var (
+		ctx    = context.Background()
+		server = &Server{ID: 1}
+	)
+	result, _, err := env.Client.Server.DeleteWithResult(ctx, server)
+	if err != nil {
+		t.Fatalf("Server.Delete failed: %s", err)
+	}
+	if result.Action.ID != 2 {
+		t.Errorf("unexpected action ID: %v", result.Action.ID)
+	}
+}
+
 func TestServerClientUpdate(t *testing.T) {
 	var (
 		ctx    = context.Background()
@@ -1336,7 +1374,7 @@ func TestServerClientCreateImageWithOptions(t *testing.T) {
 	ctx := context.Background()
 	opts := &ServerCreateImageOpts{
 		Type:        ImageTypeBackup,
-		Description: String("my backup"),
+		Description: Ptr("my backup"),
 	}
 	result, _, err := env.Client.Server.CreateImage(ctx, &Server{ID: 1}, opts)
 	if err != nil {
@@ -1773,8 +1811,8 @@ func TestServerClientChangeProtection(t *testing.T) {
 		})
 
 		opts := ServerChangeProtectionOpts{
-			Delete:  Bool(true),
-			Rebuild: Bool(true),
+			Delete:  Ptr(true),
+			Rebuild: Ptr(true),
 		}
 		action, _, err := env.Client.Server.ChangeProtection(ctx, server, opts)
 		if err != nil {
diff --git a/hcloud/volume.go b/hcloud/volume.go
index 93e221e..f939d89 100644
--- a/hcloud/volume.go
+++ b/hcloud/volume.go
@@ -210,7 +210,7 @@ func (c *VolumeClient) Create(ctx context.Context, opts VolumeCreateOpts) (Volum
 		reqBody.Labels = &opts.Labels
 	}
 	if opts.Server != nil {
-		reqBody.Server = Int(opts.Server.ID)
+		reqBody.Server = Ptr(opts.Server.ID)
 	}
 	if opts.Location != nil {
 		if opts.Location.ID != 0 {
diff --git a/hcloud/volume_test.go b/hcloud/volume_test.go
index fdb046b..025ee17 100644
--- a/hcloud/volume_test.go
+++ b/hcloud/volume_test.go
@@ -438,8 +438,8 @@ func TestVolumeClientCreateWithAutomount(t *testing.T) {
 		Size:      42,
 		Server:    &Server{ID: 1},
 		Labels:    map[string]string{"key": "value"},
-		Automount: Bool(true),
-		Format:    String("xfs"),
+		Automount: Ptr(true),
+		Format:    Ptr("xfs"),
 	}
 	result, _, err := env.Client.Volume.Create(ctx, opts)
 	if err != nil {

More details

Full run details

Historical runs