diff --git a/terraform-reference/resources/service_account_api_key.mdx b/terraform-reference/resources/service_account_api_key.mdx
index 2efc0fb..1ceb832 100644
--- a/terraform-reference/resources/service_account_api_key.mdx
+++ b/terraform-reference/resources/service_account_api_key.mdx
@@ -32,17 +32,22 @@ resource "kosli_service_account" "ci" {
privilege = "member"
}
-# API key with the server-side maximum lifetime (365 days from creation)
+# Omitting expires_at lets the server choose the expiry, which is the maximum
+# it allows: 365 days from creation. No API key can be created that never
+# expires.
resource "kosli_service_account_api_key" "ci_key" {
service_account_name = kosli_service_account.ci.name
description = "Production CI key"
}
-# API key with an explicit expiry (RFC3339 timestamp)
+# An API key with an explicit expiry (RFC3339 timestamp). It must be in the
+# future and no more than 365 days out. The server silently shortens anything
+# longer.
resource "kosli_service_account_api_key" "ci_key_expiring" {
service_account_name = kosli_service_account.ci.name
description = "Temporary CI key"
- expires_at = "2026-12-31T00:00:00Z"
+ # Pick a date within 365 days of when you apply; update it as you rotate.
+ expires_at = "2027-01-01T00:00:00Z"
}
# The raw key is only available on creation and is sensitive
@@ -54,21 +59,16 @@ output "ci_api_key" {
## Expiry
-Every API key expires. The server caps each key's lifetime at **365 days from creation** and enforces the cap regardless of the client.
+The `expires_at` attribute is an RFC3339 timestamp, e.g. `2027-01-01T00:00:00Z` (offsets such as `+01:00` are accepted and normalized to UTC). The timestamp must not be in the past.
-The `expires_at` attribute is an RFC3339 timestamp, e.g. `2026-12-31T00:00:00Z` (offsets such as `+01:00` are accepted and normalized to UTC). Behavior:
-
-- **Omitted** → the server applies the maximum 365-day expiry.
-- **Set within 365 days** → honored as-is.
-- **Set beyond 365 days** → silently shortened to the 365-day cap. Terraform then reports the result as an inconsistent result after apply and marks the resource tainted so the next apply revokes and recreates it. Choose an `expires_at` within the 365-day window to avoid this.
-- **In the past** → rejected server-side at apply time.
-
-Keys that never expire can no longer be created.
+
+Kosli caps the lifetime of every API key at **365 days**. An `expires_at` further out than that is silently shortened by the server, and Terraform then reports the mismatch as `Provider produced inconsistent result after apply`. Keys that never expire can no longer be created: omitting `expires_at` yields the maximum 365-day expiry rather than no expiry at all.
+
-To derive dates dynamically, use Terraform's built-in functions, e.g. `timeadd("2026-01-01T00:00:00Z", "8760h")`.
+To derive dates from a fixed anchor, use Terraform's built-in functions, e.g. `timeadd("2027-01-01T00:00:00Z", "720h")`. Avoid deriving `expires_at` from `timestamp()`. It changes on every plan, and because `expires_at` forces replacement that would revoke and recreate the key on each apply.
-All timestamps (`expires_at`, `created_at`, `last_used_at`) are RFC3339 UTC strings. `last_used_at` is null for a key that has never been used.
+All timestamps (`expires_at`, `created_at`, `last_used_at`) are RFC3339 UTC strings. `last_used_at` is null for a key that has never been used. `expires_at` is null only for keys minted before the 365-day cap was introduced.
## Import
@@ -93,7 +93,7 @@ Because the raw key value is only returned at creation time, the `key` attribute
### Optional
-- `expires_at` (String) RFC3339 timestamp at which the key expires, e.g. `2026-12-31T00:00:00Z` (offsets allowed; whole seconds only). Must not be in the past (validated server-side at apply time) and must be no more than 365 days out: the server caps every key's lifetime and silently shortens a longer expiry, which Terraform then reports as an inconsistent result after apply. Omit to let the server apply the maximum 365-day expiry; keys that never expire can no longer be created. Changing this forces creation of a new key. Removing a previously set value from configuration leaves the existing expiry unchanged.
+- `expires_at` (String) RFC3339 timestamp at which the key expires, e.g. `2027-01-01T00:00:00Z` (offsets allowed; whole seconds only). Must not be in the past (validated server-side at apply time) and must be no more than 365 days out: the server caps every key's lifetime and silently shortens a longer expiry, which Terraform then reports as an inconsistent result after apply. Omit to let the server apply the maximum 365-day expiry; keys that never expire can no longer be created. Changing this forces creation of a new key. Removing a previously set value from configuration leaves the existing expiry unchanged.
### Read-only