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

# POST /licenses/validate — Validate a Software License

> Validate a software license by ID, product ID, and hardware identifier. Returns valid: true or false. Rate limited to 30 requests per minute per IP.

This is the core public API your application calls at runtime to check whether a license is currently valid. You supply the license ID, the product the license should be associated with, and the hardware identifier of the machine requesting access. Avalex runs a sequence of checks and returns a single `valid` boolean — no authentication or API key is required, making it straightforward to integrate directly into your product's startup or entitlement logic.

```http theme={null}
POST https://api.avalex.com/licenses/validate
```

<Note>
  This is a public endpoint. No authentication header is required to call it.
</Note>

<Warning>
  This endpoint is **rate limited to 30 requests per minute per IP address**. Exceeding this limit returns a `429 Too Many Requests` response. Design your integration to avoid polling this endpoint in tight loops; see the caching tip below.
</Warning>

## Request Body

<ParamField body="licenseId" type="string" required>
  The unique identifier of the license to validate, for example `LIC-9981-A2`.
</ParamField>

<ParamField body="productId" type="string" required>
  The product identifier that the license must be associated with. If the license belongs to a different product, validation will return `valid: false`.
</ParamField>

<ParamField body="hwid" type="string" required>
  The hardware identifier of the machine making the validation request. Avalex uses this value to enforce per-seat HWID binding on licenses that have a hardware slot limit configured.
</ParamField>

### Request Example

```bash theme={null}
curl -X POST https://api.avalex.com/licenses/validate \
  -H "Content-Type: application/json" \
  -d '{
    "licenseId": "LIC-9981-A2",
    "productId": "prod_enterprise_2026",
    "hwid": "HWID-TEST-MACHINE-01"
  }'
```

## Response — 200 OK

<ResponseField name="valid" type="boolean">
  `true` if the license passes all validation checks; `false` if any single check fails. See [Validation Logic](#validation-logic) below for the full sequence of checks performed.
</ResponseField>

```json theme={null}
{"valid": true}
```

## Validation Logic

Avalex evaluates the following checks **in order**. If any check fails, the response is `{"valid": false}` and no further checks are performed.

| Step | Check                   | Fails when                                                                                   |
| ---- | ----------------------- | -------------------------------------------------------------------------------------------- |
| 1    | **Product ID match**    | The license's `productId` does not match the `productId` you supplied in the request.        |
| 2    | **Expiry**              | The current server time is at or past the license's `expiresAtEpochMillis` value.            |
| 3    | **IP slot available**   | The requesting IP address is not already bound to this license, and no free IP slots remain. |
| 4    | **HWID slot available** | The provided `hwid` is not already bound to this license, and no free HWID slots remain.     |

Steps 3 and 4 only consume a slot on the **first** time a new IP address or hardware ID is seen. Subsequent requests from an already-bound IP or HWID do not consume additional slots.

## Error Responses

| Status                  | Body                                                            | Description                                                                                                |
| ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `429 Too Many Requests` | `{"error": "Too many validation attempts. Try again shortly."}` | Your IP address has exceeded 30 validation requests in the current 60-second window. Wait before retrying. |

## Notes

<Note>
  A `valid: false` response is a **legitimate validation outcome**, not an error condition. Your application should explicitly handle both `true` and `false` values rather than treating only `false` as an unexpected state.
</Note>

<Note>
  All validation attempts — both successful and failed — are recorded by Avalex and are visible in the Admin Portal under **Validation Attempts**. Use this log to audit usage patterns or investigate unexpected failures.
</Note>

<Tip>
  To support offline usage and reduce pressure on the rate limit, cache the last `valid: true` response locally with a grace period of **24–72 hours**. If Avalex is unreachable, your application can fall back to the cached result within the grace window before requiring a fresh check.
</Tip>
