# Verify a license (target contract)

> Documentation Preview — target API contract; endpoints may not be operational.

`POST /api/integrations/v1/licenses/verify`

Call from the Seller backend before granting **new** access. Identify the license with the canonical
`licenceKey` delivered at issuance (`licence.issued` event). For NFT-backed licenses it is
`chainId:contractAddress:tokenId`. Do not invent another license ID.

## Decision rule

- A `200` response is **not** a grant of access.
- Only `valid === true` satisfies the license half of authorization.
- Deny new access for any other value, any non-200 status, timeouts, malformed bodies and unknown
  `status`/`reason` values (absent, unknown, suspended, revoked, expired, not yet valid, unverified,
  verification unavailable).
- User authentication, named-user mapping and assignment stay in the Seller's own delivery system.

## Request

```json
{
  "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42"
}
```

## Response 200 — valid (illustrative)

```json
{
  "schemaVersion": "1.0",
  "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42",
  "productRef": "ai-specialist-standard",
  "buyerOrganizationId": "org_2B5D8F1H4K7M",
  "licenceHolderId": "org_2B5D8F1H4K7M",
  "seatLimit": 25,
  "issued": true,
  "valid": true,
  "status": "active",
  "reason": null,
  "validFrom": "2027-01-01T00:00:00Z",
  "validUntil": "2028-01-01T00:00:00Z",
  "checkedAt": "2027-03-15T10:30:00Z",
  "revision": 3
}
```

## Response 200 — fail closed

```json
{
  "schemaVersion": "1.0",
  "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42",
  "productRef": "ai-specialist-standard",
  "buyerOrganizationId": "org_2B5D8F1H4K7M",
  "licenceHolderId": "org_2B5D8F1H4K7M",
  "seatLimit": 25,
  "issued": true,
  "valid": false,
  "status": "unverified",
  "reason": "lifecycle_state_missing",
  "validFrom": null,
  "validUntil": null,
  "checkedAt": "2027-03-15T10:30:00Z",
  "revision": null
}
```

## Fields

- `valid`: the only field that may permit access (license half).
- `issued`: issuance evidence exists; not the same as currently valid.
- `productRef`: registered product from the issued licence.
- `buyerOrganizationId` / `licenceHolderId`: Buyer and rights-holder organization references;
  they may differ and are returned only within the authenticated Seller's scope.
- `seatLimit`: seat/unit allowance within one corporate licence, or null when not applicable.
  Your delivery system enforces seats and user assignments.
- `status`: active | not_yet_valid | expired | suspended | revoked | unverified.
- `reason`: null when valid; otherwise a machine-readable reason. Treat unknown values as deny.
- `validFrom` / `validUntil`: license period in UTC (null if unknown).
- `checkedAt`: when LicenChain evaluated the state.
- `revision`: monotonic lifecycle revision (null if unknown).

No propagation time is promised for lifecycle changes.

## cURL

```bash
curl --request POST "$LICENCHAIN_API_BASE_URL/api/integrations/v1/licenses/verify" \
  --header "Authorization: Bearer $LICENCHAIN_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
  "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42"
}'
```

## TypeScript

```ts
// Server-side only. Returns true only when LicenChain reports valid === true.
// Every other outcome (non-200, timeout, malformed body, valid !== true) denies new access.
const baseUrl = process.env.LICENCHAIN_API_BASE_URL;
const apiKey = process.env.LICENCHAIN_API_KEY;

export async function isLicenceValid(licenceKey: string): Promise<boolean> {
  const payload = { licenceKey };
  try {
    const response = await fetch(`${baseUrl}/api/integrations/v1/licenses/verify`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(payload),
      signal: AbortSignal.timeout(5000),
    });
    if (response.status !== 200) return false;
    const result: { valid?: unknown } = await response.json();
    return result.valid === true;
  } catch {
    return false;
  }
}

// Example: the licenceKey you stored from the licence.issued event.
const allowed = await isLicenceValid('11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42');
```
