> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lexq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 정책 실행

> 배포된 정책을 입력 팩트에 대해 실시간으로 실행합니다. LexQ는 용도별 4가지 실행 모드를 제공합니다.

## Base URL

```
https://api.lexq.io/api/v1/execution
```

<Warning>
  실행 API의 Base URL(`/api/v1/execution`)은 관리 API(`/api/v1/partners`)와 다릅니다. 각 API에 맞는 Base URL을 사용하세요.
</Warning>

## 인증

관리 API와 동일한 API Key를 사용합니다. 모든 요청에 포함하세요:

```
x-api-key: YOUR_API_KEY
```

## 실행 모드

| 모드        | 엔드포인트                                         | 용도                          |
| --------- | --------------------------------------------- | --------------------------- |
| **단일 그룹** | `POST /groups/{groupId}`                      | 일반적인 단건 주문 평가               |
| **특정 버전** | `POST /groups/{groupId}/versions/{versionId}` | A/B 테스트, 롤백 테스트             |
| **배치**    | `POST /groups/{groupId}/batch`                | 장바구니 계산, 상품 목록 혜택 표기        |
| **복합**    | `POST /composite`                             | 다중 그룹 동시 평가 (할인 + 쿠폰 + 배송비) |

***

## 단일 그룹 실행

현재 배포된 버전의 모든 활성 규칙을 평가합니다.

```
POST /groups/{groupId}
```

### 요청

```bash theme={null}
curl -X POST https://api.lexq.io/api/v1/execution/groups/{groupId} \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-12345" \
  -d '{
    "facts": {
      "user_id": "user_abc",
      "payment_amount": 150000,
      "customer_tier": "VIP"
    },
    "context": {
      "channel": "mobile-app"
    }
  }'
```

| 필드        | 타입     |  필수 | 설명                             |
| --------- | ------ | :-: | ------------------------------ |
| `facts`   | object |  ✅  | 규칙 평가를 위한 입력 변수                |
| `context` | object |     | 추가 메타데이터 (조건에 사용되지 않으며 액션에 전달) |

### 응답

```json theme={null}
{
  "result": "SUCCESS",
  "data": {
    "traceId": "2e31f2b8-...",
    "inputFacts": {
      "user_id": "user_abc",
      "payment_amount": 150000,
      "customer_tier": "VIP"
    },
    "mutatedFacts": {
      "payment_amount": 135000
    },
    "generatedVariables": {
      "payment_amount__delta": -15000
    },
    "executionTraces": [
      {
        "tenantId": "acme-corp",
        "policyGroupId": "01f2b274-...",
        "policyVersionId": "a6062090-...",
        "ruleId": "3b16ced1-...",
        "ruleName": "VIP 10% 할인",
        "executedAt": "2026-04-27T09:44:10Z",
        "matched": true,
        "matchExpression": "(customer_tier == 'VIP') && (payment_amount >= 100000)",
        "inputFacts": {
          "customer_tier": "VIP",
          "payment_amount": 150000
        },
        "generatedActions": [
          {
            "type": "MUTATE_FACT",
            "parameters": { "refVar": "payment_amount", "operator": "SUB", "method": "PERCENTAGE", "rate": 10 }
          }
        ]
      }
    ],
    "decisionTraces": [
      {
        "ruleId": "3b16ced1-...",
        "ruleName": "VIP 10% 할인",
        "policyGroupId": "01f2b274-...",
        "policyVersionId": "a6062090-...",
        "status": "SELECTED",
        "reasonCode": "FINAL_WINNER",
        "reasonDetail": null
      }
    ]
  }
}
```

`data.traceId`는 이 실행의 감사 핸들입니다 — 나중에 이 실행을 추적하려면 자체 레코드와 함께 저장하세요.

응답의 `data` 객체는 상태를 세 층위로 분리합니다:

* `inputFacts` — 요청에서 받은 입력 팩트 (정규화 후).
* `mutatedFacts` — 규칙 액션에 의해 값이 변경된 팩트만.
* `generatedVariables` — 규칙 액션이 새로 생성한 변수 (입력에 없었던 것). 액션별 변화 키도 포함됩니다 — `{refVar}__delta`(`MUTATE_FACT`의 부호 포함 변화량)와 `{targetVar}__delta`(`INCREMENT_FACT`의 0 이상 증분량).

이렇게 분리하면 감사·재현·디버그가 결정적입니다 — 어떤 값이 어디서 왔는지가 항상 명확합니다. 합본이 필요하면 `{ ...inputFacts, ...mutatedFacts, ...generatedVariables }`로 재구성하세요.

### 멱등성

`Idempotency-Key` 헤더를 포함하면 중복 실행을 방지합니다. 동일한 키로 이미 처리된 요청이 있으면, 정책을 재실행하지 않고 원래 응답을 반환합니다.

***

## 특정 버전 실행

트래픽 라우팅을 우회하여 정책 그룹의 특정 버전을 실행합니다. A/B 테스트 후보 버전 검증이나 롤백 전 이전 버전 테스트에 유용합니다.

```
POST /groups/{groupId}/versions/{versionId}
```

요청과 응답 형식은 단일 그룹 실행과 동일합니다. 현재 배포된 버전 대신 지정된 버전을 사용하는 것만 다릅니다.

<Info>
  버전은 `ACTIVE` (발행됨) 상태여야 합니다. DRAFT 버전은 Engine API로 실행할 수 없습니다 — DRAFT 테스트에는 [드라이런](/ko/guides/dry-run)을 사용하세요.
</Info>

***

## 배치 실행

여러 팩트 세트를 동일한 정책 그룹에 대해 한 번의 호출로 실행합니다. 일관성을 위해 배치 내 모든 요청이 동일 버전으로 평가됩니다.

```
POST /groups/{groupId}/batch
```

### 요청

```bash theme={null}
curl -X POST https://api.lexq.io/api/v1/execution/groups/{groupId}/batch \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      { "facts": { "user_id": "user_1", "payment_amount": 50000 } },
      { "facts": { "user_id": "user_2", "payment_amount": 150000 } },
      { "facts": { "user_id": "user_3", "payment_amount": 300000 } }
    ],
    "sharedContext": {
      "channel": "batch-job"
    }
  }'
```

| 필드              | 타입     |  필수 | 설명                  |
| --------------- | ------ | :-: | ------------------- |
| `requests`      | array  |  ✅  | 평가할 팩트 세트 목록        |
| `sharedContext` | object |     | 모든 요청에 병합되는 공유 컨텍스트 |

### 응답

```json theme={null}
{
  "result": "SUCCESS",
  "data": {
    "results": [
      { "inputFacts": { ... }, "mutatedFacts": { ... }, "generatedVariables": { ... }, "executionTraces": [ ... ], "decisionTraces": [ ... ] },
      { "inputFacts": { ... }, "mutatedFacts": { ... }, "generatedVariables": { ... }, "executionTraces": [ ... ], "decisionTraces": [ ... ] },
      { "inputFacts": { ... }, "mutatedFacts": { ... }, "generatedVariables": { ... }, "executionTraces": [ ... ], "decisionTraces": [ ... ] }
    ],
    "totalProcessingTimeMs": 45
  }
}
```

결과는 입력 요청과 동일한 순서로 반환됩니다. 각 요청의 context는 `sharedContext`와 병합됩니다 (개별 요청 컨텍스트가 충돌 시 우선).

<Info>
  배치 실행은 TPS 스로틀링에서 아이템 수와 관계없이 **1회 API 호출**로 계산됩니다. 단, 과금은 총 아이템 수 기준입니다.
</Info>

***

## 복합 실행

동일한 팩트를 여러 정책 그룹에 대해 한 번의 호출로 평가합니다. 하나의 거래가 여러 정책 검사를 동시에 통과해야 할 때 사용합니다 (예: 상품할인 + 장바구니쿠폰 + 배송비 + 멤버십적립).

```
POST /composite
```

### 요청

```bash theme={null}
curl -X POST https://api.lexq.io/api/v1/execution/composite \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-12345-composite" \
  -d '{
    "targetGroupIds": ["group-discount-id", "group-coupon-id", "group-shipping-id"],
    "facts": {
      "user_id": "user_abc",
      "payment_amount": 250000
    },
    "context": {
      "channel": "checkout"
    }
  }'
```

| 필드               | 타입     |  필수 | 설명                |
| ---------------- | ------ | :-: | ----------------- |
| `targetGroupIds` | array  |  ✅  | 평가할 정책 그룹 ID 목록   |
| `facts`          | object |  ✅  | 모든 그룹에 공유되는 입력 팩트 |
| `context`        | object |     | 모든 그룹에 공유되는 컨텍스트  |

응답에는 평가된 모든 그룹의 `inputFacts`, `mutatedFacts`, `generatedVariables`, `executionTraces`, `decisionTraces`가 병합되어 포함됩니다.

모든 대상 그룹이 `ACTIVE` 상태여야 합니다.

***

## 필수 팩트 조회

실행 엔드포인트를 호출하기 전에 배포된 버전이 어떤 입력 팩트를 필요로 하는지 확인합니다.

```
GET /groups/{groupId}/requirements
```

```bash theme={null}
curl https://api.lexq.io/api/v1/execution/groups/{groupId}/requirements \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept-Language: ko"
```

`Accept-Language` 헤더로 `usedBy` 설명의 언어를 제어합니다 (`en` 또는 `ko`).

### 응답

```json theme={null}
{
  "result": "SUCCESS",
  "data": {
    "groupId": "01f2b274-...",
    "versionId": "a6062090-...",
    "versionNo": 5,
    "requiredFacts": [
      {
        "key": "payment_amount",
        "type": "NUMBER",
        "displayName": "결제 금액",
        "usedBy": [
          "조건 [GREATER_THAN_OR_EQUAL] (규칙: VIP 10% 할인)",
          "MUTATE_FACT 액션 (규칙: VIP 10% 할인)"
        ]
      },
      {
        "key": "customer_tier",
        "type": "STRING",
        "displayName": "고객 등급",
        "usedBy": [
          "조건 [EQUALS] (규칙: VIP 10% 할인)"
        ]
      }
    ],
    "exampleRequest": {
      "facts": { "payment_amount": 50000, "customer_tier": "" },
      "context": { "reqTime": "2025-01-01T00:00:00Z" }
    }
  }
}
```

<Tip>
  첫 연동 전에 이 엔드포인트를 호출하여 필요한 팩트를 확인하세요. `exampleRequest`를 시작 템플릿으로 복사하고 실제 값을 채워 넣으면 됩니다.
</Tip>

***

## 시스템 팩트

모든 테넌트는 계정 생성 시 3개의 시스템 팩트가 자동 등록됩니다. 수동 생성이 필요 없습니다:

| 키           | 타입           | 설명     |
| ----------- | ------------ | ------ |
| `user_id`   | STRING       | 사용자 ID |
| `email`     | STRING       | 이메일 주소 |
| `user_tags` | LIST\_STRING | 사용자 태그 |

각 팩트가 실제 실행에서 *필수*인지는 배포된 버전의 어떤 규칙이 그 팩트를 참조하느냐에 달려 있습니다. 정확히 어떤 팩트가 필요한지는 `GET /groups/{groupId}/requirements`를 호출해 확인하세요.

그 외 모든 팩트(예: `payment_amount`, `customer_tier`, `order_region`)는 사용자가 정의하며, 사용 전에 [팩트 정의](/ko/guides/fact-definitions)에서 등록해야 합니다.

***

## 오류 처리

모든 오류는 동일한 envelope 형태를 따릅니다:

```json theme={null}
{ "result": "ERROR", "code": "P-001", "message": "정책 그룹을 찾을 수 없습니다." }
```

실행 엔드포인트에서 가장 자주 마주치는 오류:

| HTTP | 코드      | 발생 조건                                |
| :--: | ------- | ------------------------------------ |
|  401 | `A-003` | `x-api-key` 헤더가 누락되거나 변조·폐기됨         |
|  404 | `P-001` | `groupId`가 존재하지 않거나 다른 tenant 소속     |
|  403 | `P-009` | 그룹은 존재하지만 `DISABLED` 상태              |
|  400 | `P-019` | 그룹에 배포된 버전이 없음 — 먼저 버전을 배포해야 함       |
|  404 | `P-002` | (특정 버전 모드) `versionId`가 존재하지 않음      |
|  400 | `P-016` | (특정 버전 모드) 버전이 대상 그룹에 속하지 않음         |
|  400 | `P-015` | 필수 입력 팩트 누락 — `/requirements`로 확인    |
|  400 | `C-001` | 요청 본문 형식 오류 또는 검증 실패                 |
|  400 | `P-003` | 평가 도중 액션 실행에서 오류 발생                  |
|  409 | `I-001` | `Idempotency-Key`로 이미 처리됨 (원본 응답 반환) |
|  409 | `I-002` | 동일 키 요청이 처리 중 — 잠시 후 재시도             |
|  403 | `B-008` | 현재 플랜의 월 실행 한도 도달                    |
|  429 | `C-007` | 플랜의 최대 TPS 초과                        |

전체 도메인(인증·빌링·시뮬레이션 등)에 걸친 코드 레지스트리는 [에러 레퍼런스](/ko/api/errors)를 참조하세요.

## 다음 단계

<CardGroup cols={2}>
  <Card title="드라이런" icon="flask-vial" href="/ko/guides/dry-run">
    라이브 전에 부작용 없이 규칙을 테스트합니다.
  </Card>

  <Card title="팩트 정의" icon="database" href="/ko/guides/fact-definitions">
    규칙에 사용할 커스텀 입력 변수를 등록합니다.
  </Card>
</CardGroup>
