
# 조직에 구성원 초대

`POST /v1/invitations` · 권한 `invitations:write`

이메일 주소를 대상으로 조직 가입 초대를 만듭니다. `organization_id` 없이 생성하면 환경 초대가 됩니다. 이는 초대 전용 가입 모드의 가입 링크이며, 멤버십을 만들지 않고 역할도 지정할 수 없습니다(`role_ids` 요청 시 `422 validation_failed` 반환). 응답에는 `organization_id: null`가 없습니다. `delivery`은 필수입니다. `email`을 사용하면 환경의 초대 템플릿(`delivery_status: queued`)으로 초대 이메일을 발송하며, 링크는 이메일에만 포함되고 응답에는 `accept_url`이 없습니다. `link_only`을 사용하면 이메일을 보내지 않고 링크를 반환하므로 직접 공유하십시오. 수락 링크(`accept_url`)는 이 응답에서만 한 번 반환되며, 이후 어떤 엔드포인트도 반환하지 않습니다. 응답에는 `Cache-Control: no-store`이 포함되며, 같은 `Idempotency-Key`에 대해 재전송된 응답에는 링크가 빠지고 `secret_redacted: true`이 포함됩니다. 배포에 초대 이메일이 설정되지 않은 경우(프로덕션이 아닌 환경에서만), `email` 전달은 `501 not_implemented`을 반환하며 아무것도 기록되지 않습니다. 같은 조직과 이메일에 같은 역할의 대기 중 초대가 이미 있으면 새 링크로 재발송되며, 응답은 `200`입니다. 만료되었으나 아직 처리되지 않은 대기 초대는 같은 요청에서 `expired`로 표시되고 새 초대가 기록됩니다(`invitation.expired` 작업으로 감사 기록). 같은 역할의 대기 `email` 초대는 이메일로 재발송되며, 응답은 `200`이고 `delivery: email`을 포함하며 `accept_url`은 없습니다. 이 쓰기 작업은 `invitation.created` 작업으로 감사 기록됩니다.

## 요청

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `organization_id` | 문자열 | 아니요 | 조직 ID(`org_…`). 환경 초대(역할 없음)를 만들려면 생략하십시오. |
| `email` | 문자열 | 예 | 초대받는 사람의 이메일 주소, 최대 320자. |
| `delivery` | 문자열 | 예 | `email`(링크는 이메일에만 포함) 또는 `link_only`(링크는 이 응답에만 포함). |
| `role_ids` | 문자열[] | 아니요 | 수락 시 부여할 역할(`rol_…`), 최대 10개. 비어 있으면 기본 구성원 역할. |
| `application_id` | 문자열 | 아니요 | 초대받은 사람이 들어갈 애플리케이션(`app_…`). 이 환경에 존재해야 합니다. |
| `ttl_days` | 정수 | 아니요 | 1–30. 기본값은 환경의 `invitation_ttl_days`(7)입니다. |

## 응답

초대와 수락 링크가 포함된 `201 Created`을 반환합니다(대기 초대를 재발송한 경우 `200 OK`).

```json
{
  "id": "inv_01j9xt9v3w4y8z0a2b4c6d8e0g",
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "email": "ada@example.com",
  "role_ids": [
    "rol_01j9xr7s1t2w6x8y0z2a4b6c8e"
  ],
  "application_id": null,
  "delivery": "link_only",
  "delivery_status": "not_sent",
  "status": "pending",
  "send_count": 1,
  "last_sent_at": "2026-10-12T09:31:00.123456Z",
  "expires_at": "2026-10-19T09:31:00.123456Z",
  "accepted_user_id": null,
  "accepted_at": null,
  "revoked_at": null,
  "created_at": "2026-10-12T09:31:00.123456Z",
  "updated_at": "2026-10-12T09:31:00.123456Z",
  "version": 1,
  "accept_url": "https://acme.eu.keenauth.app/u/login?invitation=<token>"
}
```

## 오류

| HTTP | 코드 | 발생 조건 |
|---|---|---|
| 404 | `not_found` | 이 환경에 조직 또는 애플리케이션이 없습니다. |
| 409 | `invitation_already_pending` | 이 이메일에 대해 다른 역할을 가진 대기 초대가 있습니다. |
| 409 | `membership_already_exists` | 이 이메일은 이미 구성원에게 속해 있습니다. |
| 409 | `organization_disabled` | 조직이 비활성화되어 있습니다. |
| 422 | `invitation_invalid` | 이 값으로는 초대를 만들 수 없습니다. |
| 422 | `validation_failed` | 필드가 없거나 잘못되었습니다. |
| 429 | `invite_rate_limited` | 초대가 10분 이내에 재발송되었거나 이미 5회 재발송되었습니다. |
| 501 | `not_implemented` | `delivery`이 `email`이고 배포에 초대 이메일이 설정되지 않았습니다. |
| 401 | `unauthenticated` | 자격 증명이 없거나 잘못되었거나 취소되었습니다. |
| 403 | `insufficient_permissions` | 자격 증명에 필요한 권한이 없습니다. 권한이 높은(`is_privileged`) 역할을 할당하려면 `roles:write`도 필요합니다. 없으면 아무것도 기록되지 않으며 `required_permissions`에 두 권한이 모두 나열됩니다. |
| 429 | `rate_limited` | 요청 한도를 초과했습니다. `Retry-After` 기간 후에 다시 시도하십시오. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없습니다. `Retry-After` 기간 후에 다시 시도하십시오. |

## 예시

```bash
curl -i -X POST "https://api.eu.keenauth.com/v1/invitations" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"organization_id":"org_01j9xk3m7q8r2s4t6v8w0y2z4a","email":"ada@example.com","delivery":"link_only","role_ids":["rol_01j9xr7s1t2w6x8y0z2a4b6c8e"]}'
```

## 개인정보

이 엔드포인트는 개인정보를 포함합니다: 조직 멤버십과 초대. [KeenAuth API의 개인정보](/docs/policies/personal-data)에서 저장되는 항목, 보관 기간, 삭제 방법을 설명합니다. 어떤 필드에도 특수 범주의 개인정보(건강, 생체 정보, 종교, 정치적 견해, 성적 지향)를 보내지 마십시오.
