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

# Validate

> Validate a license key against a product using POST /api/v1/validate.

Send a `POST` request to validate a license key. This is the primary endpoint you call each time your application needs to check whether a license is active and eligible to run.

<Tip>
  Use the interactive playground on this page to paste your product API key, send a real request, and inspect the live response without leaving the docs.
</Tip>

## Endpoint

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
POST https://api.keyport.sbs/api/v1/validate
```

## Headers

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
Authorization: Bearer kp_live_xxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
```

## Request body

<ParamField body="license_key" type="string" required>
  The license key to validate. Must match a key issued under the product associated with your API key.
</ParamField>

<ParamField body="version" type="string">
  The version string of your application (e.g. `"2.1.0"`). Required if you want the `version_valid` field populated. Only evaluated when the version module is enabled on the product.
</ParamField>

<ParamField body="device_id" type="string">
  An arbitrary identifier for the device or machine making the request. Used for informational purposes; does not affect validation outcome.
</ParamField>

## Response fields

<ResponseField name="valid" type="boolean" required>
  `true` if the license is currently valid and all checks passed. `false` for any failure, including business-state failures that return HTTP 200.
</ResponseField>

<ResponseField name="status" type="string" required>
  Machine-readable result string. Use this as the source of truth in your application logic. See [status values](#status-values) below.
</ResponseField>

<ResponseField name="expires" type="string | null">
  ISO 8601 expiry timestamp for the license. `null` for lifetime licenses or when the license was not found.
</ResponseField>

<ResponseField name="ip_registered" type="boolean | null">
  Whether the calling IP was newly registered during this request. `null` when the IP system is disabled or the license was invalid.
</ResponseField>

<ResponseField name="ip_count" type="number | null">
  Number of unique IPs currently registered to this license. `null` when the license was not found or the IP system is disabled.
</ResponseField>

<ResponseField name="max_ips" type="number | null">
  Maximum number of unique IPs allowed for this license. `null` when the IP system is disabled or the license was not found.
</ResponseField>

<ResponseField name="version" type="string | null">
  The version string accepted or pinned for this license. Only populated when the version module is enabled on the product; otherwise `null`.
</ResponseField>

<ResponseField name="version_valid" type="boolean | null">
  Whether the `version` you submitted is permitted for this license. Only populated when the version module is enabled and a `version` was included in the request; otherwise `null`.
</ResponseField>

<ResponseField name="custom" type="object | null">
  Custom response payload defined by the product's custom response module. Only populated when the module is enabled and the template resolves to a non-empty object; otherwise `null`.
</ResponseField>

<ResponseField name="signature" type="string | null">
  Cryptographic signature of the response payload. Only populated when signed validation is enabled at both the product and organization level; otherwise `null`.
</ResponseField>

## Examples

<CodeGroup>
  ```bash Success theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --request POST \
    --url https://api.keyport.sbs/api/v1/validate \
    --header 'Authorization: Bearer kp_live_xxxxxxxxxxxxxxxxxxxxxxxxx' \
    --header 'Content-Type: application/json' \
    --data '{
      "license_key": "ABCD-EFGH-IJKL-MNOP",
      "version": "2.1.0",
      "device_id": "desktop-main"
    }'
  ```

  ```json Success response theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "valid": true,
    "status": "active",
    "expires": "2027-01-01T00:00:00.000Z",
    "ip_registered": false,
    "ip_count": 1,
    "max_ips": 1,
    "version": null,
    "version_valid": null,
    "custom": null,
    "signature": null
  }
  ```

  ```json Failure response theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "valid": false,
    "status": "expired",
    "expires": null,
    "ip_registered": false,
    "ip_count": null,
    "max_ips": null,
    "version": null,
    "version_valid": null,
    "custom": null,
    "signature": null
  }
  ```
</CodeGroup>

## Status values

Most failures return HTTP 200 with `valid: false`. Read the `status` field to determine the cause.

| Status                | Meaning                                                              |
| --------------------- | -------------------------------------------------------------------- |
| `active`              | License is valid and all checks passed                               |
| `license_not_found`   | No license exists for the provided key under this product            |
| `revoked`             | License has been manually revoked                                    |
| `expired`             | License expiry date has passed                                       |
| `ip_blocked`          | The calling IP is explicitly blocked                                 |
| `ip_limit_reached`    | The license has already registered its maximum number of unique IPs  |
| `ip_not_registered`   | IP system is in allow-list mode and this IP is not registered        |
| `product_not_found`   | The product associated with your API key does not exist              |
| `product_archived`    | The product has been archived and is no longer accepting validations |
| `product_disabled`    | The product has been disabled                                        |
| `org_suspended`       | The organization account has been suspended                          |
| `billing_suspended`   | The organization's billing is suspended                              |
| `invalid_api_key`     | The `Authorization` header is missing or the API key is not valid    |
| `rate_limit_exceeded` | The daily validation limit for this product has been exceeded        |

## HTTP status codes

| HTTP status | Meaning                                                               |
| ----------- | --------------------------------------------------------------------- |
| `200`       | Request was processed. Check `valid` and `status` for the outcome.    |
| `401`       | Missing or invalid product API key.                                   |
| `429`       | Daily product request limit exceeded. Check the `Retry-After` header. |

<Note>
  `401` and `429` are the only HTTP error codes this endpoint returns. All other failure outcomes — including revoked, expired, and blocked licenses — come back as `200` with `valid: false`.
</Note>

## Field population notes

* **`signature`** — Only present when signed validation is enabled at both the product and organization level.
* **`custom`** — Only present when the product's custom response module is enabled and the template or override resolves to a non-empty object.
* **`version` and `version_valid`** — Only present when the version module is enabled on the product.


## OpenAPI

````yaml POST /api/v1/validate
openapi: 3.0.3
info:
  title: KeyPort Public API
  version: v1
  description: Public license validation and license lookup endpoints for KeyPort.
servers:
  - url: https://api.keyport.sbs
    description: Production API
security:
  - bearerAuth: []
paths:
  /api/v1/validate:
    post:
      summary: Validate a license
      operationId: validateLicense
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateRequest'
      responses:
        '200':
          description: Validation processed.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ValidateSuccess'
                  - $ref: '#/components/schemas/ValidateFailure'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedError'
        '429':
          description: Daily limit exceeded.
          headers:
            Retry-After:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
components:
  schemas:
    ValidateRequest:
      type: object
      required:
        - license_key
      properties:
        license_key:
          type: string
          example: ABCD-EFGH-IJKL-MNOP
        version:
          type: string
          example: 2.1.0
        device_id:
          type: string
          example: desktop-main
    ValidateSuccess:
      type: object
      properties:
        valid:
          type: boolean
          example: true
        status:
          type: string
          example: active
        expires:
          type: string
          format: date-time
          nullable: true
          example: '2027-01-01T00:00:00.000Z'
        ip_registered:
          type: boolean
          nullable: true
          example: false
        ip_count:
          type: integer
          nullable: true
          example: 1
        max_ips:
          type: integer
          nullable: true
          example: 1
        version:
          type: string
          nullable: true
          example: 2.1.0
        version_valid:
          type: boolean
          nullable: true
          example: true
        custom:
          type: object
          nullable: true
          additionalProperties: true
          example:
            tier: pro
            exports: true
        signature:
          type: string
          nullable: true
          example: null
    ValidateFailure:
      type: object
      properties:
        valid:
          type: boolean
          example: false
        status:
          type: string
          example: expired
        expires:
          type: string
          format: date-time
          nullable: true
          example: null
        ip_registered:
          type: boolean
          nullable: true
          example: false
        ip_count:
          type: integer
          nullable: true
          example: null
        max_ips:
          type: integer
          nullable: true
          example: null
        version:
          type: string
          nullable: true
          example: null
        version_valid:
          type: boolean
          nullable: true
          example: null
        custom:
          type: object
          nullable: true
          additionalProperties: true
          example: null
        signature:
          type: string
          nullable: true
          example: null
    UnauthorizedError:
      type: object
      properties:
        valid:
          type: boolean
          example: false
        status:
          type: string
          example: invalid_api_key
        message:
          type: string
          example: >-
            Missing Authorization header. Use: Authorization: Bearer
            YOUR_API_KEY
    RateLimitError:
      type: object
      properties:
        valid:
          type: boolean
          example: false
        status:
          type: string
          example: rate_limit_exceeded
        message:
          type: string
          example: >-
            This license provider or organization owner has reached the daily
            API request limit for their current plan. Please ask them to upgrade
            to a paid plan for higher limits, or try again after midnight UTC.
        retry_after:
          type: integer
          example: 3600
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: Use your product API key in the Authorization header.

````