> ## 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.

# Policy Execution

> Execute deployed policies against input facts in real time. LexQ provides 4 execution modes for different use cases.

## Base URL

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

<Warning>
  The Execution API base URL (`/api/v1/execution`) is different from the Management API (`/api/v1/partners`). Use the correct base URL for each.
</Warning>

## Authentication

Same API Key as the Management API. Include in every request:

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

## Execution Modes

LexQ provides 4 execution modes. Choose the one that fits your use case:

| Mode                 | Endpoint                                      | Use Case                                              |
| -------------------- | --------------------------------------------- | ----------------------------------------------------- |
| **Single Group**     | `POST /groups/{groupId}`                      | Standard single-order evaluation                      |
| **Specific Version** | `POST /groups/{groupId}/versions/{versionId}` | A/B testing, rollback testing                         |
| **Batch**            | `POST /groups/{groupId}/batch`                | Cart calculation, bulk product listing                |
| **Composite**        | `POST /composite`                             | Multi-group evaluation (discount + coupon + shipping) |

***

## Single Group Execution

Evaluates all active rules in the currently deployed version of a policy group.

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

### Request

```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"
    }
  }'
```

| Field     | Type   | Required | Description                                                     |
| --------- | ------ | :------: | --------------------------------------------------------------- |
| `facts`   | object |     ✅    | Input variables for rule evaluation                             |
| `context` | object |          | Additional metadata (not used in conditions, passed to actions) |

### Response

```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% Discount",
        "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% Discount",
        "policyGroupId": "01f2b274-...",
        "policyVersionId": "a6062090-...",
        "status": "SELECTED",
        "reasonCode": "FINAL_WINNER",
        "reasonDetail": null
      }
    ]
  }
}
```

`data.traceId` is the execution's audit handle — store it alongside your own records to trace this execution later.

The `data` object exposes three layers of state explicitly:

* `inputFacts` — facts as received from the request, normalized.
* `mutatedFacts` — only the facts whose values were changed by rule actions.
* `generatedVariables` — variables created by rule actions that did not exist in the input. Includes per-action change keys like `{refVar}__delta` (signed change from `MUTATE_FACT`) and `{targetVar}__delta` (non-negative increment from `INCREMENT_FACT`).

This split makes audits, replays, and debugging deterministic — you always know which value originated where. The merged "final state" can be reconstructed as `{ ...inputFacts, ...mutatedFacts, ...generatedVariables }`.

### Idempotency

Include an `Idempotency-Key` header to prevent duplicate executions. If a request with the same key has already been processed, the original response is returned without re-executing the policy.

***

## Specific Version Execution

Executes a specific version of a policy group, bypassing traffic routing. Useful for A/B testing a candidate version or testing a rollback target before switching.

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

Request and response formats are identical to Single Group Execution. The only difference is that the engine uses the specified version instead of the currently deployed one.

<Info>
  The version must be in `ACTIVE` (published) status. DRAFT versions cannot be executed via the Engine API — use [Dry Run](/guides/dry-run) for testing DRAFT versions.
</Info>

***

## Batch Execution

Executes multiple fact sets against the same policy group in a single call. All requests in the batch are evaluated against the same version for consistency.

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

### Request

```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"
    }
  }'
```

| Field           | Type   | Required | Description                       |
| --------------- | ------ | :------: | --------------------------------- |
| `requests`      | array  |     ✅    | List of fact sets to evaluate     |
| `sharedContext` | object |          | Context merged into every request |

### Response

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

Results are returned in the same order as the input requests. Each item in the context is merged with `sharedContext` (per-request context takes priority on conflict).

<Info>
  Batch execution counts as **1 API call** for TPS throttling, regardless of the number of items. However, billing is based on the total number of items.
</Info>

***

## Composite Execution

Evaluates the same facts against multiple policy groups in a single call. Useful when a transaction must pass through multiple policy checks simultaneously (e.g., product discount + cart coupon + shipping fee + membership points).

```
POST /composite
```

### Request

```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"
    }
  }'
```

| Field            | Type   | Required | Description                       |
| ---------------- | ------ | :------: | --------------------------------- |
| `targetGroupIds` | array  |     ✅    | Policy group IDs to evaluate      |
| `facts`          | object |     ✅    | Shared input facts for all groups |
| `context`        | object |          | Shared context for all groups     |

### Response

The response contains merged `inputFacts`, `mutatedFacts`, `generatedVariables`, `executionTraces`, and `decisionTraces` from all evaluated groups.

All target groups must be in `ACTIVE` status. If any group is `DISABLED` or not found, the entire request fails.

***

## Requirements

Check which input facts a deployed version requires before calling the execution endpoint. Returns required keys, types, and an auto-generated sample request body.

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

### Request

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

The `Accept-Language` header controls the language of the `usedBy` descriptions (`en` or `ko`).

### Response

```json theme={null}
{
  "result": "SUCCESS",
  "data": {
    "groupId": "01f2b274-...",
    "versionId": "a6062090-...",
    "versionNo": 5,
    "requiredFacts": [
      {
        "key": "payment_amount",
        "type": "NUMBER",
        "displayName": "Payment Amount",
        "usedBy": [
          "Condition [GREATER_THAN_OR_EQUAL] in rule: VIP 10% Discount",
          "MUTATE_FACT action in rule: VIP 10% Discount"
        ]
      },
      {
        "key": "customer_tier",
        "type": "STRING",
        "displayName": "Customer Tier",
        "usedBy": [
          "Condition [EQUALS] in rule: VIP 10% Discount"
        ]
      }
    ],
    "exampleRequest": {
      "facts": { "payment_amount": 50000, "customer_tier": "" },
      "context": { "reqTime": "2025-01-01T00:00:00Z" }
    }
  }
}
```

<Tip>
  Call this endpoint before your first integration to see exactly which facts you need to provide. Copy the `exampleRequest` as a starting template and fill in real values.
</Tip>

***

## System Facts

Every tenant gets 3 pre-registered system facts available immediately after account creation. You do not need to create these manually:

| Key         | Type         | Description   |
| ----------- | ------------ | ------------- |
| `user_id`   | STRING       | User ID       |
| `email`     | STRING       | Email Address |
| `user_tags` | LIST\_STRING | User Tags     |

Whether each fact is *required* for a particular execution depends on which rules in the deployed version reference it. Call `GET /groups/{groupId}/requirements` to see exactly which facts the engine expects.

All other facts (e.g., `payment_amount`, `customer_tier`, `order_region`) are user-defined and must be registered via [Fact Definitions](/guides/fact-definitions) before use.

***

## Error Handling

All errors follow the same envelope shape:

```json theme={null}
{ "result": "ERROR", "code": "P-001", "message": "Policy group not found." }
```

The most common errors you will encounter when calling execution endpoints:

| HTTP | Code    | When it happens                                                              |
| :--: | ------- | ---------------------------------------------------------------------------- |
|  401 | `A-003` | The `x-api-key` header is missing, malformed, or revoked                     |
|  404 | `P-001` | `groupId` does not exist or belongs to another tenant                        |
|  403 | `P-009` | Group exists but is in `DISABLED` status                                     |
|  400 | `P-019` | Group has no deployed version yet — deploy a version first                   |
|  404 | `P-002` | (Specific Version mode) `versionId` does not exist                           |
|  400 | `P-016` | (Specific Version mode) The version does not belong to the target group      |
|  400 | `P-015` | Required input facts missing — call `/requirements` to check                 |
|  400 | `C-001` | Request body malformed or fails validation                                   |
|  400 | `P-003` | Action execution raised an error during evaluation                           |
|  409 | `I-001` | `Idempotency-Key` already processed (original response is returned)          |
|  409 | `I-002` | A request with this `Idempotency-Key` is currently in flight — retry shortly |
|  403 | `B-008` | Monthly execution quota reached for the current plan                         |
|  429 | `C-007` | Request rate exceeded plan's Max TPS                                         |

For the complete code registry across all domains (auth, billing, simulation, etc.), see [Error Reference](/api/errors).

## Next Steps

<CardGroup cols={2}>
  <Card title="Dry Run" icon="flask-vial" href="/guides/dry-run">
    Test rules without side effects before going live.
  </Card>

  <Card title="Fact Definitions" icon="database" href="/guides/fact-definitions">
    Register custom input variables for your rules.
  </Card>
</CardGroup>
