
# 조직 정책 생성 또는 교체

`PUT /v1/organizations/{org_id}/policies/{policy_kind}` · 권한 `organizations:write`

하나의 정책 종류에 대한 설정 객체 전체를 교체합니다. 조직 정책은 환경의 설정보다 엄격하게만 설정할 수 있습니다. 요금제 제한은 서버에서 적용됩니다. `session` 및 `ip_allowlist` 종류와 AAGUID 허용 목록에는 Enterprise 요금제가 필요합니다(개발 환경에서는 사용 가능). 설정 내용은 관리 감사 로그에 기록되지만 `organization.updated` 이벤트에는 포함되지 않습니다.

## 요청

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `org_id` | path | 예 | 조직 ID(`org_…`)입니다. |
| `policy_kind` | path | 예 | 정책 종류: `authentication_methods`, `mfa`, `phishing_resistant`, `sso_enforcement`, `session` 또는 `ip_allowlist`입니다. 다른 값은 `404`를 반환합니다. |
| `If-Match` | header | 아니요 | 정책이 존재하면 필요합니다(마지막으로 읽은 `ETag`). 정책을 새로 만들려면 생략합니다. `*`는 기존 정책과 일치합니다. |
| `settings` | object | 예 | 종류별 설정 객체입니다. 종류별 필드: `authentication_methods`: `allowed_connection_ids`(`conn_…`), `allow_passkey`, `allow_password`; `mfa`: `required_for`(`none`, `admins`, `all`), `allowed_factors`(`totp`, `webauthn`, `sms`, `email`), `enrollment_grace_days`(0-30); `phishing_resistant`: `required_for`, `aaguid_allowlist`(최대 500개); `sso_enforcement`: `mode`(`off`, `org_context`, `verified_domains`), `connection_ids`(`conn_…`), `bypass_user_ids`(`usr_…`, 최대 2개); `session`: `idle_minutes`, `absolute_minutes`, `refresh_idle_days`, `refresh_absolute_days`; `ip_allowlist`: `cidrs`(최대 100개), `applies_to`(`admins`, `all`). |

요청 본문:

```json
{
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  }
}
```

## 응답

정책을 생성하면 `201 Created`, 교체하면 `200 OK`를 반환합니다. 본문은 정책이며, 응답에는 새 `ETag`가 포함됩니다.

```json
{
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "policy_kind": "mfa",
  "schema_version": 1,
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  },
  "updated_at": "2026-10-12T09:30:00.123456Z",
  "version": 1
}
```

## 오류

| HTTP | code | 발생 조건 |
|---|---|---|
| 400 | `unknown_field` | 본문에 스키마에 없거나 읽기 전용인 필드가 있습니다. |
| 403 | `plan_feature_unavailable` | 해당 종류 또는 AAGUID 허용 목록에 더 높은 요금제가 필요합니다. 문제 응답에는 `feature`와 `required_plan`이 포함됩니다. |
| 404 | `not_found` | 조직이 존재하지 않거나 종류를 알 수 없습니다. |
| 409 | `domain_not_verified` | `mode`가 `verified_domains`인 `sso_enforcement`에는 조직에 인증된 도메인이 필요합니다. |
| 409 | `connection_test_required` | `sso_enforcement`를 켜려면(`off` 또는 정책 없음에서 전환) `connection_ids` 중 하나 이상의 현재 구성에 대해 최근 24시간 내 테스트 로그인이 성공해야 합니다. 연결 테스트(`POST /v1/connections/{connection_id}:test`)를 실행한 후 다시 시도하십시오. |
| 409 | `organization_pending_deletion` | 조직이 셀프 서비스 삭제를 대기 중입니다. 먼저 삭제를 취소하십시오. |
| 412 | `precondition_failed` | `If-Match`가 현재 버전과 일치하지 않거나, 이 종류의 정책이 없는 상태에서 전송되었거나, 이 요청이 생성되는 동안 다른 요청이 정책을 만들었습니다. 정책을 다시 읽으십시오. |
| 422 | `validation_failed` | 설정 필드가 알 수 없거나 유효하지 않습니다. `errors[]`가 `/settings/…`를 가리킵니다. |
| 422 | `policy_weaker_than_parent` | 설정이 환경의 설정보다 약합니다. `errors[]`가 약한 필드를 가리킵니다. |
| 422 | `at_least_one_method` | `authentication_methods`에 로그인 방법이 남지 않습니다. |
| 428 | `precondition_required` | 정책이 존재하는데 `If-Match`가 없습니다. |
| 401 | `unauthenticated` | 자격 증명이 없거나, 유효하지 않거나, 취소되었습니다. |
| 403 | `insufficient_permissions` | 자격 증명에 필요한 권한이 없습니다. |
| 429 | `rate_limited` | 요청 한도를 초과했습니다. `Retry-After` 기간 후 다시 시도하십시오. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없습니다. `Retry-After` 기간 후 다시 시도하십시오. |

## 예시

```bash
curl -X PUT https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/policies/mfa \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'
```
