Repository: stackitcloud/terraform-provider-stackit
Pull request #697: Add model serving
Author: login: PatrickKoss
Created: 2025-02-26T16:50:13Z | Merged: 2025-03-28T15:20:26Z

Add model serving to stackit terraform provider


Diff:
diff --git a/docs/index.md b/docs/index.md
index ae1998cda..d02d6e8b4 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -162,6 +162,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
 - `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
 - `logme_custom_endpoint` (String) Custom endpoint for the LogMe service
 - `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service
+- `modelserving_custom_endpoint` (String) Custom endpoint for the Model Serving service
 - `mongodbflex_custom_endpoint` (String) Custom endpoint for the MongoDB Flex service
 - `objectstorage_custom_endpoint` (String) Custom endpoint for the Object Storage service
 - `observability_custom_endpoint` (String) Custom endpoint for the Observability service
diff --git a/docs/resources/modelserving_token.md b/docs/resources/modelserving_token.md
new file mode 100644
index 000000000..ad5b13507
--- /dev/null
+++ b/docs/resources/modelserving_token.md
@@ -0,0 +1,71 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackit_modelserving_token Resource - stackit"
+subcategory: ""
+description: |-
+  Model Serving Auth Token Resource schema.
+  Example Usage
+  Automatically rotate model serving token
+  
+  resource "time_rotating" "rotate" {
+      rotation_days = 80
+  }
+  
+  resource "stackit_modelserving_token" "example" {
+      project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+      name       = "Example token"
+      
+      rotate_when_changed = {
+          rotation = time_rotating.rotate.id
+      }
+  
+  }
+---
+
+# stackit_modelserving_token (Resource)
+
+Model Serving Auth Token Resource schema.
+
+## Example Usage
+
+### Automatically rotate model serving token
+```terraform
+resource "time_rotating" "rotate" {
+    rotation_days = 80
+}
+
+resource "stackit_modelserving_token" "example" {
+    project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+    name       = "Example token"
+    
+    rotate_when_changed = {
+        rotation = time_rotating.rotate.id
+    }
+
+}
+```
+
+
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `name` (String) Name of the model serving auth token.
+- `project_id` (String) STACKIT project ID to which the model serving auth token is associated.
+
+### Optional
+
+- `description` (String) The description of the model serving auth token.
+- `region` (String) Region to which the model serving auth token is associated. If not defined, the provider region is used
+- `rotate_when_changed` (Map of String) A map of arbitrary key/value pairs that will force recreation of the token when they change, enabling token rotation based on external conditions such as a rotating timestamp. Changing this forces a new resource to be created.
+- `ttl_duration` (String) The TTL duration of the model serving auth token. E.g. 5h30m40s,5h,5h30m,30m,30s
+
+### Read-Only
+
+- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`token_id`".
+- `state` (String) State of the model serving auth token.
+- `token` (String, Sensitive) Content of the model serving auth token.
+- `token_id` (String) The model serving auth token ID.
+- `valid_until` (String) The time until the model serving auth token is valid.
diff --git a/go.mod b/go.mod
index 44d03780f..d4d787c48 100644
--- a/go.mod
+++ b/go.mod
@@ -18,6 +18,7 @@ require (
 	github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.0.0
 	github.com/stackitcloud/stackit-sdk-go/services/logme v0.21.0
 	github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.21.0
+	github.com/stackitcloud/stackit-sdk-go/services/modelserving v0.2.1
 	github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.18.0
 	github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.1.0
 	github.com/stackitcloud/stackit-sdk-go/services/observability v0.3.0
@@ -40,7 +41,7 @@ require (
 require github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
 
 require (
-	github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
+	github.com/ProtonMail/go-crypto v1.1.3 // indirect
 	github.com/agext/levenshtein v1.2.2 // indirect
 	github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
 	github.com/cloudflare/circl v1.3.7 // indirect
@@ -56,17 +57,17 @@ require (
 	github.com/hashicorp/go-plugin v1.6.2 // indirect
 	github.com/hashicorp/go-uuid v1.0.3 // indirect
 	github.com/hashicorp/go-version v1.7.0 // indirect
-	github.com/hashicorp/hc-install v0.9.0 // indirect
+	github.com/hashicorp/hc-install v0.9.1 // indirect
 	github.com/hashicorp/hcl/v2 v2.23.0 // indirect
 	github.com/hashicorp/logutils v1.0.0 // indirect
-	github.com/hashicorp/terraform-exec v0.21.0 // indirect
-	github.com/hashicorp/terraform-json v0.23.0 // indirect
+	github.com/hashicorp/terraform-exec v0.22.0 // indirect
+	github.com/hashicorp/terraform-json v0.24.0 // indirect
 	github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 // indirect
 	github.com/hashicorp/terraform-registry-address v0.2.4 // indirect
 	github.com/hashicorp/terraform-svchost v0.1.1 // indirect
 	github.com/hashicorp/yamux v0.1.1 // indirect
 	github.com/kr/pretty v0.3.1 // indirect
-	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-colorable v0.1.14 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -74,19 +75,19 @@ require (
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/oklog/run v1.1.0 // indirect
-	github.com/rogpeppe/go-internal v1.12.0 // indirect
+	github.com/rogpeppe/go-internal v1.13.1 // indirect
 	github.com/stackitcloud/stackit-sdk-go/services/authorization v0.6.0
 	github.com/stretchr/testify v1.8.4 // indirect
 	github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
 	github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	github.com/zclconf/go-cty v1.15.0 // indirect
+	github.com/zclconf/go-cty v1.16.2 // indirect
 	golang.org/x/crypto v0.32.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
-	golang.org/x/sync v0.10.0 // indirect
+	golang.org/x/sync v0.11.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
-	golang.org/x/text v0.21.0 // indirect
-	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
+	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/tools v0.22.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
 	google.golang.org/grpc v1.69.4 // indirect
diff --git a/go.sum b/go.sum
index 784cb61d6..2d8d559b5 100644
--- a/go.sum
+++ b/go.sum
@@ -2,8 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
 dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
 github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
-github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
+github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
 github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
 github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
 github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
@@ -14,8 +14,8 @@ github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp
 github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
 github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
-github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
+github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -26,10 +26,10 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
-github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
-github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
-github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
+github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
+github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
+github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
+github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -76,16 +76,16 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C
 github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
 github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE=
-github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg=
+github.com/hashicorp/hc-install v0.9.1 h1:gkqTfE3vVbafGQo6VZXcy2v5yoz2bE0+nhZXruCuODQ=
+github.com/hashicorp/hc-install v0.9.1/go.mod h1:pWWvN/IrfeBK4XPeXXYkL6EjMufHkCK5DvwxeLKuBf0=
 github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
 github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
 github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
-github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
-github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
-github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
+github.com/hashicorp/terraform-exec v0.22.0 h1:G5+4Sz6jYZfRYUCg6eQgDsqTzkNXV+fP8l+uRmZHj64=
+github.com/hashicorp/terraform-exec v0.22.0/go.mod h1:bjVbsncaeh8jVdhttWYZuBGj21FcYw6Ia/XfHcNO7lQ=
+github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4NbeATsYz8Q=
+github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow=
 github.com/hashicorp/terraform-plugin-framework v1.14.1 h1:jaT1yvU/kEKEsxnbrn4ZHlgcxyIfjvZ41BLdlLk52fY=
 github.com/hashicorp/terraform-plugin-framework v1.14.1/go.mod h1:xNUKmvTs6ldbwTuId5euAtg37dTxuyj3LHS3uj7BHQ4=
 github.com/hashicorp/terraform-plugin-framework-validators v0.17.0 h1:0uYQcqqgW3BMyyve07WJgpKorXST3zkpzvrOnf3mpbg=
@@ -120,11 +120,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
@@ -145,12 +144,12 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
-github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
-github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
+github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
+github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
 github.com/stackitcloud/stackit-sdk-go/core v0.16.2 h1:F8A4P/LLlQSbz0S0+G3m8rb3BUOK6EcR/CKx5UQY5jQ=
 github.com/stackitcloud/stackit-sdk-go/core v0.16.2/go.mod h1:8KIw3czdNJ9sdil9QQimxjR6vHjeINFrRv0iZ67wfn0=
 github.com/stackitcloud/stackit-sdk-go/services/argus v0.11.0 h1:JVEx/ouHB6PlwGzQa3ywyDym1HTWo3WgrxAyXprCnuM=
@@ -167,6 +166,8 @@ github.com/stackitcloud/stackit-sdk-go/services/logme v0.21.0 h1:P7bxaVzkZPGMWIt
 github.com/stackitcloud/stackit-sdk-go/services/logme v0.21.0/go.mod h1:os4Kp2+jkMUJ2dZtgU9A91N3EJSw3MMh2slxgK1609g=
 github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.21.0 h1:ks1i+cfD/YPRss//4aq6uvxbLvUwb5QvcUrOPeboLFY=
 github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.21.0/go.mod h1:kGAT87SO5Wkv/CSZevMZcPml3V38G6tnT1Wvdkdmkv4=
+github.com/stackitcloud/stackit-sdk-go/services/modelserving v0.2.1 h1:6G6AZWZMWYRw4l+Gvg0KG3VcYnl/phOA8bpJ+2Yz/Rw=
+github.com/stackitcloud/stackit-sdk-go/services/modelserving v0.2.1/go.mod h1:i3wU8mtK1sdu8AaVDFc39nfKyjuRAd/0oCnuGl1wMfs=
 github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.18.0 h1:mXVFa5/5uvOibPAUU0HTM7uf7H95IbnYnIzNvR5gB00=
 github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.18.0/go.mod h1:uuTdgDo4Ju2W0eMfHc3a5n9SXNKJPdxuq15e3AFss6Q=
 github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.1.0 h1:kWfmDQeTMijx0ySPiPfL4EU1TL6lcpkRVrzXenSaX6w=
@@ -213,8 +214,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV
 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
 github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
-github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
+github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
+github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
 github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
 github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
 go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
@@ -243,8 +244,8 @@ golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -255,7 +256,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -268,13 +268,13 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
diff --git a/stackit/internal/core/core.go b/stackit/internal/core/core.go
index d1fbe1b96..f4502311d 100644
--- a/stackit/internal/core/core.go
+++ b/stackit/internal/core/core.go
@@ -27,6 +27,7 @@ type ProviderData struct {
 	LogMeCustomEndpoint             string
 	MariaDBCustomEndpoint           string
 	MongoDBFlexCustomEndpoint       string
+	ModelServingCustomEndpoint      string
 	ObjectStorageCustomEndpoint     string
 	ObservabilityCustomEndpoint     string
 	OpenSearchCustomEndpoint        string
diff --git a/stackit/internal/services/modelserving/modelserving_acc_test.go b/stackit/internal/services/modelserving/modelserving_acc_test.go
new file mode 100644
index 000000000..4f31d5ca4
--- /dev/null
+++ b/stackit/internal/services/modelserving/modelserving_acc_test.go
@@ -0,0 +1,158 @@
+package modelserving_test
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+	"github.com/hashicorp/terraform-plugin-testing/terraform"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
+)
+
+// Token resource data
+var tokenResource = map[string]string{
+	"project_id":          testutil.ProjectId,
+	"name":                "token01",
+	"description":         "my description",
+	"description_updated": "my description updated",
+	"region":              testutil.Region,
+	"ttl_duration":        "1h",
+}
+
+func inputTokenConfig(name, description string) string {
+	return fmt.Sprintf(`
+		%s
+
+		resource "stackit_modelserving_token" "token" {
+			project_id = "%s"
+			region = "%s"
+			name = "%s"
+			description = "%s"
+			ttl_duration = "%s"
+		}
+		`,
+		testutil.ModelServingProviderConfig(),
+		tokenResource["project_id"],
+		tokenResource["region"],
+		name,
+		description,
+		tokenResource["ttl_duration"],
+	)
+}
+
+func TestAccModelServingTokenResource(t *testing.T) {
+	resource.Test(t, resource.TestCase{
+		ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
+		CheckDestroy:             testAccCheckModelServingTokenDestroy,
+		Steps: []resource.TestStep{
+			// Creation
+			{
+				Config: inputTokenConfig(
+					tokenResource["name"],
+					tokenResource["description"],
+				),
+				Check: resource.ComposeAggregateTestCheckFunc(
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "project_id", tokenResource["project_id"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "region", tokenResource["region"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "name", tokenResource["name"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "description", tokenResource["description"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "ttl_duration", tokenResource["ttl_duration"]),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "token_id"),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "state"),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "valid_until"),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "token"),
+				),
+			},
+			// Update
+			{
+				Config: inputTokenConfig(
+					tokenResource["name"],
+					tokenResource["description_updated"],
+				),
+				Check: resource.ComposeAggregateTestCheckFunc(
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "project_id", tokenResource["project_id"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "region", tokenResource["region"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "name", tokenResource["name"]),
+					resource.TestCheckResourceAttr("stackit_modelserving_token.token", "description", tokenResource["description_updated"]),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "token_id"),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "state"),
+					resource.TestCheckResourceAttrSet("stackit_modelserving_token.token", "valid_until"),
+				),
+			},
+			// Deletion is done by the framework implicitly
+		},
+	})
+}
+
+func testAccCheckModelServingTokenDestroy(s *terraform.State) error {
+	ctx := context.Background()
+	var client *modelserving.APIClient
+	var err error
+
+	if testutil.ModelServingCustomEndpoint == "" {
+		client, err = modelserving.NewAPIClient()
+	} else {
+		client, err = modelserving.NewAPIClient(
+			config.WithEndpoint(testutil.ModelServingCustomEndpoint),
+		)
+	}
+
+	if err != nil {
+		return fmt.Errorf("creating client: %w", err)
+	}
+
+	tokensToDestroy := []string{}
+	for _, rs := range s.RootModule().Resources {
+		if rs.Type != "stackit_modelserving_token" {
+			continue
+		}
+
+		// Token terraform ID: "[project_id],[region],[token_id]"
+		idParts := strings.Split(rs.Primary.ID, core.Separator)
+		if len(idParts) != 3 {
+			return fmt.Errorf("invalid ID: %s", rs.Primary.ID)
+		}
+		if idParts[2] != "" {
+			tokensToDestroy = append(tokensToDestroy, idParts[2])
+		}
+	}
+
+	if len(tokensToDestroy) == 0 {
+		return nil
+	}
+
+	tokensResp, err := client.ListTokens(ctx, testutil.Region, testutil.ProjectId).Execute()
+	if err != nil {
+		return fmt.Errorf("getting tokensResp: %w", err)
+	}
+
+	if tokensResp.Tokens == nil || (tokensResp.Tokens != nil && len(*tokensResp.Tokens) == 0) {
+		fmt.Print("No tokens found for project \n")
+		return nil
+	}
+
+	items := *tokensResp.Tokens
+	for i := range items {
+		if items[i].Name == nil {
+			continue
+		}
+		if utils.Contains(tokensToDestroy, *items[i].Name) {
+			_, err := client.DeleteToken(ctx, testutil.Region, testutil.ProjectId, *items[i].Id).Execute()
+			if err != nil {
+				return fmt.Errorf("destroying token %s during CheckDestroy: %w", *items[i].Name, err)
+			}
+			_, err = wait.DeleteModelServingWaitHandler(ctx, client, testutil.Region, testutil.ProjectId, *items[i].Id).WaitWithContext(ctx)
+			if err != nil {
+				return fmt.Errorf("destroying token %s during CheckDestroy: waiting for deletion %w", *items[i].Name, err)
+			}
+		}
+	}
+	return nil
+}
diff --git a/stackit/internal/services/modelserving/token/description.md b/stackit/internal/services/modelserving/token/description.md
new file mode 100644
index 000000000..43ad6daf5
--- /dev/null
+++ b/stackit/internal/services/modelserving/token/description.md
@@ -0,0 +1,20 @@
+Model Serving Auth Token Resource schema.
+
+## Example Usage
+
+### Automatically rotate model serving token
+```terraform
+resource "time_rotating" "rotate" {
+    rotation_days = 80
+}
+
+resource "stackit_modelserving_token" "example" {
+    project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+    name       = "Example token"
+    
+    rotate_when_changed = {
+        rotation = time_rotating.rotate.id
+    }
+
+}
+```
\ No newline at end of file
diff --git a/stackit/internal/services/modelserving/token/resource.go b/stackit/internal/services/modelserving/token/resource.go
new file mode 100644
index 000000000..8ad04a976
--- /dev/null
+++ b/stackit/internal/services/modelserving/token/resource.go
@@ -0,0 +1,676 @@
+package token
+
+import (
+	"context"
+	_ "embed"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource               = &tokenResource{}
+	_ resource.ResourceWithConfigure  = &tokenResource{}
+	_ resource.ResourceWithModifyPlan = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+//go:embed description.md
+var markdownDescription string
+
+type Model struct {
+	Id          types.String `tfsdk:"id"` // needed by TF
+	ProjectId   types.String `tfsdk:"project_id"`
+	Region      types.String `tfsdk:"region"`
+	TokenId     types.String `tfsdk:"token_id"`
+	Name        types.String `tfsdk:"name"`
+	Description types.String `tfsdk:"description"`
+	State       types.String `tfsdk:"state"`
+	ValidUntil  types.String `tfsdk:"valid_until"`
+	TTLDuration types.String `tfsdk:"ttl_duration"`
+	Token       types.String `tfsdk:"token"`
+	// RotateWhenChanged is a map of arbitrary key/value pairs that will force
+	// recreation of the token when they change, enabling token rotation based on
+	// external conditions such as a rotating timestamp. Changing this forces a new
+	// resource to be created.
+	RotateWhenChanged types.Map `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	var serviceEnablementClient *serviceenablement.APIClient
+	if providerData.ServiceEnablementCustomEndpoint != "" {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ServiceEnablementCustomEndpoint),
+		)
+	} else {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring service enablement client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	r.serviceEnablementClient = serviceEnablementClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// ModifyPlan implements resource.ResourceWithModifyPlan.
+// Use the modifier to set the effective region in the current plan.
+func (r *tokenResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
+	var configModel Model
+
+	// skip initial empty configuration to avoid follow-up errors
+	if req.Config.Raw.IsNull() {
+		return
+	}
+	resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	var planModel Model
+	resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	utils.AdaptRegion(
+		ctx,
+		configModel.Region,
+		&planModel.Region,
+		r.providerData.GetRegion(),
+		resp,
+	)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
+	resp.Schema = schema.Schema{
+		Description: markdownDescription,
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`region`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Optional: true,
+				// must be computed to allow for storing the override value from the provider
+				Computed:    true,
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used",
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token. E.g. 5h30m40s,5h,5h30m,30m,30s",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+				Validators: []validator.String{
+					validate.ValidDurationString(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				Validators: []validator.String{
+					stringvalidator.LengthBetween(1, 2000),
+				},
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+				Validators: []validator.String{
+					stringvalidator.LengthBetween(1, 200),
+				},
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"token": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+				Sensitive:   true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// If model serving is not enabled, enable it
+	err := r.serviceEnablementClient.EnableServiceRegional(ctx, region, projectId, utils.ModelServingServiceId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				core.LogAndAddError(ctx, &resp.Diagnostics, "Error enabling model serving",
+					fmt.Sprintf("Service not available in region %s \n%v", region, err),
+				)
+				return
+			}
+		}
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	_, err = serviceEnablementWait.EnableServiceWaitHandler(ctx, r.serviceEnablementClient, region, projectId, utils.ModelServingServiceId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating model serving auth token", fmt.Sprintf("Creating API payload: %v", err))
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating model serving auth token", fmt.Sprintf("Waiting for token to be active: %v", err))
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model, region)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating model serving auth token", fmt.Sprintf("Processing API payload: %v", err))
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading model serving auth token", fmt.Sprintf("Calling API: %v", err))
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(ctx, &resp.Diagnostics, "Error reading model serving auth token", "Model serving auth token has expired")
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading model serving auth token", fmt.Sprintf("Processing API payload: %v", err))
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating model serving auth token", fmt.Sprintf("Creating API payload: %v", err))
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).PartialUpdateTokenPayload(*payload).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(ctx, &resp.Diagnostics, "Error updating model serving auth token", "Model serving auth token has expired")
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating model serving auth token", fmt.Sprintf("Waiting for token to be updated: %v", err))
+		return
+	}
+
+	// Since STACKIT is not saving the content of the token. We have to use it from the state.
+	model.Token = state.Token
+	err = mapGetResponse(waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating model serving auth token", fmt.Sprintf("Processing API payload: %v", err))
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting model serving auth token", fmt.Sprintf("Calling API: %v", err))
+		return
+	}
+
+	_, err = wait.DeleteModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting model serving auth token", fmt.Sprintf("Waiting for token to be deleted: %v", err))
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token deleted")
+}
+
+func mapCreateResponse(tokenCreateResp *modelserving.CreateTokenResponse, waitResp *modelserving.GetTokenResponse, model *Model, region string) error {
+	if tokenCreateResp == nil || tokenCreateResp.Token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	token := tokenCreateResp.Token
+
+	if token.Id == nil {
+		return fmt.Errorf("token id not present")
+	}
+
+	validUntil := types.StringNull()
+	if token.ValidUntil != nil {
+		validUntil = types.StringValue(token.ValidUntil.Format(time.RFC3339))
+	}
+
+	if waitResp == nil || waitResp.Token == nil || waitResp.Token.State == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	idParts := []string{model.ProjectId.ValueString(), region, *tokenCreateResp.Token.Id}
+	model.Id = types.StringValue(
+		strings.Join(idParts, core.Separator),
+	)
+	model.TokenId = types.StringPointerValue(token.Id)
+	model.Name = types.StringPointerValue(token.Name)
+	model.State = types.StringPointerValue(waitResp.Token.State)
+	model.ValidUntil = validUntil
+	model.Token = types.StringPointerValue(token.Content)
+	model.Description = types.StringPointerValue(token.Description)
+
+	return nil
+}
+
+func mapGetResponse(tokenGetResp *modelserving.GetTokenResponse, model *Model) error {
+	if tokenGetResp == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	if tokenGetResp.Token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	// theoretically, should never happen, but still catch null pointers
+	validUntil := types.StringNull()
+	if tokenGetResp.Token.ValidUntil != nil {
+		validUntil = types.StringValue(tokenGetResp.Token.ValidUntil.Format(time.RFC3339))
+	}
+
+	idParts := []string{model.ProjectId.ValueString(), model.Region.ValueString(), model.TokenId.ValueString()}
+	model.Id = types.StringValue(strings.Join(idParts, core.Separator))
+	model.TokenId = types.StringPointerValue(tokenGetResp.Token.Id)
+	model.Name = types.StringPointerValue(tokenGetResp.Token.Name)
+	model.State = types.StringPointerValue(tokenGetResp.Token.State)
+	model.ValidUntil = validUntil
+	model.Description = types.StringPointerValue(tokenGetResp.Token.Description)
+
+	return nil
+}
+
+func toCreatePayload(model *Model) (*modelserving.CreateTokenPayload, error) {
+	if model == nil {
+		return nil, fmt.Errorf("nil model")
+	}
+
+	return &modelserving.CreateTokenPayload{
+		Name:        conversion.StringValueToPointer(model.Name),
+		Description: conversion.StringValueToPointer(model.Description),
+		TtlDuration: conversion.StringValueToPointer(model.TTLDuration),
+	}, nil
+}
+
+func toUpdatePayload(model *Model) (*modelserving.PartialUpdateTokenPayload, error) {
+	if model == nil {
+		return nil, fmt.Errorf("nil model")
+	}
+
+	return &modelserving.PartialUpdateTokenPayload{
+		Name:        conversion.StringValueToPointer(model.Name),
+		Description: conversion.StringValueToPointer(model.Description),
+	}, nil
+}
diff --git a/stackit/internal/services/modelserving/token/resource_test.go b/stackit/internal/services/modelserving/token/resource_test.go
new file mode 100644
index 000000000..c8a2bc61f
--- /dev/null
+++ b/stackit/internal/services/modelserving/token/resource_test.go
@@ -0,0 +1,341 @@
+package token
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+)
+
+func TestMapGetTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		state       *Model
+		input       *modelserving.GetTokenResponse
+		expected    Model
+		isValid     bool
+	}{
+		{
+			description: "should error when response is nil",
+			state:       &Model{},
+			input:       nil,
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when token is nil in response",
+			state:       &Model{},
+			input:       &modelserving.GetTokenResponse{Token: nil},
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when state is nil in response",
+			state:       nil,
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{},
+			},
+			expected: Model{},
+			isValid:  false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,eu01,tid"),
+				ProjectId:         types.StringValue("pid"),
+				TokenId:           types.StringValue("tid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,eu01,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapGetResponse(tt.input, tt.state)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestMapCreateTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description              string
+		state                    *Model
+		inputCreateTokenResponse *modelserving.CreateTokenResponse
+		inputGetTokenResponse    *modelserving.GetTokenResponse
+		expected                 Model
+		isValid                  bool
+	}{
+		{
+			description:              "should error when create token response is nil",
+			state:                    &Model{},
+			inputCreateTokenResponse: nil,
+			inputGetTokenResponse:    nil,
+			expected:                 Model{},
+			isValid:                  false,
+		},
+		{
+			description: "should error when token is nil in create token response",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: nil,
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state: &Model{
+				Id:        types.StringValue("pid,eu01,tid"),
+				ProjectId: types.StringValue("pid"),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,eu01,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					State: utils.Ptr("active"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,eu01,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				Token:             types.StringValue("content"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapCreateResponse(
+				tt.inputCreateTokenResponse,
+				tt.inputGetTokenResponse,
+				tt.state,
+				"eu01",
+			)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToCreatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.CreateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+				TTLDuration: types.StringValue("1h"),
+			},
+			expected: &modelserving.CreateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+				TtlDuration: utils.Ptr("1h"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := toCreatePayload(tt.input)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(output, tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToUpdatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.PartialUpdateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+			},
+			expected: &modelserving.PartialUpdateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := toUpdatePayload(tt.input)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(output, tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go
index 133aa5be7..70347c0b2 100644
--- a/stackit/internal/testutil/testutil.go
+++ b/stackit/internal/testutil/testutil.go
@@ -56,6 +56,7 @@ var (
 	LoadBalancerCustomEndpoint    = os.Getenv("TF_ACC_LOADBALANCER_CUSTOM_ENDPOINT")
 	LogMeCustomEndpoint           = os.Getenv("TF_ACC_LOGME_CUSTOM_ENDPOINT")
 	MariaDBCustomEndpoint         = os.Getenv("TF_ACC_MARIADB_CUSTOM_ENDPOINT")
+	ModelServingCustomEndpoint    = os.Getenv("TF_ACC_MODELSERVING_CUSTOM_ENDPOINT")
 	AuthorizationCustomEndpoint   = os.Getenv("TF_ACC_authorization_custom_endpoint")
 	MongoDBFlexCustomEndpoint     = os.Getenv("TF_ACC_MONGODBFLEX_CUSTOM_ENDPOINT")
 	OpenSearchCustomEndpoint      = os.Getenv("TF_ACC_OPENSEARCH_CUSTOM_ENDPOINT")
@@ -178,6 +179,22 @@ func MariaDBProviderConfig() string {
 	)
 }
 
+func ModelServingProviderConfig() string {
+	if ModelServingCustomEndpoint == "" {
+		return `
+		provider "stackit" {
+			region = "eu01"
+		}
+		`
+	}
+	return fmt.Sprintf(`
+		provider "stackit" {
+			modelserving_custom_endpoint = "%s"
+		}`,
+		ModelServingCustomEndpoint,
+	)
+}
+
 func MongoDBFlexProviderConfig() string {
 	if MongoDBFlexCustomEndpoint == "" {
 		return `
diff --git a/stackit/internal/utils/regions.go b/stackit/internal/utils/regions.go
index 554d9b0aa..7af581c77 100644
--- a/stackit/internal/utils/regions.go
+++ b/stackit/internal/utils/regions.go
@@ -26,7 +26,7 @@ func AdaptRegion(ctx context.Context, configRegion types.String, planRegion *typ
 
 	// check if the currently configured region corresponds to the planned region
 	// on mismatch override the planned region with the intended region
-	// and force a replace of the resource
+	// and force a replacement of the resource
 	p := path.Root("region")
 	if !intendedRegion.Equal(*planRegion) {
 		resp.RequiresReplace.Append(p)
diff --git a/stackit/internal/utils/utils.go b/stackit/internal/utils/utils.go
index 4e9942b5a..dc938bb25 100644
--- a/stackit/internal/utils/utils.go
+++ b/stackit/internal/utils/utils.go
@@ -5,14 +5,14 @@ import (
 	"regexp"
 	"strings"
 
+	"github.com/hashicorp/terraform-plugin-framework/types"
 	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
 	"github.com/stackitcloud/stackit-sdk-go/core/utils"
-
-	"github.com/hashicorp/terraform-plugin-framework/types"
 )
 
 const (
-	SKEServiceId = "cloud.stackit.ske"
+	SKEServiceId          = "cloud.stackit.ske"
+	ModelServingServiceId = "cloud.stackit.model-serving"
 )
 
 var (
@@ -72,7 +72,7 @@ func ListValuetoStringSlice(list basetypes.ListValue) ([]string, error) {
 	return result, nil
 }
 
-// Remove leading 0s from backup schedule numbers (e.g. "00 00 * * *" becomes "0 0 * * *")
+// SimplifyBackupSchedule removes leading 0s from backup schedule numbers (e.g. "00 00 * * *" becomes "0 0 * * *")
 // Needed as the API does it internally and would otherwise cause inconsistent result in Terraform
 func SimplifyBackupSchedule(schedule string) string {
 	regex := regexp.MustCompile(`0+\d+`) // Matches series of one or more zeros followed by a series of one or more digits
diff --git a/stackit/internal/validate/validate.go b/stackit/internal/validate/validate.go
index 90f4779f5..57b149660 100644
--- a/stackit/internal/validate/validate.go
+++ b/stackit/internal/validate/validate.go
@@ -295,3 +295,21 @@ func FileExists() *Validator {
 		},
 	}
 }
+
+func ValidDurationString() *Validator {
+	description := "value must be in a valid duration string. Such as \"300ms\", \"-1.5h\" or \"2h45m\".\nValid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"."
+
+	return &Validator{
+		description: description,
+		validate: func(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
+			_, err := time.ParseDuration(req.ConfigValue.ValueString())
+			if err != nil {
+				resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
+					req.Path,
+					description,
+					req.ConfigValue.ValueString(),
+				))
+			}
+		},
+	}
+}
diff --git a/stackit/internal/validate/validate_test.go b/stackit/internal/validate/validate_test.go
index eba170caf..316180061 100644
--- a/stackit/internal/validate/validate_test.go
+++ b/stackit/internal/validate/validate_test.go
@@ -769,3 +769,79 @@ func TestFileExists(t *testing.T) {
 		})
 	}
 }
+
+func TestValidTtlDuration(t *testing.T) {
+	tests := []struct {
+		description string
+		input       string
+		isValid     bool
+	}{
+		{
+			"valid duration with hours, minutes, and seconds",
+			"5h30m40s",
+			true,
+		},
+		{
+			"valid duration with hours only",
+			"5h",
+			true,
+		},
+		{
+			"valid duration with hours and minutes",
+			"5h30m",
+			true,
+		},
+		{
+			"valid duration with minutes only",
+			"30m",
+			true,
+		},
+		{
+			"valid duration with seconds only",
+			"30s",
+			true,
+		},
+		{
+			"invalid duration with incorrect unit",
+			"30o",
+			false,
+		},
+		{
+			"invalid duration without unit",
+			"30",
+			false,
+		},
+		{
+			"invalid duration with invalid letters",
+			"30e",
+			false,
+		},
+		{
+			"invalid duration with letters in middle",
+			"1h30x",
+			false,
+		},
+		{
+			"empty string",
+			"",
+			false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			r := validator.StringResponse{}
+			va := ValidDurationString()
+			va.ValidateString(context.Background(), validator.StringRequest{
+				ConfigValue: types.StringValue(tt.input),
+			}, &r)
+
+			if !tt.isValid && !r.Diagnostics.HasError() {
+				t.Fatalf("Expected validation to fail for input: %v", tt.input)
+			}
+			if tt.isValid && r.Diagnostics.HasError() {
+				t.Fatalf("Expected validation to succeed for input: %v, but got errors: %v", tt.input, r.Diagnostics.Errors())
+			}
+		})
+	}
+}
diff --git a/stackit/provider.go b/stackit/provider.go
index ef83c2ef7..1090cb348 100644
--- a/stackit/provider.go
+++ b/stackit/provider.go
@@ -42,6 +42,7 @@ import (
 	logMeInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/instance"
 	mariaDBCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/mariadb/credential"
 	mariaDBInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/mariadb/instance"
+	modelServingToken "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/modelserving/token"
 	mongoDBFlexInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/mongodbflex/instance"
 	mongoDBFlexUser "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/mongodbflex/user"
 	objectStorageBucket "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/bucket"
@@ -118,6 +119,7 @@ type providerModel struct {
 	IaaSCustomEndpoint              types.String `tfsdk:"iaas_custom_endpoint"`
 	PostgresFlexCustomEndpoint      types.String `tfsdk:"postgresflex_custom_endpoint"`
 	MongoDBFlexCustomEndpoint       types.String `tfsdk:"mongodbflex_custom_endpoint"`
+	ModelServingCustomEndpoint      types.String `tfsdk:"modelserving_custom_endpoint"`
 	LoadBalancerCustomEndpoint      types.String `tfsdk:"loadbalancer_custom_endpoint"`
 	LogMeCustomEndpoint             types.String `tfsdk:"logme_custom_endpoint"`
 	RabbitMQCustomEndpoint          types.String `tfsdk:"rabbitmq_custom_endpoint"`
@@ -156,6 +158,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
 		"dns_custom_endpoint":                "Custom endpoint for the DNS service",
 		"iaas_custom_endpoint":               "Custom endpoint for the IaaS service",
 		"mongodbflex_custom_endpoint":        "Custom endpoint for the MongoDB Flex service",
+		"modelserving_custom_endpoint":       "Custom endpoint for the Model Serving service",
 		"loadbalancer_custom_endpoint":       "Custom endpoint for the Load Balancer service",
 		"logme_custom_endpoint":              "Custom endpoint for the LogMe service",
 		"rabbitmq_custom_endpoint":           "Custom endpoint for the RabbitMQ service",
@@ -246,6 +249,10 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
 				Optional:    true,
 				Description: descriptions["mariadb_custom_endpoint"],
 			},
+			"modelserving_custom_endpoint": schema.StringAttribute{
+				Optional:    true,
+				Description: descriptions["modelserving_custom_endpoint"],
+			},
 			"authorization_custom_endpoint": schema.StringAttribute{
 				Optional:    true,
 				Description: descriptions["authorization_custom_endpoint"],
@@ -376,6 +383,9 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
 	if !(providerConfig.PostgresFlexCustomEndpoint.IsUnknown() || providerConfig.PostgresFlexCustomEndpoint.IsNull()) {
 		providerData.PostgresFlexCustomEndpoint = providerConfig.PostgresFlexCustomEndpoint.ValueString()
 	}
+	if !(providerConfig.ModelServingCustomEndpoint.IsUnknown() || providerConfig.ModelServingCustomEndpoint.IsNull()) {
+		providerData.ModelServingCustomEndpoint = providerConfig.ModelServingCustomEndpoint.ValueString()
+	}
 	if !(providerConfig.MongoDBFlexCustomEndpoint.IsUnknown() || providerConfig.MongoDBFlexCustomEndpoint.IsNull()) {
 		providerData.MongoDBFlexCustomEndpoint = providerConfig.MongoDBFlexCustomEndpoint.ValueString()
 	}
@@ -537,6 +547,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
 		logMeCredential.NewCredentialResource,
 		mariaDBInstance.NewInstanceResource,
 		mariaDBCredential.NewCredentialResource,
+		modelServingToken.NewTokenResource,
 		mongoDBFlexInstance.NewInstanceResource,
 		mongoDBFlexUser.NewUserResource,
 		objectStorageBucket.NewBucketResource,


Reviews:
user: login: h3adex
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-26T19:28:02Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T06:32:58Z
user: login: busilina
state: COMMENTED
author_association: NONE
submitted_at: 2025-02-27T08:06:57Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T08:13:37Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T10:00:14Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T10:12:27Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T10:23:34Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T10:24:08Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T15:31:10Z
user: login: h3adex
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:58:16Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T16:52:50Z
user: login: h3adex
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T08:31:05Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T12:45:07Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T12:52:09Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T12:52:24Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T12:52:36Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T12:52:46Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:51:02Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:51:24Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:51:57Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T15:06:19Z
user: login: h3adex
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T15:29:25Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:42:50Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:58:24Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:58:56Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:59:01Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:59:37Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:02:30Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:05:27Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:12:08Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:12:25Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:13:36Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:14:05Z
user: login: bahkauv70
body: Generally please try not to reformat unrelated code if not strictly required by the linter etc.. This blows up the change and increases the required effort for reviewing... If possible, please revert changes to unrelated files, Thanks!
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:27:10Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:38:53Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T13:12:59Z
user: login: h3adex
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T13:16:50Z
user: login: PatrickKoss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T13:21:19Z
user: login: h3adex
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T14:54:18Z
user: login: bahkauv70
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T15:00:28Z
user: login: bahkauv70
body: Looks good now, all tests from my side worked
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:55:59Z
user: login: bahkauv70
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T08:26:55Z
user: login: bahkauv70
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T09:28:11Z

Review comments:
diff_hunk: @@ -0,0 +1,651 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/core/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id          types.String `tfsdk:"id"` // needed by TF
+	ProjectId   types.String `tfsdk:"project_id"`
+	Region      types.String `tfsdk:"region"`
+	TokenId     types.String `tfsdk:"token_id"`
+	Name        types.String `tfsdk:"name"`
+	Description types.String `tfsdk:"description"`
+	State       types.String `tfsdk:"state"`
+	ValidUntil  types.String `tfsdk:"validUntil"`
+	TTLDuration types.String `tfsdk:"ttlDuration"`
+	Content     types.String `tfsdk:"content"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_model_serving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.DnsCustomEndpoint != "" {
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: Is this correct? DnsCustomEndpoint? Should it not be a custom model-serving endpoint.
created_at: 2025-02-26T19:28:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,651 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/core/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id          types.String `tfsdk:"id"` // needed by TF
+	ProjectId   types.String `tfsdk:"project_id"`
+	Region      types.String `tfsdk:"region"`
+	TokenId     types.String `tfsdk:"token_id"`
+	Name        types.String `tfsdk:"name"`
+	Description types.String `tfsdk:"description"`
+	State       types.String `tfsdk:"state"`
+	ValidUntil  types.String `tfsdk:"validUntil"`
+	TTLDuration types.String `tfsdk:"ttlDuration"`
+	Content     types.String `tfsdk:"content"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_model_serving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.DnsCustomEndpoint != "" {
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: Got me. Copied from dns and forgot to adjust the value here. Adjusted it to the right value
created_at: 2025-02-27T06:32:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,656 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/core/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id          types.String `tfsdk:"id"` // needed by TF
+	ProjectId   types.String `tfsdk:"project_id"`
+	Region      types.String `tfsdk:"region"`
+	TokenId     types.String `tfsdk:"token_id"`
+	Name        types.String `tfsdk:"name"`
+	Description types.String `tfsdk:"description"`
+	State       types.String `tfsdk:"state"`
+	ValidUntil  types.String `tfsdk:"validUntil"`
+	TTLDuration types.String `tfsdk:"ttlDuration"`
+	Content     types.String `tfsdk:"content"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_model_serving_token"
path: stackit/internal/services/modelserving/token/resource.go
user: login: busilina
body: I'm not sure whether we should call it `_modelserving_token` (like, `_secretsmanager_` or `_objectstorage_`). I'm not aware whether there is a naming convention or not. Just wanted to point it out.
created_at: 2025-02-27T08:04:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,656 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/core/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id          types.String `tfsdk:"id"` // needed by TF
+	ProjectId   types.String `tfsdk:"project_id"`
+	Region      types.String `tfsdk:"region"`
+	TokenId     types.String `tfsdk:"token_id"`
+	Name        types.String `tfsdk:"name"`
+	Description types.String `tfsdk:"description"`
+	State       types.String `tfsdk:"state"`
+	ValidUntil  types.String `tfsdk:"validUntil"`
+	TTLDuration types.String `tfsdk:"ttlDuration"`
+	Content     types.String `tfsdk:"content"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_model_serving_token"
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: Thanks for pointing out. Renamed all _model_serving_ to _modelserving_
created_at: 2025-02-27T08:13:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,6 +25,7 @@ type ProviderData struct {
 	LogMeCustomEndpoint             string
 	MariaDBCustomEndpoint           string
 	MongoDBFlexCustomEndpoint       string
+	ModelServingCustomEndpoint      string
path: stackit/internal/core/core.go
user: login: rubenhoenle
body: You will have to add the custom endpoint to the

- [x] provider model https://github.com/stackitcloud/terraform-provider-stackit/blob/b5ce160d13eacdfb2068f4770b19756e85e7878c/stackit/provider.go#L114
- [x] schema https://github.com/stackitcloud/terraform-provider-stackit/blob/b5ce160d13eacdfb2068f4770b19756e85e7878c/stackit/provider.go#L149 and https://github.com/stackitcloud/terraform-provider-stackit/blob/b5ce160d13eacdfb2068f4770b19756e85e7878c/stackit/provider.go#L240-L243
- [x] and also read the configuration https://github.com/stackitcloud/terraform-provider-stackit/blob/b5ce160d13eacdfb2068f4770b19756e85e7878c/stackit/provider.go#L355-L357


created_at: 2025-02-27T10:00:14Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,237 @@
+package modelserving_test
path: stackit/internal/services/modelserving/model_serving_acc_test.go
user: login: rubenhoenle
body: Maybe rename this file to `modelserving_acc_test.go` to stay consistent.
created_at: 2025-02-27T10:12:27Z
author_association: MEMBER
diff_hunk: @@ -25,6 +25,7 @@ type ProviderData struct {
 	LogMeCustomEndpoint             string
 	MariaDBCustomEndpoint           string
 	MongoDBFlexCustomEndpoint       string
+	ModelServingCustomEndpoint      string
path: stackit/internal/core/core.go
user: login: PatrickKoss
body: Thanks for pointing out. I added the model serving stuff everywhere in the file.
created_at: 2025-02-27T10:23:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,237 @@
+package modelserving_test
path: stackit/internal/services/modelserving/model_serving_acc_test.go
user: login: PatrickKoss
body: renamed.
created_at: 2025-02-27T10:24:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+resource "stackit_modelserving_token" "example" {
+  project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+  region     = "eu01"
path: examples/resources/stackit_modelserving_token/resource.tf
user: login: rubenhoenle
body: - [x] As said in the chat, let's wait with the merge until we decided about how we want to implement the new multi-region concept in the Terraform provider.
created_at: 2025-02-27T15:31:10Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,656 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/core/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id          types.String `tfsdk:"id"` // needed by TF
+	ProjectId   types.String `tfsdk:"project_id"`
+	Region      types.String `tfsdk:"region"`
+	TokenId     types.String `tfsdk:"token_id"`
+	Name        types.String `tfsdk:"name"`
+	Description types.String `tfsdk:"description"`
+	State       types.String `tfsdk:"state"`
+	ValidUntil  types.String `tfsdk:"validUntil"`
+	TTLDuration types.String `tfsdk:"ttlDuration"`
+	Content     types.String `tfsdk:"content"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Computed:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"validUntil": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := r.activeTokenHandler(ctx, projectId, *createTokenResp.Token.Id, region).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	err = mapUpdateResponse(updateTokenResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	_, err = r.deleteTokenHandler(ctx, projectId, tokenId, region).WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Waiting for token to be deleted: %v", err),
+		)
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token deleted")
+}
+
+// ImportState imports a resource into the Terraform state on success.
+// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
+func (r *tokenResource) ImportState(
+	ctx context.Context,
+	req resource.ImportStateRequest,
+	resp *resource.ImportStateResponse,
+) {
+	idParts := strings.Split(req.ID, core.Separator)
+	if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error importing model serving auth token",
+			fmt.Sprintf(
+				"Expected import identifier with format [project_id],[token_id], got %q",
+				req.ID,
+			),
+		)
+		return
+	}
+
+	resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
+	resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("token_id"), idParts[1])...)
+
+	tflog.Info(ctx, "Model-Serving auth token state imported")
+}
+
+func mapCreateResponse(
+	tokenCreateResp *modelserving.CreateTokenResponse,
+	waitResp *modelserving.GetTokenResponse,
+	model *Model,
+) error {
+	if tokenCreateResp == nil || tokenCreateResp.Token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	token := tokenCreateResp.Token
+
+	if token.Id == nil {
+		return fmt.Errorf("token id not present")
+	}
+
+	validUntil := time.Now().Format(time.RFC3339)
+	if token.ValidUntil != nil {
+		validUntil = token.ValidUntil.Format(time.RFC3339)
+	}
+
+	if waitResp == nil || waitResp.Token == nil || waitResp.Token.State == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	idParts := []string{
+		model.ProjectId.ValueString(),
+		*tokenCreateResp.Token.Id,
+	}
+	model.Id = types.StringValue(
+		strings.Join(idParts, core.Separator),
+	)
+	model.TokenId = types.StringPointerValue(token.Id)
+	model.Name = types.StringPointerValue(token.Name)
+	model.Region = types.StringPointerValue(token.Region)
+	model.State = types.StringPointerValue(waitResp.Token.State)
+	model.ValidUntil = types.StringValue(validUntil)
+	model.Content = types.StringPointerValue(token.Content)
+	model.Description = types.StringPointerValue(token.Description)
+
+	return nil
+}
+
+func mapUpdateResponse(tokenUpdateResp *modelserving.UpdateTokenResponse, model *Model) error {
+	if tokenUpdateResp == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	return mapToken(tokenUpdateResp.Token, model)
+}
+
+func mapToken(token *modelserving.Token, model *Model) error {
+	if token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	// theoretically, should never happen, but still catch null pointers
+	validUntil := time.Now().Format(time.RFC3339)
+	if token.ValidUntil != nil {
+		validUntil = token.ValidUntil.Format(time.RFC3339)
+	}
+
+	idParts := []string{
+		model.ProjectId.ValueString(),
+		model.TokenId.ValueString(),
+	}
+	model.Id = types.StringValue(
+		strings.Join(idParts, core.Separator),
+	)
+	model.TokenId = types.StringPointerValue(token.Id)
+	model.Name = types.StringPointerValue(token.Name)
+	model.Region = types.StringPointerValue(token.Region)
+	model.State = types.StringPointerValue(token.State)
+	model.ValidUntil = types.StringValue(validUntil)
+	model.Description = types.StringPointerValue(token.Description)
+
+	return nil
+}
+
+func mapGetResponse(tokenGetResp *modelserving.GetTokenResponse, model *Model) error {
+	if tokenGetResp == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	return mapToken(tokenGetResp.Token, model)
+}
+
+func toCreatePayload(model *Model) (*modelserving.CreateTokenPayload, error) {
+	if model == nil {
+		return nil, fmt.Errorf("nil model")
+	}
+
+	return &modelserving.CreateTokenPayload{
+		Name:        conversion.StringValueToPointer(model.Name),
+		Description: conversion.StringValueToPointer(model.Description),
+		TtlDuration: conversion.StringValueToPointer(model.TTLDuration),
+	}, nil
+}
+
+func toUpdatePayload(model *Model) (*modelserving.PartialUpdateTokenPayload, error) {
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: These handlers should be part of the stackit-go-sdk. There is a wait package for each service which our tf provider leverages: e.g. https://github.com/stackitcloud/stackit-sdk-go/tree/main/services/iaas/wait. The wait packages are not automatically generated

created_at: 2025-03-07T08:58:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+resource "stackit_modelserving_token" "example" {
+  project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+  region     = "eu01"
path: examples/resources/stackit_modelserving_token/resource.tf
user: login: rubenhoenle
body: Decision was made, we will keep it as it is. 😊 
created_at: 2025-03-10T16:52:50Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: unused const
created_at: 2025-03-19T08:18:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: should we really throw an error here? I feel like a warning is more appropriate if a token has expired.
created_at: 2025-03-19T08:23:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+	region := state.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: same as above
created_at: 2025-03-19T08:25:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: what if the response is 404? We have to remove it from the state and recreate. E.g. someone deletes the token manually in the portal
created_at: 2025-03-19T08:27:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: currently just errors
created_at: 2025-03-19T08:28:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: Ig if the region changes the token has to be recreated?
created_at: 2025-03-19T08:30:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,224 @@
+package token
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ datasource.DataSource = &tokenDataSource{}
+)
+
+// NewTokenDataSource is a helper function to simplify the provider implementation.
+func NewTokenDataSource() datasource.DataSource {
+	return &tokenDataSource{}
+}
+
+// tokenDataSource is the data source implementation.
+type tokenDataSource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the data source type name.
+func (d *tokenDataSource) Metadata(
+	_ context.Context,
+	req datasource.MetadataRequest,
+	resp *datasource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *tokenDataSource) Configure(
+	ctx context.Context,
+	req datasource.ConfigureRequest,
+	resp *datasource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	d.client = apiClient
+
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the data source.
+func (d *tokenDataSource) Schema(
+	_ context.Context,
+	_ datasource.SchemaRequest,
+	resp *datasource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token datasource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
path: stackit/internal/services/modelserving/token/datasource.go
user: login: rubenhoenle
body: ```suggestion
				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
```
created_at: 2025-03-20T12:25:40Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
path: stackit/internal/services/modelserving/token/resource.go
user: login: rubenhoenle
body: ```suggestion
				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used."",
```
created_at: 2025-03-20T12:31:44Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
path: stackit/internal/services/modelserving/token/resource.go
user: login: rubenhoenle
body: ```suggestion
	var region string
	if utils.IsUndefined(model.Region) {
		region = r.providerData.GetRegion()
	} else {
		region = model.Region.ValueString()
	}
```

The region of the API client (from the SDK) will be removed long-term due to the implementation of the multi-region concept. So please get the default value from the provider config instead.
created_at: 2025-03-20T12:40:21Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
path: stackit/internal/services/modelserving/token/resource.go
user: login: rubenhoenle
body: Referenced utils pkg: `github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils`
created_at: 2025-03-20T12:41:33Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+	region := state.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
path: stackit/internal/services/modelserving/token/resource.go
user: login: rubenhoenle
body: ```suggestion
var region string
	if utils.IsUndefined(state.Region) {
		region = r.providerData.GetRegion()
	} else {
		region = state.Region.ValueString()
	}
```

same here
created_at: 2025-03-20T12:42:51Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+	region := state.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Waiting for token to be updated: %v", err),
+		)
+		return
+	}
+
+	err = mapGetResponse(waitResp, &model, &state)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
path: stackit/internal/services/modelserving/token/resource.go
user: login: rubenhoenle
body: ```suggestion
var region string
	if utils.IsUndefined(model.Region) {
		region = r.providerData.GetRegion()
	} else {
		region = model.Region.ValueString()
	}
```
same here
created_at: 2025-03-20T12:43:40Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,224 @@
+package token
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ datasource.DataSource = &tokenDataSource{}
+)
+
+// NewTokenDataSource is a helper function to simplify the provider implementation.
+func NewTokenDataSource() datasource.DataSource {
+	return &tokenDataSource{}
+}
+
+// tokenDataSource is the data source implementation.
+type tokenDataSource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the data source type name.
+func (d *tokenDataSource) Metadata(
+	_ context.Context,
+	req datasource.MetadataRequest,
+	resp *datasource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *tokenDataSource) Configure(
+	ctx context.Context,
+	req datasource.ConfigureRequest,
+	resp *datasource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	d.client = apiClient
+
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the data source.
+func (d *tokenDataSource) Schema(
+	_ context.Context,
+	_ datasource.SchemaRequest,
+	resp *datasource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token datasource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Optional:    true,
+				Computed:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Computed:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (d *tokenDataSource) Read(
+	ctx context.Context,
+	req datasource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *datasource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+
+	diags := req.Config.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = d.client.GetConfig().Region
+	}
path: stackit/internal/services/modelserving/token/datasource.go
user: login: rubenhoenle
body: ```suggestion
	var region string
	if utils.IsUndefined(model.Region) {
		region = r.providerData.GetRegion()
	} else {
		region = model.Region.ValueString()
	}
```

same here
created_at: 2025-03-20T12:44:05Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
path: stackit/internal/services/modelserving/token/resource.go
user: login: rubenhoenle
body: ```suggestion
var region string
	if utils.IsUndefined(model.Region) {
		region = r.providerData.GetRegion()
	} else {
		region = model.Region.ValueString()
	}
```

same here
created_at: 2025-03-20T12:44:39Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,348 @@
+package token
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+)
+
+func TestMapGetTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		state       *Model
+		input       *modelserving.GetTokenResponse
+		expected    Model
+		isValid     bool
+	}{
+		{
+			description: "should error when response is nil",
+			state:       &Model{},
+			input:       nil,
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when token is nil in response",
+			state:       &Model{},
+			input:       &modelserving.GetTokenResponse{Token: nil},
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when state is nil in response",
+			state:       nil,
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{},
+			},
+			expected: Model{},
+			isValid:  false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				TokenId:           types.StringValue("tid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapGetResponse(tt.input, tt.state, tt.state)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestMapCreateTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description              string
+		state                    *Model
+		inputCreateTokenResponse *modelserving.CreateTokenResponse
+		inputGetTokenResponse    *modelserving.GetTokenResponse
+		expected                 Model
+		isValid                  bool
+	}{
+		{
+			description:              "should error when create token response is nil",
+			state:                    &Model{},
+			inputCreateTokenResponse: nil,
+			inputGetTokenResponse:    nil,
+			expected:                 Model{},
+			isValid:                  false,
+		},
+		{
+			description: "should error when token is nil in create token response",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: nil,
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state: &Model{
+				Id:        types.StringValue("pid,tid"),
+				ProjectId: types.StringValue("pid"),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					State: utils.Ptr("active"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				Content:           types.StringValue("content"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapCreateResponse(
+				tt.inputCreateTokenResponse,
+				tt.inputGetTokenResponse,
+				tt.state,
+			)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToCreatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.CreateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+				TTLDuration: types.StringValue("1h"),
+			},
+			expected: &modelserving.CreateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+				TtlDuration: utils.Ptr("1h"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := toCreatePayload(tt.input)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(output, tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToUpdatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.PartialUpdateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+			},
+			expected: &modelserving.PartialUpdateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
path: stackit/internal/services/modelserving/token/resource_test.go
user: login: rubenhoenle
body: ```suggestion
```
created_at: 2025-03-20T12:52:08Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,348 @@
+package token
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+)
+
+func TestMapGetTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		state       *Model
+		input       *modelserving.GetTokenResponse
+		expected    Model
+		isValid     bool
+	}{
+		{
+			description: "should error when response is nil",
+			state:       &Model{},
+			input:       nil,
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when token is nil in response",
+			state:       &Model{},
+			input:       &modelserving.GetTokenResponse{Token: nil},
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when state is nil in response",
+			state:       nil,
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{},
+			},
+			expected: Model{},
+			isValid:  false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				TokenId:           types.StringValue("tid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
path: stackit/internal/services/modelserving/token/resource_test.go
user: login: rubenhoenle
body: ```suggestion
```
created_at: 2025-03-20T12:52:24Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,348 @@
+package token
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+)
+
+func TestMapGetTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		state       *Model
+		input       *modelserving.GetTokenResponse
+		expected    Model
+		isValid     bool
+	}{
+		{
+			description: "should error when response is nil",
+			state:       &Model{},
+			input:       nil,
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when token is nil in response",
+			state:       &Model{},
+			input:       &modelserving.GetTokenResponse{Token: nil},
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when state is nil in response",
+			state:       nil,
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{},
+			},
+			expected: Model{},
+			isValid:  false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				TokenId:           types.StringValue("tid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapGetResponse(tt.input, tt.state, tt.state)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestMapCreateTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description              string
+		state                    *Model
+		inputCreateTokenResponse *modelserving.CreateTokenResponse
+		inputGetTokenResponse    *modelserving.GetTokenResponse
+		expected                 Model
+		isValid                  bool
+	}{
+		{
+			description:              "should error when create token response is nil",
+			state:                    &Model{},
+			inputCreateTokenResponse: nil,
+			inputGetTokenResponse:    nil,
+			expected:                 Model{},
+			isValid:                  false,
+		},
+		{
+			description: "should error when token is nil in create token response",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: nil,
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state: &Model{
+				Id:        types.StringValue("pid,tid"),
+				ProjectId: types.StringValue("pid"),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					State: utils.Ptr("active"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				Content:           types.StringValue("content"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
path: stackit/internal/services/modelserving/token/resource_test.go
user: login: rubenhoenle
body: ```suggestion
```
created_at: 2025-03-20T12:52:35Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,348 @@
+package token
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+)
+
+func TestMapGetTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		state       *Model
+		input       *modelserving.GetTokenResponse
+		expected    Model
+		isValid     bool
+	}{
+		{
+			description: "should error when response is nil",
+			state:       &Model{},
+			input:       nil,
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when token is nil in response",
+			state:       &Model{},
+			input:       &modelserving.GetTokenResponse{Token: nil},
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when state is nil in response",
+			state:       nil,
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{},
+			},
+			expected: Model{},
+			isValid:  false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				TokenId:           types.StringValue("tid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapGetResponse(tt.input, tt.state, tt.state)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestMapCreateTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description              string
+		state                    *Model
+		inputCreateTokenResponse *modelserving.CreateTokenResponse
+		inputGetTokenResponse    *modelserving.GetTokenResponse
+		expected                 Model
+		isValid                  bool
+	}{
+		{
+			description:              "should error when create token response is nil",
+			state:                    &Model{},
+			inputCreateTokenResponse: nil,
+			inputGetTokenResponse:    nil,
+			expected:                 Model{},
+			isValid:                  false,
+		},
+		{
+			description: "should error when token is nil in create token response",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: nil,
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state: &Model{
+				Id:        types.StringValue("pid,tid"),
+				ProjectId: types.StringValue("pid"),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					State: utils.Ptr("active"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				Content:           types.StringValue("content"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapCreateResponse(
+				tt.inputCreateTokenResponse,
+				tt.inputGetTokenResponse,
+				tt.state,
+			)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToCreatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.CreateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+				TTLDuration: types.StringValue("1h"),
+			},
+			expected: &modelserving.CreateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+				TtlDuration: utils.Ptr("1h"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
path: stackit/internal/services/modelserving/token/resource_test.go
user: login: rubenhoenle
body: ```suggestion
```
created_at: 2025-03-20T12:52:46Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+	region := state.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: added a warning and remove resource from tf state which is now recreating the inactive token. Altough not deleting the inactive token
created_at: 2025-03-20T14:51:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "STACKIT region to which the model serving auth token is associated.",
+				Required:    false,
+				Optional:    true,
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+	region := model.Region.ValueString()
+	if region == "" {
+		region = r.client.GetConfig().Region
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: added a recreate 
created_at: 2025-03-20T14:51:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,665 @@
+package token
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+	activeState   = "active"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client *modelserving.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: yes makes sense to recreate if the region changes since the token is region bound
created_at: 2025-03-20T14:51:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,348 @@
+package token
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/stackitcloud/stackit-sdk-go/core/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+)
+
+func TestMapGetTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		state       *Model
+		input       *modelserving.GetTokenResponse
+		expected    Model
+		isValid     bool
+	}{
+		{
+			description: "should error when response is nil",
+			state:       &Model{},
+			input:       nil,
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when token is nil in response",
+			state:       &Model{},
+			input:       &modelserving.GetTokenResponse{Token: nil},
+			expected:    Model{},
+			isValid:     false,
+		},
+		{
+			description: "should error when state is nil in response",
+			state:       nil,
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{},
+			},
+			expected: Model{},
+			isValid:  false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				TokenId:           types.StringValue("tid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			input: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapGetResponse(tt.input, tt.state, tt.state)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestMapCreateTokenFields(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description              string
+		state                    *Model
+		inputCreateTokenResponse *modelserving.CreateTokenResponse
+		inputGetTokenResponse    *modelserving.GetTokenResponse
+		expected                 Model
+		isValid                  bool
+	}{
+		{
+			description:              "should error when create token response is nil",
+			state:                    &Model{},
+			inputCreateTokenResponse: nil,
+			inputGetTokenResponse:    nil,
+			expected:                 Model{},
+			isValid:                  false,
+		},
+		{
+			description: "should error when token is nil in create token response",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: nil,
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state:       &Model{},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should error when get token response is nil",
+			state: &Model{
+				Id:        types.StringValue("pid,tid"),
+				ProjectId: types.StringValue("pid"),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: nil,
+			expected:              Model{},
+			isValid:               false,
+		},
+		{
+			description: "should map fields correctly",
+			state: &Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			inputCreateTokenResponse: &modelserving.CreateTokenResponse{
+				Token: &modelserving.TokenCreated{
+					Id: utils.Ptr("tid"),
+					ValidUntil: utils.Ptr(
+						time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC),
+					),
+					State:       utils.Ptr("active"),
+					Name:        utils.Ptr("name"),
+					Description: utils.Ptr("desc"),
+					Region:      utils.Ptr("eu01"),
+					Content:     utils.Ptr("content"),
+				},
+			},
+			inputGetTokenResponse: &modelserving.GetTokenResponse{
+				Token: &modelserving.Token{
+					State: utils.Ptr("active"),
+				},
+			},
+			expected: Model{
+				Id:                types.StringValue("pid,tid"),
+				ProjectId:         types.StringValue("pid"),
+				Region:            types.StringValue("eu01"),
+				TokenId:           types.StringValue("tid"),
+				Name:              types.StringValue("name"),
+				Description:       types.StringValue("desc"),
+				State:             types.StringValue("active"),
+				ValidUntil:        types.StringValue("2099-01-01T00:00:00Z"),
+				Content:           types.StringValue("content"),
+				RotateWhenChanged: types.MapNull(types.StringType),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			err := mapCreateResponse(
+				tt.inputCreateTokenResponse,
+				tt.inputGetTokenResponse,
+				tt.state,
+			)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(tt.state, &tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToCreatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.CreateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+				TTLDuration: types.StringValue("1h"),
+			},
+			expected: &modelserving.CreateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+				TtlDuration: utils.Ptr("1h"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+
+		t.Run(tt.description, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := toCreatePayload(tt.input)
+			if !tt.isValid && err == nil {
+				t.Fatalf("Should have failed")
+			}
+
+			if tt.isValid && err != nil {
+				t.Fatalf("Should not have failed: %v", err)
+			}
+
+			if tt.isValid {
+				diff := cmp.Diff(output, tt.expected)
+				if diff != "" {
+					t.Fatalf("Data does not match: %s", diff)
+				}
+			}
+		})
+	}
+}
+
+func TestToUpdatePayload(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		description string
+		input       *Model
+		expected    *modelserving.PartialUpdateTokenPayload
+		isValid     bool
+	}{
+		{
+			description: "should error on nil input",
+			input:       nil,
+			expected:    nil,
+			isValid:     false,
+		},
+		{
+			description: "should convert correctly",
+			input: &Model{
+				Name:        types.StringValue("name"),
+				Description: types.StringValue("desc"),
+			},
+			expected: &modelserving.PartialUpdateTokenPayload{
+				Name:        utils.Ptr("name"),
+				Description: utils.Ptr("desc"),
+			},
+			isValid: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
path: stackit/internal/services/modelserving/token/resource_test.go
user: login: PatrickKoss
body: usually shadows something and some linters complain about it :D 
created_at: 2025-03-20T15:06:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,738 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client       *modelserving.APIClient
+	providerData core.ProviderData
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: it is computed, so plan-modifier will never be triggered.
created_at: 2025-03-20T15:25:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,738 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client       *modelserving.APIClient
+	providerData core.ProviderData
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				core.LogAndAddWarning(
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: we usually don't print a warning in this case. The user will just see on the plan that a token recreation is triggered
created_at: 2025-03-20T15:27:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,738 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client       *modelserving.APIClient
+	providerData core.ProviderData
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				core.LogAndAddWarning(
+					ctx,
+					&resp.Diagnostics,
+					"Error reading model serving auth token",
+					"Model serving auth token not found",
+				)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				core.LogAndAddWarning(
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: same here. Usually no warning
created_at: 2025-03-20T15:28:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,738 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client       *modelserving.APIClient
+	providerData core.ProviderData
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				core.LogAndAddWarning(
+					ctx,
+					&resp.Diagnostics,
+					"Error reading model serving auth token",
+					"Model serving auth token not found",
+				)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				core.LogAndAddWarning(
+					ctx,
+					&resp.Diagnostics,
+					"Error updating model serving auth token",
+					"Model serving auth token not found",
+				)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Waiting for token to be updated: %v", err),
+		)
+		return
+	}
+
+	err = mapGetResponse(waitResp, &model, &state)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				core.LogAndAddWarning(
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: same here
created_at: 2025-03-20T15:28:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,6 +18,7 @@ require (
 	github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v0.18.0
 	github.com/stackitcloud/stackit-sdk-go/services/logme v0.21.0
 	github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.21.0
+	github.com/stackitcloud/stackit-sdk-go/services/modelserving v0.2.0
path: go.mod
user: login: bahkauv70
body: Since creation of the PR the version 0.2.1 has been released, so we need to upgrade the dependency
created_at: 2025-03-24T13:42:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: To handle the regions correctly, the resource must also implement the ResourceWithModifyPlan interface with the following implementation:
```go
// ModifyPlan implements resource.ResourceWithModifyPlan.
// Use the modifier to set the effective region in the current plan.
func (r *credentialsGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
	var configModel Model
	// skip initial empty configuration to avoid follow-up errors
	if req.Config.Raw.IsNull() {
		return
	}
	resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
	if resp.Diagnostics.HasError() {
		return
	}

	var planModel Model
	resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
	if resp.Diagnostics.HasError() {
		return
	}

	coreutils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
	if resp.Diagnostics.HasError() {
		return
	}

	resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
	if resp.Diagnostics.HasError() {
		return
	}
}

```

(see e.g. https://github.com/stackitcloud/terraform-provider-stackit/blob/1444376f351aa9909a5c759c6b2a5abe0754b719/stackit/internal/services/postgresflex/instance/resource.go#L97 for comparision)
created_at: 2025-03-24T13:58:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: withregion call is now deprecated
created_at: 2025-03-24T13:58:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: withregion call is now deprecated call is now deprecated
created_at: 2025-03-24T13:59:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	var serviceEnablementClient *serviceenablement.APIClient
+	if providerData.ServiceEnablementCustomEndpoint != "" {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ServiceEnablementCustomEndpoint),
+		)
+	} else {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: withregion call is now deprecated call is now deprecated
created_at: 2025-03-24T13:59:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: rotatewhenchanged attribute is not defined in the API and it doesn't seem to be used. Can it be removed?
created_at: 2025-03-24T14:02:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	var serviceEnablementClient *serviceenablement.APIClient
+	if providerData.ServiceEnablementCustomEndpoint != "" {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ServiceEnablementCustomEndpoint),
+		)
+	} else {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring service enablement client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	r.serviceEnablementClient = serviceEnablementClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// If model serving is not enabled, enable it
+	err := r.serviceEnablementClient.EnableService(ctx, projectId, utils.ModelServingServiceId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	_, err = serviceEnablementWait.EnableServiceWaitHandler(ctx, r.serviceEnablementClient, projectId, utils.ModelServingServiceId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Waiting for token to be updated: %v", err),
+		)
+		return
+	}
+
+	err = mapGetResponse(waitResp, &model, &state)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	_, err = wait.DeleteModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Waiting for token to be deleted: %v", err),
+		)
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token deleted")
+}
+
+// ImportState imports a resource into the Terraform state on success.
+// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
+func (r *tokenResource) ImportState(
+	ctx context.Context,
+	req resource.ImportStateRequest,
+	resp *resource.ImportStateResponse,
+) {
+	idParts := strings.Split(req.ID, core.Separator)
+	if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error importing model serving auth token",
+			fmt.Sprintf(
+				"Expected import identifier with format [project_id],[token_id], got %q",
+				req.ID,
+			),
+		)
+		return
+	}
+
+	resp.Diagnostics.Append(
+		resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
+	resp.Diagnostics.Append(
+		resp.State.SetAttribute(ctx, path.Root("token_id"), idParts[1])...)
+
+	tflog.Info(ctx, "Model-Serving auth token state imported")
+}
+
+func mapCreateResponse(
+	tokenCreateResp *modelserving.CreateTokenResponse,
+	waitResp *modelserving.GetTokenResponse,
+	model *Model,
+) error {
+	if tokenCreateResp == nil || tokenCreateResp.Token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	token := tokenCreateResp.Token
+
+	if token.Id == nil {
+		return fmt.Errorf("token id not present")
+	}
+
+	validUntil := time.Now().Format(time.RFC3339)
+	if token.ValidUntil != nil {
+		validUntil = token.ValidUntil.Format(time.RFC3339)
+	}
+
+	if waitResp == nil || waitResp.Token == nil || waitResp.Token.State == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	idParts := []string{
+		model.ProjectId.ValueString(),
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: With the changes to the region handling, the region identifier should now be part of the ID as well, see [postgresflex](https://github.com/stackitcloud/terraform-provider-stackit/blob/1444376f351aa9909a5c759c6b2a5abe0754b719/stackit/internal/services/postgresflex/instance/resource.go#L638) for comparision
created_at: 2025-03-24T14:05:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	var serviceEnablementClient *serviceenablement.APIClient
+	if providerData.ServiceEnablementCustomEndpoint != "" {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ServiceEnablementCustomEndpoint),
+		)
+	} else {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring service enablement client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	r.serviceEnablementClient = serviceEnablementClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// If model serving is not enabled, enable it
+	err := r.serviceEnablementClient.EnableService(ctx, projectId, utils.ModelServingServiceId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	_, err = serviceEnablementWait.EnableServiceWaitHandler(ctx, r.serviceEnablementClient, projectId, utils.ModelServingServiceId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Waiting for token to be updated: %v", err),
+		)
+		return
+	}
+
+	err = mapGetResponse(waitResp, &model, &state)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	_, err = wait.DeleteModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Waiting for token to be deleted: %v", err),
+		)
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token deleted")
+}
+
+// ImportState imports a resource into the Terraform state on success.
+// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
+func (r *tokenResource) ImportState(
+	ctx context.Context,
+	req resource.ImportStateRequest,
+	resp *resource.ImportStateResponse,
+) {
+	idParts := strings.Split(req.ID, core.Separator)
+	if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error importing model serving auth token",
+			fmt.Sprintf(
+				"Expected import identifier with format [project_id],[token_id], got %q",
+				req.ID,
+			),
+		)
+		return
+	}
+
+	resp.Diagnostics.Append(
+		resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
+	resp.Diagnostics.Append(
+		resp.State.SetAttribute(ctx, path.Root("token_id"), idParts[1])...)
+
+	tflog.Info(ctx, "Model-Serving auth token state imported")
+}
+
+func mapCreateResponse(
+	tokenCreateResp *modelserving.CreateTokenResponse,
+	waitResp *modelserving.GetTokenResponse,
+	model *Model,
+) error {
+	if tokenCreateResp == nil || tokenCreateResp.Token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	token := tokenCreateResp.Token
+
+	if token.Id == nil {
+		return fmt.Errorf("token id not present")
+	}
+
+	validUntil := time.Now().Format(time.RFC3339)
+	if token.ValidUntil != nil {
+		validUntil = token.ValidUntil.Format(time.RFC3339)
+	}
+
+	if waitResp == nil || waitResp.Token == nil || waitResp.Token.State == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	idParts := []string{
+		model.ProjectId.ValueString(),
+		*tokenCreateResp.Token.Id,
+	}
+	model.Id = types.StringValue(
+		strings.Join(idParts, core.Separator),
+	)
+	model.TokenId = types.StringPointerValue(token.Id)
+	model.Name = types.StringPointerValue(token.Name)
+	model.State = types.StringPointerValue(waitResp.Token.State)
+	model.ValidUntil = types.StringValue(validUntil)
+	model.Content = types.StringPointerValue(token.Content)
+	model.Description = types.StringPointerValue(token.Description)
+
+	return nil
+}
+
+func mapToken(token *modelserving.Token, model, state *Model) error {
+	if token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+	if state == nil {
+		return fmt.Errorf("state input is nil")
+	}
+
+	// theoretically, should never happen, but still catch null pointers
+	validUntil := time.Now().Format(time.RFC3339)
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: What is the correct default value, if the token does not define a validUntil attribute? Is it really 'now' or should it be undefined, i.e. types.StringNull?
created_at: 2025-03-24T14:12:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	var serviceEnablementClient *serviceenablement.APIClient
+	if providerData.ServiceEnablementCustomEndpoint != "" {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ServiceEnablementCustomEndpoint),
+		)
+	} else {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring service enablement client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	r.serviceEnablementClient = serviceEnablementClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// If model serving is not enabled, enable it
+	err := r.serviceEnablementClient.EnableService(ctx, projectId, utils.ModelServingServiceId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	_, err = serviceEnablementWait.EnableServiceWaitHandler(ctx, r.serviceEnablementClient, projectId, utils.ModelServingServiceId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Waiting for token to be updated: %v", err),
+		)
+		return
+	}
+
+	err = mapGetResponse(waitResp, &model, &state)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	_, err = wait.DeleteModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Waiting for token to be deleted: %v", err),
+		)
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token deleted")
+}
+
+// ImportState imports a resource into the Terraform state on success.
+// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
+func (r *tokenResource) ImportState(
+	ctx context.Context,
+	req resource.ImportStateRequest,
+	resp *resource.ImportStateResponse,
+) {
+	idParts := strings.Split(req.ID, core.Separator)
+	if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error importing model serving auth token",
+			fmt.Sprintf(
+				"Expected import identifier with format [project_id],[token_id], got %q",
+				req.ID,
+			),
+		)
+		return
+	}
+
+	resp.Diagnostics.Append(
+		resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
+	resp.Diagnostics.Append(
+		resp.State.SetAttribute(ctx, path.Root("token_id"), idParts[1])...)
+
+	tflog.Info(ctx, "Model-Serving auth token state imported")
+}
+
+func mapCreateResponse(
+	tokenCreateResp *modelserving.CreateTokenResponse,
+	waitResp *modelserving.GetTokenResponse,
+	model *Model,
+) error {
+	if tokenCreateResp == nil || tokenCreateResp.Token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+
+	token := tokenCreateResp.Token
+
+	if token.Id == nil {
+		return fmt.Errorf("token id not present")
+	}
+
+	validUntil := time.Now().Format(time.RFC3339)
+	if token.ValidUntil != nil {
+		validUntil = token.ValidUntil.Format(time.RFC3339)
+	}
+
+	if waitResp == nil || waitResp.Token == nil || waitResp.Token.State == nil {
+		return fmt.Errorf("response input is nil")
+	}
+
+	idParts := []string{
+		model.ProjectId.ValueString(),
+		*tokenCreateResp.Token.Id,
+	}
+	model.Id = types.StringValue(
+		strings.Join(idParts, core.Separator),
+	)
+	model.TokenId = types.StringPointerValue(token.Id)
+	model.Name = types.StringPointerValue(token.Name)
+	model.State = types.StringPointerValue(waitResp.Token.State)
+	model.ValidUntil = types.StringValue(validUntil)
+	model.Content = types.StringPointerValue(token.Content)
+	model.Description = types.StringPointerValue(token.Description)
+
+	return nil
+}
+
+func mapToken(token *modelserving.Token, model, state *Model) error {
+	if token == nil {
+		return fmt.Errorf("response input is nil")
+	}
+	if model == nil {
+		return fmt.Errorf("model input is nil")
+	}
+	if state == nil {
+		return fmt.Errorf("state input is nil")
+	}
+
+	// theoretically, should never happen, but still catch null pointers
+	validUntil := time.Now().Format(time.RFC3339)
+	if token.ValidUntil != nil {
+		validUntil = token.ValidUntil.Format(time.RFC3339)
+	}
+
+	idParts := []string{
+		model.ProjectId.ValueString(),
+		model.TokenId.ValueString(),
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: see above, the region should be part of the id
created_at: 2025-03-24T14:12:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
+}
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+	return &tokenResource{}
+}
+
+// tokenResource is the resource implementation.
+type tokenResource struct {
+	client                  *modelserving.APIClient
+	providerData            core.ProviderData
+	serviceEnablementClient *serviceenablement.APIClient
+}
+
+// Metadata returns the resource type name.
+func (r *tokenResource) Metadata(
+	_ context.Context,
+	req resource.MetadataRequest,
+	resp *resource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *tokenResource) Configure(
+	ctx context.Context,
+	req resource.ConfigureRequest,
+	resp *resource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		ctx = tflog.SetField(
+			ctx,
+			"modelserving_custom_endpoint",
+			providerData.ModelServingCustomEndpoint,
+		)
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	} else {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	var serviceEnablementClient *serviceenablement.APIClient
+	if providerData.ServiceEnablementCustomEndpoint != "" {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ServiceEnablementCustomEndpoint),
+		)
+	} else {
+		serviceEnablementClient, err = serviceenablement.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithRegion(providerData.GetRegion()),
+		)
+	}
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring service enablement client",
+			fmt.Sprintf(
+				"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
+				err,
+			),
+		)
+		return
+	}
+
+	r.client = apiClient
+	r.providerData = providerData
+	r.serviceEnablementClient = serviceEnablementClient
+	tflog.Info(ctx, "Model-Serving auth token client configured")
+}
+
+// Schema defines the schema for the resource.
+func (r *tokenResource) Schema(
+	_ context.Context,
+	_ resource.SchemaRequest,
+	resp *resource.SchemaResponse,
+) {
+	resp.Schema = schema.Schema{
+		Description: "Model Serving Auth Token Resource schema.",
+		Attributes: map[string]schema.Attribute{
+			"id": schema.StringAttribute{
+				Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`token_id`\".",
+				Computed:    true,
+			},
+			"project_id": schema.StringAttribute{
+				Description: "STACKIT project ID to which the model serving auth token is associated.",
+				Required:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"region": schema.StringAttribute{
+				Description: "Region to which the model serving auth token is associated. If not defined, the provider region is used.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"token_id": schema.StringAttribute{
+				Description: "The model serving auth token ID.",
+				Computed:    true,
+				Validators: []validator.String{
+					validate.UUID(),
+					validate.NoSeparator(),
+				},
+			},
+			"ttl_duration": schema.StringAttribute{
+				Description: "The TTL duration of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.RequiresReplace(),
+				},
+			},
+			"rotate_when_changed": schema.MapAttribute{
+				Description: "A map of arbitrary key/value pairs that will force " +
+					"recreation of the token when they change, enabling token rotation " +
+					"based on external conditions such as a rotating timestamp. Changing " +
+					"this forces a new resource to be created.",
+				Optional:    true,
+				Required:    false,
+				ElementType: types.StringType,
+				PlanModifiers: []planmodifier.Map{
+					mapplanmodifier.RequiresReplace(),
+				},
+			},
+			"description": schema.StringAttribute{
+				Description: "The description of the model serving auth token.",
+				Required:    false,
+				Optional:    true,
+			},
+			"name": schema.StringAttribute{
+				Description: "Name of the model serving auth token.",
+				Required:    true,
+			},
+			"state": schema.StringAttribute{
+				Description: "State of the model serving auth token.",
+				Computed:    true,
+			},
+			"content": schema.StringAttribute{
+				Description: "Content of the model serving auth token.",
+				Computed:    true,
+			},
+			"valid_until": schema.StringAttribute{
+				Description: "The time until the model serving auth token is valid.",
+				Computed:    true,
+			},
+		},
+	}
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *tokenResource) Create(
+	ctx context.Context,
+	req resource.CreateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.CreateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// If model serving is not enabled, enable it
+	err := r.serviceEnablementClient.EnableService(ctx, projectId, utils.ModelServingServiceId).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	_, err = serviceEnablementWait.EnableServiceWaitHandler(ctx, r.serviceEnablementClient, projectId, utils.ModelServingServiceId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error enabling model serving",
+			fmt.Sprintf("Error enabling model serving: %v", err),
+		)
+		return
+	}
+
+	// Generate API request body from model
+	payload, err := toCreatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Create new model serving auth token
+	createTokenResp, err := r.client.CreateToken(ctx, region, projectId).
+		CreateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	waitResp, err := wait.CreateModelServingWaitHandler(ctx, r.client, region, projectId, *createTokenResp.Token.Id).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Waiting for token to be active: %v", err),
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapCreateResponse(createTokenResp, waitResp, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error creating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set state to fully populated data
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token created")
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *tokenResource) Read(
+	ctx context.Context,
+	req resource.ReadRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.ReadResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	getTokenResp, err := r.client.GetToken(ctx, region, projectId, tokenId).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	if getTokenResp != nil && getTokenResp.Token.State != nil &&
+		*getTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	// Map response body to schema
+	err = mapGetResponse(getTokenResp, &model, &model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error reading model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	// Set refreshed state
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token read")
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *tokenResource) Update(
+	ctx context.Context,
+	req resource.UpdateRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.UpdateResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.Plan.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Get current state
+	var state Model
+	diags = req.State.Get(ctx, &state)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := state.ProjectId.ValueString()
+	tokenId := state.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Generate API request body from model
+	payload, err := toUpdatePayload(&model)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Creating API payload: %v", err),
+		)
+		return
+	}
+
+	// Update model serving auth token
+	updateTokenResp, err := r.client.PartialUpdateToken(ctx, region, projectId, tokenId).
+		PartialUpdateTokenPayload(*payload).
+		Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				// Remove the resource from the state so Terraform will recreate it
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf(
+				"Calling API: %v, tokenId: %s, region: %s, projectId: %s",
+				err,
+				tokenId,
+				region,
+				projectId,
+			),
+		)
+		return
+	}
+
+	if updateTokenResp != nil && updateTokenResp.Token.State != nil &&
+		*updateTokenResp.Token.State == inactiveState {
+		resp.State.RemoveResource(ctx)
+		core.LogAndAddWarning(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			"Model serving auth token has expired",
+		)
+		return
+	}
+
+	waitResp, err := wait.UpdateModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Waiting for token to be updated: %v", err),
+		)
+		return
+	}
+
+	err = mapGetResponse(waitResp, &model, &state)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error updating model serving auth token",
+			fmt.Sprintf("Processing API payload: %v", err),
+		)
+		return
+	}
+
+	diags = resp.State.Set(ctx, model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token updated")
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *tokenResource) Delete(
+	ctx context.Context,
+	req resource.DeleteRequest, //nolint:gocritic // function signature required by Terraform
+	resp *resource.DeleteResponse,
+) { // nolint:gocritic // function signature required by Terraform
+	// Retrieve values from plan
+	var model Model
+	diags := req.State.Get(ctx, &model)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	projectId := model.ProjectId.ValueString()
+	tokenId := model.TokenId.ValueString()
+
+	var region string
+	if utils.IsUndefined(model.Region) {
+		region = r.providerData.GetRegion()
+	} else {
+		region = model.Region.ValueString()
+	}
+
+	ctx = tflog.SetField(ctx, "project_id", projectId)
+	ctx = tflog.SetField(ctx, "token_id", tokenId)
+	ctx = tflog.SetField(ctx, "region", region)
+
+	// Delete existing model serving auth token. We will ignore the state 'deleting' for now.
+	_, err := r.client.DeleteToken(ctx, region, projectId, tokenId).Execute()
+	if err != nil {
+		var oapiErr *oapierror.GenericOpenAPIError
+		if errors.As(err, &oapiErr) {
+			if oapiErr.StatusCode == http.StatusNotFound {
+				resp.State.RemoveResource(ctx)
+				return
+			}
+		}
+
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Calling API: %v", err),
+		)
+		return
+	}
+
+	_, err = wait.DeleteModelServingWaitHandler(ctx, r.client, region, projectId, tokenId).
+		WaitWithContext(ctx)
+	if err != nil {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error deleting model serving auth token",
+			fmt.Sprintf("Waiting for token to be deleted: %v", err),
+		)
+		return
+	}
+
+	tflog.Info(ctx, "Model-Serving auth token deleted")
+}
+
+// ImportState imports a resource into the Terraform state on success.
+// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
+func (r *tokenResource) ImportState(
+	ctx context.Context,
+	req resource.ImportStateRequest,
+	resp *resource.ImportStateResponse,
+) {
+	idParts := strings.Split(req.ID, core.Separator)
+	if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: the id should contain the region as well, see https://github.com/stackitcloud/terraform-provider-stackit/blob/1444376f351aa9909a5c759c6b2a5abe0754b719/stackit/internal/services/postgresflex/instance/resource.go#L541 for comparision
created_at: 2025-03-24T14:13:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,229 @@
+package token
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ datasource.DataSource = &tokenDataSource{}
+)
+
+// NewTokenDataSource is a helper function to simplify the provider implementation.
+func NewTokenDataSource() datasource.DataSource {
+	return &tokenDataSource{}
+}
+
+// tokenDataSource is the data source implementation.
+type tokenDataSource struct {
+	client       *modelserving.APIClient
+	providerData core.ProviderData
+}
+
+// Metadata returns the data source type name.
+func (d *tokenDataSource) Metadata(
+	_ context.Context,
+	req datasource.MetadataRequest,
+	resp *datasource.MetadataResponse,
+) {
+	resp.TypeName = req.ProviderTypeName + "_modelserving_token"
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *tokenDataSource) Configure(
+	ctx context.Context,
+	req datasource.ConfigureRequest,
+	resp *datasource.ConfigureResponse,
+) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	providerData, ok := req.ProviderData.(core.ProviderData)
+	if !ok {
+		core.LogAndAddError(
+			ctx,
+			&resp.Diagnostics,
+			"Error configuring API client",
+			fmt.Sprintf(
+				"Expected configure type stackit.ProviderData, got %T",
+				req.ProviderData,
+			),
+		)
+		return
+	}
+
+	var apiClient *modelserving.APIClient
+	var err error
+	if providerData.ModelServingCustomEndpoint != "" {
+		apiClient, err = modelserving.NewAPIClient(
+			config.WithCustomAuth(providerData.RoundTripper),
+			config.WithEndpoint(providerData.ModelServingCustomEndpoint),
+			config.WithRegion(providerData.GetRegion()),
path: stackit/internal/services/modelserving/token/datasource.go
user: login: bahkauv70
body: this function has been deprecated
created_at: 2025-03-24T14:14:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: The attribute is still defined, but it is not used in any request/response and it is not computed. Could you please clarify it's purpose?
created_at: 2025-03-25T12:38:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: I thought I have commented on it :D
Maybe an user want to rotate the token after a certain amount of time like this example:
```
resource "time_rotating" "rotate" {
  rotation_minutes = 1
}

resource "stackit_modelserving_token" "example" {
  project_id = "722ceecb-ac78-47b8-a7bc-65aa591622ce"
  name       = "Example token"
  ttl_duration = "10m"
#   rotate_when_changed = {
#       rotation = time_rotating.rotate.id
#     }
  region = "eu01"
}
```

I stole the code from Moritz service account rotation
created_at: 2025-03-25T13:12:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: We might want to add it to the generated docs like I've done it:
https://github.com/stackitcloud/terraform-provider-stackit/blob/main/stackit/internal/services/serviceaccount/key/const.go
created_at: 2025-03-25T13:16:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: PatrickKoss
body: generating docs is pain since it generate a bunch of docs that should not be in the pr. Manually deleting them is annoying so I will skip docs here :D
created_at: 2025-03-25T13:21:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: h3adex
body: this issue got fixed. Just rebase from the latest master
created_at: 2025-03-25T14:54:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,767 @@
+package token
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+	"github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving"
+	"github.com/stackitcloud/stackit-sdk-go/services/modelserving/wait"
+	"github.com/stackitcloud/stackit-sdk-go/services/serviceenablement"
+	serviceEnablementWait "github.com/stackitcloud/stackit-sdk-go/services/serviceenablement/wait"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+	"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ resource.Resource                = &tokenResource{}
+	_ resource.ResourceWithConfigure   = &tokenResource{}
+	_ resource.ResourceWithImportState = &tokenResource{}
+)
+
+const (
+	inactiveState = "inactive"
+)
+
+type Model struct {
+	Id                types.String `tfsdk:"id"` // needed by TF
+	ProjectId         types.String `tfsdk:"project_id"`
+	Region            types.String `tfsdk:"region"`
+	TokenId           types.String `tfsdk:"token_id"`
+	Name              types.String `tfsdk:"name"`
+	Description       types.String `tfsdk:"description"`
+	State             types.String `tfsdk:"state"`
+	ValidUntil        types.String `tfsdk:"valid_until"`
+	TTLDuration       types.String `tfsdk:"ttl_duration"`
+	Content           types.String `tfsdk:"content"`
+	RotateWhenChanged types.Map    `tfsdk:"rotate_when_changed"`
path: stackit/internal/services/modelserving/token/resource.go
user: login: bahkauv70
body: Ok, got the intention of the attribute, and now I understood the description of the attribute as well ;-) Perhaps to avoid confusion it would be good to add a sentence to the description, that this is a client-side only attribute and is not actually sent to the API.
created_at: 2025-03-25T15:00:28Z
author_association: CONTRIBUTOR
path: stackit/internal/services/modelserving/token/const.go
user: login: bahkauv70
body: Informational note: We plan to move away from the string constants in a const.go file and intend to use embedded markdown files instead. No change required for this as of now
created_at: 2025-03-27T15:48:59Z
author_association: CONTRIBUTOR

PR comments:
user: login: rubenhoenle
created_at: 2025-02-27T10:03:11Z
body: Could you add some examples for your new resource and datasource please?

see https://github.com/stackitcloud/terraform-provider-stackit/tree/main/examples
author_association: MEMBER
user: login: PatrickKoss
created_at: 2025-02-27T10:23:54Z
body: > Could you add some examples for your new resource and datasource please?
> 
> see https://github.com/stackitcloud/terraform-provider-stackit/tree/main/examples

Added the examples
author_association: CONTRIBUTOR
user: login: h3adex
created_at: 2025-03-12T10:12:15Z
body: Please also run`make generate-docs`and push the model_serving docs to this PR :-)
author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-14T11:48:21Z
body: will come back to this pr once the stackit-go-sdk is released because I need the wait handler
author_association: CONTRIBUTOR
user: login: h3adex
created_at: 2025-03-14T12:04:44Z
body: > will come back to this pr once the stackit-go-sdk is released because I need the wait handler

[Release (2025-03-14)](https://github.com/stackitcloud/stackit-sdk-go/releases/tag/release-2025-03-14)
author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-17T15:15:56Z
body: @h3adex @rubenhoenle can you check again? Is this the right way to implement the default region if not in the provider specified?
author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-24T09:54:07Z
body: @h3adex @rubenhoenle can you check again? 
author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-25T06:09:50Z
body: > Generally please try not to reformat unrelated code if not strictly required by the linter etc.. This blows up the change and increases the required effort for reviewing... If possible, please revert changes to unrelated files, Thanks!

will not revert any reformats since it is too much work and brings zero value
author_association: CONTRIBUTOR
user: login: bahkauv70
created_at: 2025-03-25T15:05:25Z
body: Ok, looks good now. Could you please provide a sample terraform file and some testing instructions, so we can do some acceptance testsing for this change. If it works, we can merge it asap. 
For future PR please try to avoid reformatting of existing code when not necessary to keep the diffs as small as possible
author_association: CONTRIBUTOR
user: login: h3adex
created_at: 2025-03-25T15:07:32Z
body: ```terraform
provider "stackit" {
  default_region           = var.stackit_region
  service_account_key_path = var.stackit_service_account_key_path
  enable_beta_resources    = true
}

resource "time_rotating" "rotate" {
  rotation_minutes = 1
}

resource "stackit_modelserving_token" "example" {
  project_id = var.stackit_project_id
  region     = "eu01"
  name       = "Example token"

  rotate_when_changed = {
    rotation = time_rotating.rotate.id
  }
}
```

author_association: CONTRIBUTOR
user: login: bahkauv70
created_at: 2025-03-25T15:30:12Z
body: Creating the datasource still has some issues:
when using the following terraform file:
```terraform

resource "time_rotating" "rotate" {
  rotation_minutes = 60
}

resource "stackit_modelserving_token" "example" {
  project_id = var.project_id
  region     = "eu01"
  name       = "Example token"

  rotate_when_changed = {
    rotation = time_rotating.rotate.id
  }
}

data "stackit_modelserving_token" "exampleds" {
  project_id = var.project_id
  token_id   = stackit_modelserving_token.example.token_id
}

output "testoutput" {
  value = [
    "token id: ${data.stackit_modelserving_token.exampleds.token_id}",
  ]
}

```
the following error occurs
```shell
│ Error: Value Conversion Error
│ 
│   with data.stackit_modelserving_token.exampleds,
│ An unexpected error was encountered trying to convert tftypes.Value into token.Model. This
│ is always an error in the provider. Please report the following to the provider developer:
│ 
│ mismatch between struct and object: Struct defines fields not found in object: ttl_duration
│ and rotate_when_changed.
╵
```

The datasource has a different structure as the resource, so we probably need a dedicated model for the datasource 

author_association: CONTRIBUTOR
user: login: h3adex
created_at: 2025-03-25T15:33:36Z
body: I also got a region error on the data-source.

![Screenshot 2025-03-25 at 16 32 54](https://github.com/user-attachments/assets/417119a1-cb9d-4694-a613-7214efd151d2)

author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-27T06:27:00Z
body: > Creating the datasource still has some issues: when using the following terraform file:
> 
> ```hcl
> resource "time_rotating" "rotate" {
>   rotation_minutes = 60
> }
> 
> resource "stackit_modelserving_token" "example" {
>   project_id = var.project_id
>   region     = "eu01"
>   name       = "Example token"
> 
>   rotate_when_changed = {
>     rotation = time_rotating.rotate.id
>   }
> }
> 
> data "stackit_modelserving_token" "exampleds" {
>   project_id = var.project_id
>   token_id   = stackit_modelserving_token.example.token_id
> }
> 
> output "testoutput" {
>   value = [
>     "token id: ${data.stackit_modelserving_token.exampleds.token_id}",
>   ]
> }
> ```
> 
> the following error occurs
> 
> ```shell
> │ Error: Value Conversion Error
> │ 
> │   with data.stackit_modelserving_token.exampleds,
> │ An unexpected error was encountered trying to convert tftypes.Value into token.Model. This
> │ is always an error in the provider. Please report the following to the provider developer:
> │ 
> │ mismatch between struct and object: Struct defines fields not found in object: ttl_duration
> │ and rotate_when_changed.
> ╵
> ```
> 
> The datasource has a different structure as the resource, so we probably need a dedicated model for the datasource

fixed it
author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-27T06:27:21Z
body: > I also got a region error on the data-source.
> 
> ![Screenshot 2025-03-25 at 16 32 54](https://private-user-images.githubusercontent.com/39736813/426604964-417119a1-cb9d-4694-a613-7214efd151d2.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDMwNTMzODEsIm5iZiI6MTc0MzA1MzA4MSwicGF0aCI6Ii8zOTczNjgxMy80MjY2MDQ5NjQtNDE3MTE5YTEtY2I5ZC00Njk0LWE2MTMtNzIxNGVmZDE1MWQyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMjclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzI3VDA1MjQ0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTEzMzc0Y2FiOTZjZDQxMmM3NzRjNGQ0NDMwZjI4MGU5YzcyODY3ZWI3OWUwYmU5ZjFlYzVmNWUwMGFjY2U0YmUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.eg1i98LPirxRC9b5GWkPmaaKf4wwOXLS2EkBJOFAFOw)

should be fixed
author_association: CONTRIBUTOR
user: login: PatrickKoss
created_at: 2025-03-27T07:56:04Z
body: > Ok, looks good now. Could you please provide a sample terraform file and some testing instructions, so we can do some acceptance testsing for this change. If it works, we can merge it asap. For future PR please try to avoid reformatting of existing code when not necessary to keep the diffs as small as possible

where do I need to add these instructions? I see there is some kind of acceptance test in each service.
author_association: CONTRIBUTOR
user: login: bahkauv70
created_at: 2025-03-27T15:43:55Z
body: The testing instructions are commonly added to the JIRA issues (for this one it would be STACKITTPR-147. Note that due to permission issues the TPR project is most suitable. In this case however the terraform config is sufficient.
author_association: CONTRIBUTOR