Nice_cxone_screen_recording_policy provider 1.19.2 drops 422 on retention_hours binding

How does the provider actually map retention_hours to the underlying /api/v2/screen-recording/policies endpoint when the API rejects values over 720?

The 03:15 JST deployment pipeline stalled again. State backup shows zero drift, but Terraform 1.9.8 paired with nice-cxone 1.19.2 throws a 422 Unprocessable Entity on the nice_cxone_screen_recording_policy resource. Validation logic inside the provider seems to clamp the integer, yet the API response body returns {“code”:“validation_error”,“message”:“retention_period exceeds maximum allowed threshold”}.

Tried dropping the value to 720 first. Pipeline still failed. Checked the raw HTTP request via TF_LOG=DEBUG. The payload sends “retention_hours”: 1440 despite the HCL block explicitly stating retention_hours = 720. Looks like the state file is caching the previous run’s value and the diff calculation skips the override. Forced a terraform apply -target=nice_cxone_screen_recording_policy.prod_screen_policy -refresh=false anyway. Same 422.

Cleared the local state cache, ran terraform init -upgrade, pulled the latest provider binary. The resource definition in the provider source still lists retention_hours as a computed attribute on read, but writable on create/update. API docs say the field maps to retention_period_days behind the scenes. Provider doesn’t handle the unit conversion.

Workaround attempted: switched to using the REST API directly via a null_resource with local-exec. That actually pushes the config through without errors. Terraform state imports it fine, but every subsequent plan marks it as tainted because the API returns retention_period_days instead of retention_hours. Drift detection goes haywire. Mic stays hot while debugging this.

Provider changelog for 1.19.0 mentions improved media policy handling but skipped screen recording entirely. State backup won’t save us if the schema keeps mutating the field name on every refresh.

resource "nice_cxone_screen_recording_policy" "prod_screen_policy" {
 name = "Prod-Desktop-Recording"
 retention_hours = 720
 scope = "global"
}

Error trace points to client.go:342 during the PATCH request. Field mapping is completely off.

The 422 isn’t coming from the provider clamping the value incorrectly. It’s the API rejecting anything that violates the global org limit, which is usually set much lower than 720 hours by default. The provider just passes the integer through. You need to check your org settings first.

Run a GET on /api/v2/screen-recording/settings to see the maxRetentionHours value. If it’s 2160 (90 days), then 720 is fine. If it’s lower, the API will hard reject it. The Terraform provider doesn’t handle this dependency automatically yet. You’ll need to update the org setting via API or UI before applying the policy.

# Check current limit first
data "nice_cxone_screen_recording_settings" "current" {}

resource "nice_cxone_screen_recording_policy" "main" {
 name = "Standard Policy"
 retention_hours = 720 # Must be <= data.nice_cxone_screen_recording_settings.current.max_retention_hours
}

Also ensure you’re using a service account with screenrecording:manage scope. The error message is misleading because it points to the policy resource, but the validation happens at the org level.

Be careful here. The API limit isn’t the only thing that bites you. If you crank that retention up in Terraform but your storage backend (S3 bucket or Azure Blob) has a lifecycle rule set to expire objects sooner, you get a silent mismatch. The recording stays in GC’s index but the actual media file vanishes. Debugging that nightmare takes forever because the webhook payload still references a 404.

Check your storage provider’s lifecycle policies. If you’re using S3, run this to verify:

aws s3api get-bucket-lifecycle-configuration --bucket your-gc-media-bucket

Look for Expiration rules. If the rule says Days: 90 but you set retention_hours to 2160 (90 days), you’re cutting it close. Clock skew between GC and AWS can cause the media to expire before GC purges the record. Set the storage expiration slightly higher than the GC retention to be safe. I usually add a 48-hour buffer. It costs a tiny bit more in storage, but it saves hours of support tickets when agents complain about missing playback.