
# Admin Portal 링크 생성

`POST /v1/admin-portal-links` · 권한 `admin-portal-links:write`

고객 조직의 IT 관리자가 지원팀의 도움 없이 호스팅된 Admin Portal에서 SSO, 디렉터리 동기화, 도메인 검증 등을 설정할 수 있는 링크를 생성합니다. 관리자가 애플리케이션에서 "SSO 설정"을 클릭할 때마다 새 링크를 생성하고 즉시 `url`로 리디렉션하세요. 링크는 5분간 유효하며, 포털 세션을 정확히 한 번 엽니다. `url`은 이 응답에서 한 번만 반환되며, 이후에는 어떤 엔드포인트에서도 반환되지 않습니다. 응답에는 `Cache-Control: no-store`가 포함되고, 같은 `Idempotency-Key`에 대해 다시 재생된 응답에는 `url`이 빠지고 `secret_redacted: true`가 포함됩니다. 포털 세션은 60분 동안 유휴 상태이거나 최대 8시간 유지됩니다. `DELETE /v1/admin-portal-links/{link_id}`로 취소할 수 있습니다. 생성 작업은 `admin_portal_link.created`로 감사됩니다(URL은 감사 로그에 기록되지 않습니다).

`audit_logs`에는 Business 요금제가, `log_streams`에는 Enterprise 요금제가 필요합니다. 그 밖의 인텐트는 모든 요금제에서 사용할 수 있습니다. `delivery: email`은 대신 초대를 보냅니다. 초대는 7일간 유효하며 `recipient_email`로 이메일이 발송됩니다. 응답에는 `url`도 주소도 포함되지 않습니다. 수신자가 초대를 열면 같은 주소로 6자리 코드(10분간 유효)가 발송되며, 검증된 코드만 포털 세션을 엽니다. 초대 메일을 보낼 수 없으면 호출은 `Retry-After`와 함께 `503 service_unavailable`로 응답하며, 아무것도 생성되지 않습니다. 초대는 `DELETE /v1/admin-portal-links/{link_id}`로 취소합니다. 수신자 주소는 응답, 목록, 감사 로그, 어떤 로그에도 나타나지 않습니다.

## 요청

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `organization_id` | string | 예 | 조직 ID(`org_…`). 이 환경에 존재해야 합니다. |
| `intents` | string[] | 예 | `sso`, `dsync`, `domain_verification`, `certificate_renewal`, `audit_logs`(Business), `log_streams`(Enterprise) 중 1~6개. 같은 값을 두 번 넣을 수 없습니다. |
| `return_url` | string | 예 | 최대 2048바이트의 절대 `https` URL. 사용자 정보나 프래그먼트가 없어야 합니다. 그 오리진은 이 환경에 등록된 활성 애플리케이션의 `redirect_uri` 오리진과 일치해야 합니다. |
| `delivery` | string | 아니요 | `link`(기본값; 응답에 5분간 유효한 일회용 `url` 포함) 또는 `email`(`recipient_email`로 메일이 발송되는 7일 유효 초대; `url` 없음). |
| `recipient_email` | string | 아니요 | `delivery: email`일 때만 사용하며, 그 경우 필수입니다. 최대 320자. 없으면 `422` `/recipient_email` `required`; `delivery: link`와 함께 쓰면 `not_allowed`; 올바르지 않은 주소이면 `invalid_value`. |

## 응답

링크와 일회용 `url`이 담긴 `201 Created`.

```json
{
  "id": "apl_01j9xv2c4d6e8f0g2h4j6k8m0n",
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "intents": [
    "dsync",
    "sso"
  ],
  "delivery": "link",
  "return_url": "https://app.example.com/settings/sso",
  "expires_at": "2026-10-12T09:36:00.123456Z",
  "consumed_at": null,
  "revoked_at": null,
  "created_at": "2026-10-12T09:31:00.123456Z",
  "url": "https://acme.eu.keenauth.app/portal/l/<token>"
}
```

## 오류

| HTTP | code | 발생 조건 |
|---|---|---|
| 403 | `plan_feature_unavailable` | 인텐트에 더 높은 요금제가 필요합니다. `feature`와 `required_plan`이 이를 나타냅니다. |
| 404 | `not_found` | 이 환경에 조직이 존재하지 않습니다. |
| 409 | `organization_disabled` | 조직이 활성 상태가 아닙니다(비활성화됨). 링크가 생성되지 않습니다. |
| 409 | `organization_pending_deletion` | 조직이 삭제 예정입니다. 링크가 생성되지 않습니다. |
| 422 | `validation_failed` | 필드가 없거나 올바르지 않습니다. `/return_url`의 `origin_not_registered`는 오리진이 등록된 애플리케이션의 것이 아닐 때입니다. `/recipient_email`의 `required`, `not_allowed`, `invalid_value`. |
| 401 | `unauthenticated` | 자격 증명이 없거나, 올바르지 않거나, 취소되었습니다. |
| 403 | `insufficient_permissions` | 자격 증명에 필요한 권한이 없습니다. |
| 429 | `rate_limited` | 요청 한도를 초과했습니다. `Retry-After` 기간이 지난 후 다시 시도하세요. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없거나, (`delivery: email`) 초대 메일을 보낼 수 없습니다. 아무것도 생성되지 않습니다. `Retry-After` 기간이 지난 후 다시 시도하세요. |

## 예시

```bash
curl -i -X POST "https://api.eu.keenauth.com/v1/admin-portal-links" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"organization_id":"org_01j9xk3m7q8r2s4t6v8w0y2z4a","intents":["sso","dsync"],"return_url":"https://app.example.com/settings/sso"}'
```

### 이메일로 초대 보내기

```bash
curl -i -X POST "https://api.eu.keenauth.com/v1/admin-portal-links" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"organization_id":"org_01j9xk3m7q8r2s4t6v8w0y2z4a","intents":["sso"],"return_url":"https://app.example.com/settings/sso","delivery":"email","recipient_email":"it@customer.example"}'
```

`201 Created`; 응답에는 `url`도 주소도 없습니다.

```json
{
  "id": "apl_01j9xv2c4d6e8f0g2h4j6k8m0n",
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "intents": [
    "sso"
  ],
  "delivery": "email",
  "return_url": "https://app.example.com/settings/sso",
  "expires_at": "2026-10-19T09:31:00.123456Z",
  "consumed_at": null,
  "revoked_at": null,
  "created_at": "2026-10-12T09:31:00.123456Z"
}
```

CLI로는 다음과 같습니다: `keenauth admin-portal-links create --organization-id org_01j9xk3m7q8r2s4t6v8w0y2z4a --intents sso --return-url https://app.example.com/settings/sso --delivery email --recipient-email it@customer.example`.
