{
  "openapi": "3.1.0",
  "info": {
    "title": "LicenChain Integrations API — target contract",
    "version": "1.0.0-target",
    "summary": "Seller-to-LicenChain licensing requests and server-side license verification.",
    "description": "This document describes the agreed **target** behavior of the LicenChain Integrations API so that\nimplementations can conform to it. Hosts shown here are illustrative (`.example`). Real Sandbox and\nProduction base URLs and API keys are issued during Seller onboarding.\n\nAPI identifiers keep the `licence` spelling (for example `licenceKey`, `licence.issued`) for\ncompatibility with existing issuance data. Prose uses US English “license”.\n\n**Authentication.** Seller → LicenChain operations use the server-side Seller API key\n(`sellerApiKey`, HTTP Bearer). LicenChain → Seller webhooks do **not** use that key: each delivery\nis authenticated by the `webhookSignature` scheme (`LC-Signature` HMAC-SHA256 over\n`<LC-Timestamp>.<raw request body>`), declared on every webhook operation.\n\n**License model.** Each licensing request (v1: one product) results in exactly **one** corporate\nlicense with one canonical `licenceKey`. The optional `quantity` is the seat or unit allowance\n*within* that license; it never creates one license per seat.",
    "contact": {
      "name": "LicenChain",
      "url": "https://licenchain.io/contact/"
    }
  },
  "servers": [
    {
      "url": "https://sandbox-api.licenchain.example",
      "description": "Illustrative Sandbox host. Your real base URL is issued during Seller onboarding."
    }
  ],
  "security": [
    {
      "sellerApiKey": []
    }
  ],
  "tags": [
    {
      "name": "Licensing requests",
      "description": "Submit approved order data after your own PO, contract or invoice process and receive a Buyer onboarding link."
    },
    {
      "name": "License verification",
      "description": "Check from your backend whether an issued license is currently valid before granting new access."
    }
  ],
  "paths": {
    "/api/integrations/v1/licensing-requests": {
      "post": {
        "tags": [
          "Licensing requests"
        ],
        "operationId": "createLicensingRequest",
        "summary": "Submit a licensing request",
        "description": "Creates a licensing request for one registered product and returns a single-use Buyer onboarding link.\nSubmit approved order **data**, not the PO document. Product terms are predefined on the registered\nproduct; the Buyer accepts the applicable terms version inside LicenChain.\n\nSend an `Idempotency-Key` with every request. Repeating the same key with the same body returns the\noriginal result with `200` and `Idempotent-Replayed: true`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LicensingRequestCreate"
              },
              "examples": {
                "singleProductOrder": {
                  "summary": "Single-product corporate order",
                  "value": {
                    "externalOrderId": "SELLER-PO-EXAMPLE-001",
                    "productRef": "ai-specialist-standard",
                    "buyer": {
                      "organizationName": "Example Research Cooperative",
                      "contactEmail": "licensing@example.com"
                    },
                    "price": {
                      "currency": "EUR",
                      "netLicensedAmount": "1200.00"
                    },
                    "validity": {
                      "validFrom": "2027-01-01T00:00:00Z",
                      "validUntil": "2028-01-01T00:00:00Z"
                    },
                    "quantity": 25
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay of a previous identical request. The original result is returned.",
            "headers": {
              "Idempotent-Replayed": {
                "description": "Always `true` on a replayed response.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              },
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicensingRequestCreated"
                },
                "examples": {
                  "replayed": {
                    "value": {
                      "schemaVersion": "1.0",
                      "requestId": "lreq_7Q2M9K4X8P3D",
                      "externalOrderId": "SELLER-PO-EXAMPLE-001",
                      "productRef": "ai-specialist-standard",
                      "termsVersion": "2026.2",
                      "status": "awaiting_buyer",
                      "onboarding": {
                        "url": "https://sandbox.licenchain.example/onboarding/REDACTED-ONE-TIME-TOKEN",
                        "expiresAt": "2026-12-08T09:00:00Z"
                      },
                      "createdAt": "2026-11-08T09:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Licensing request created. Distribute `onboarding.url` to the Buyer contact through your own channel.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicensingRequestCreated"
                },
                "examples": {
                  "created": {
                    "value": {
                      "schemaVersion": "1.0",
                      "requestId": "lreq_7Q2M9K4X8P3D",
                      "externalOrderId": "SELLER-PO-EXAMPLE-001",
                      "productRef": "ai-specialist-standard",
                      "termsVersion": "2026.2",
                      "status": "awaiting_buyer",
                      "onboarding": {
                        "url": "https://sandbox.licenchain.example/onboarding/REDACTED-ONE-TIME-TOKEN",
                        "expiresAt": "2026-12-08T09:00:00Z"
                      },
                      "createdAt": "2026-11-08T09:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed or fails validation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "validationFailed": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#validation_failed",
                      "title": "Request validation failed",
                      "status": 400,
                      "code": "validation_failed",
                      "detail": "One or more fields are invalid.",
                      "traceId": "trc_4H8K2M6Q9R",
                      "errors": [
                        {
                          "field": "price.netLicensedAmount",
                          "code": "invalid_decimal",
                          "message": "Use a decimal string with exactly two fraction digits, for example \"1200.00\"."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthenticated": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#unauthenticated",
                      "title": "Authentication required",
                      "status": 401,
                      "code": "unauthenticated",
                      "detail": "The API key is missing, malformed, expired or revoked.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The API key is valid but not permitted to submit licensing requests.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#forbidden",
                      "title": "Not permitted",
                      "status": 403,
                      "code": "forbidden",
                      "detail": "The API key is not permitted to perform this operation.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`productRef` does not identify a registered product of your Seller account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "productNotFound": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#product_not_found",
                      "title": "Product not found",
                      "status": 404,
                      "code": "product_not_found",
                      "detail": "No registered product with this productRef belongs to your Seller account.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Duplicate order or reused idempotency key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "duplicateExternalOrder": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#duplicate_external_order",
                      "title": "Order already submitted",
                      "status": 409,
                      "code": "duplicate_external_order",
                      "detail": "A licensing request for this externalOrderId already exists.",
                      "traceId": "trc_4H8K2M6Q9R",
                      "existingRequestId": "lreq_7Q2M9K4X8P3D"
                    }
                  },
                  "idempotencyKeyReused": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#idempotency_key_reused",
                      "title": "Idempotency key reused",
                      "status": 409,
                      "code": "idempotency_key_reused",
                      "detail": "This Idempotency-Key was already used with a different request body.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "code": "rate_limited",
                      "detail": "Retry after the number of seconds in the Retry-After header.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with the same Idempotency-Key.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unavailable": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#service_unavailable",
                      "title": "Service unavailable",
                      "status": 503,
                      "code": "service_unavailable",
                      "detail": "The service is temporarily unavailable. Retry with backoff.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/integrations/v1/licenses/verify": {
      "post": {
        "tags": [
          "License verification"
        ],
        "operationId": "verifyLicence",
        "summary": "Verify a license",
        "description": "Returns the current lifecycle state and issued-product, Buyer, holder and seat-limit references for a corporate licence identified by its canonical `licenceKey`. Only its authenticated Seller can query that licence.\n\n**A `200` response is not a grant of access.** Only `valid === true` satisfies the license half of\nyour authorization decision. Treat every other value, any non-`200` response, a timeout or a\nmalformed body as **deny new access**. User authentication and assignment stay in your own delivery system.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LicenceVerificationRequest"
              },
              "examples": {
                "nftBacked": {
                  "summary": "NFT-backed license key",
                  "value": {
                    "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The license was found. Inspect `valid`; `200` alone does not mean the license is active.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenceVerificationResult"
                },
                "examples": {
                  "active": {
                    "summary": "Active license (illustrative)",
                    "value": {
                      "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
                    }
                  },
                  "lifecycleStateMissing": {
                    "summary": "Fail closed: lifecycle state missing",
                    "value": {
                      "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
                    }
                  },
                  "expired": {
                    "summary": "Expired license",
                    "value": {
                      "schemaVersion": "1.0",
                      "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42",
                      "productRef": "ai-specialist-standard",
                      "buyerOrganizationId": "org_2B5D8F1H4K7M",
                      "licenceHolderId": "org_2B5D8F1H4K7M",
                      "seatLimit": 25,
                      "issued": true,
                      "valid": false,
                      "status": "expired",
                      "reason": "validity_period_ended",
                      "validFrom": "2027-01-01T00:00:00Z",
                      "validUntil": "2028-01-01T00:00:00Z",
                      "checkedAt": "2028-01-02T08:00:00Z",
                      "revision": 4
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The `licenceKey` is missing or malformed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "validationFailed": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#validation_failed",
                      "title": "Request validation failed",
                      "status": 400,
                      "code": "validation_failed",
                      "detail": "One or more fields are invalid.",
                      "traceId": "trc_4H8K2M6Q9R",
                      "errors": [
                        {
                          "field": "licenceKey",
                          "code": "invalid_format",
                          "message": "Provide the licenceKey exactly as delivered at issuance."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired or revoked API key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthenticated": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#unauthenticated",
                      "title": "Authentication required",
                      "status": 401,
                      "code": "unauthenticated",
                      "detail": "The API key is missing, malformed, expired or revoked.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The API key is valid but not permitted to verify licenses.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#forbidden",
                      "title": "Not permitted",
                      "status": 403,
                      "code": "forbidden",
                      "detail": "The API key is not permitted to perform this operation.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown license, or a license that belongs to another Seller. Deny access.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "licenceNotFound": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#licence_not_found",
                      "title": "License not found",
                      "status": 404,
                      "code": "licence_not_found",
                      "detail": "No license with this licenceKey is visible to your Seller account.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Deny new access until a successful check.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "code": "rate_limited",
                      "detail": "Retry after the number of seconds in the Retry-After header.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Verification unavailable. Deny new access; do not treat as valid.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unavailable": {
                    "value": {
                      "type": "https://developers.licenchain.io/api/docs/errors/#service_unavailable",
                      "title": "Service unavailable",
                      "status": 503,
                      "code": "service_unavailable",
                      "detail": "The service is temporarily unavailable. Retry with backoff.",
                      "traceId": "trc_4H8K2M6Q9R"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "onboardingCompleted": {
      "post": {
        "operationId": "onboardingCompleted",
        "summary": "onboarding.completed",
        "description": "Sent when the Buyer has accepted the applicable terms and completed onboarding. This is **not** license issuance; wait for `licence.issued` before storing a licenceKey. Authenticated by `LC-Signature`, not by the Seller API key.",
        "security": [
          {
            "webhookSignature": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardingCompletedEvent"
              },
              "examples": {
                "completed": {
                  "value": {
                    "schemaVersion": "1.0",
                    "eventId": "evt_5N1R8T2W6Y0B",
                    "type": "onboarding.completed",
                    "occurredAt": "2026-11-12T14:05:00Z",
                    "environment": "sandbox",
                    "externalOrderId": "SELLER-PO-EXAMPLE-001",
                    "requestId": "lreq_7Q2M9K4X8P3D",
                    "data": {
                      "onboardingStatus": "completed",
                      "sellerId": "sel_3J6K9M2P5R8T",
                      "productRef": "ai-specialist-standard",
                      "buyerOrganizationId": "org_2B5D8F1H4K7M",
                      "termsVersion": "2026.2",
                      "termsAcceptedAt": "2026-11-12T14:04:51Z",
                      "transactions": [
                        {
                          "transactionId": "txn_8F4D2K9Q1M7C"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged after the event is durably recorded (including duplicate deliveries of the same eventId)."
          },
          "401": {
            "description": "Your endpoint rejected the delivery because `LC-Signature` did not match or `LC-Timestamp` is outside the five-minute tolerance. Do not process the body."
          }
        }
      }
    },
    "licenceIssued": {
      "post": {
        "operationId": "licenceIssued",
        "summary": "licence.issued",
        "description": "Sent when the one corporate license for a licensing request has actually been issued. Carries the\ncanonical `licenceKey` to store and use for verification, the accepted terms version, the initial\nstatus, validity period, seat/unit allowance and references to the licensing request and onboarding\nacceptance. Identifiers only — no names or email addresses. Authenticated by `LC-Signature`, not by\nthe Seller API key.\n\nIn this contract version this event is the only way the `licenceKey` is delivered to the Seller.\nVerification cannot recover a missed issuance; see the Webhooks guide for reconciliation.",
        "security": [
          {
            "webhookSignature": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LicenceIssuedEvent"
              },
              "examples": {
                "issued": {
                  "value": {
                    "schemaVersion": "1.0",
                    "eventId": "evt_9C3V7X1Z5A2D",
                    "type": "licence.issued",
                    "occurredAt": "2026-11-12T14:20:00Z",
                    "environment": "sandbox",
                    "externalOrderId": "SELLER-PO-EXAMPLE-001",
                    "requestId": "lreq_7Q2M9K4X8P3D",
                    "data": {
                      "licenceKey": "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42",
                      "statusAtIssuance": "not_yet_valid",
                      "validFrom": "2027-01-01T00:00:00Z",
                      "validUntil": "2028-01-01T00:00:00Z",
                      "seatLimit": 25,
                      "sellerId": "sel_3J6K9M2P5R8T",
                      "productRef": "ai-specialist-standard",
                      "buyerOrganizationId": "org_2B5D8F1H4K7M",
                      "licenceHolderId": "org_2B5D8F1H4K7M",
                      "termsVersion": "2026.2",
                      "termsAcceptedAt": "2026-11-12T14:04:51Z",
                      "onboardingEventId": "evt_5N1R8T2W6Y0B",
                      "transactionId": "txn_8F4D2K9Q1M7C"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged after the event is durably recorded (including duplicate deliveries of the same eventId)."
          },
          "401": {
            "description": "Your endpoint rejected the delivery because `LC-Signature` did not match or `LC-Timestamp` is outside the five-minute tolerance. Do not process the body."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sellerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Seller- or marketplace-scoped server-side API key, sent as `Authorization: Bearer <key>`. The key alone determines the Seller; Seller or marketplace IDs in the body are never trusted for authorization. Never expose the key in browsers, mobile apps, logs or agent prompts. Applies to Seller → LicenChain operations only; webhooks use `webhookSignature`."
      },
      "webhookSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "LC-Signature",
        "description": "Authenticates LicenChain → Seller webhook deliveries. **Not** the Seller API key and not a Bearer token.\n\n1. Read `LC-Timestamp` (Unix seconds) and the **raw** request body bytes before any JSON parsing.\n2. Compute `HMAC-SHA256(webhookSigningSecret, \"<LC-Timestamp>.<raw request body>\")` and encode it as lowercase hex.\n3. Compare `v1=<hex>` with `LC-Signature` using a **constant-time** comparison.\n4. Reject the delivery if the signature does not match or `LC-Timestamp` differs from your clock by more than **five minutes** (300 seconds).\n5. De-duplicate by `LC-Event-Id`, which equals `eventId` in the body.\n\nThe webhook signing secret is issued per Seller endpoint during onboarding and is separate from the Seller API key.",
        "x-licenchain-signature": {
          "algorithm": "HMAC-SHA256",
          "signedPayload": "<LC-Timestamp>.<raw request body>",
          "encoding": "hex (lowercase)",
          "prefix": "v1=",
          "timestampHeader": "LC-Timestamp",
          "eventIdHeader": "LC-Event-Id",
          "toleranceSeconds": 300,
          "comparison": "constant-time"
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique key per logical order submission (UUID v4 recommended). Reuse the same key when retrying the same order. Keys are retained for at least 24 hours.",
        "schema": {
          "type": "string",
          "minLength": 16,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9_-]+$"
        },
        "example": "5d1f8a3c-2b7e-4c49-9f0a-6e3b2d4c8a17"
      },
      "WebhookTimestamp": {
        "name": "LC-Timestamp",
        "in": "header",
        "required": true,
        "description": "Unix time in seconds when the delivery was signed. Reject deliveries older than 5 minutes.",
        "schema": {
          "type": "string",
          "pattern": "^\\d{10}$"
        },
        "example": "1794492300"
      },
      "WebhookEventId": {
        "name": "LC-Event-Id",
        "in": "header",
        "required": true,
        "description": "Same value as `eventId` in the body. Use it to de-duplicate deliveries.",
        "schema": {
          "type": "string"
        },
        "example": "evt_5N1R8T2W6Y0B"
      },
      "WebhookSignature": {
        "name": "LC-Signature",
        "in": "header",
        "required": true,
        "description": "`v1=` followed by the lowercase hex HMAC-SHA256 of `<LC-Timestamp>.<raw request body>` using your webhook signing secret. Compare in constant time. This header carries the `webhookSignature` security scheme.",
        "schema": {
          "type": "string",
          "pattern": "^v1=[0-9a-f]{64}$"
        }
      }
    },
    "headers": {
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "NoStore": {
        "description": "Responses are not cacheable.",
        "schema": {
          "type": "string",
          "const": "no-store"
        }
      }
    },
    "schemas": {
      "UtcDateTime": {
        "type": "string",
        "format": "date-time",
        "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?Z$",
        "description": "RFC 3339 timestamp in UTC with a `Z` suffix. Offsets other than `Z` are rejected.",
        "examples": [
          "2027-01-01T00:00:00Z"
        ]
      },
      "LicenceKey": {
        "type": "string",
        "minLength": 1,
        "maxLength": 256,
        "pattern": "^\\S+$",
        "description": "Canonical license identifier delivered at issuance. Treat as opaque and store it exactly as received. For NFT-backed licenses it has the form `chainId:contractAddress:tokenId`.",
        "examples": [
          "11155111:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:42"
        ]
      },
      "Buyer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "organizationName",
          "contactEmail"
        ],
        "description": "Corporate Buyer. No named end users are required.",
        "properties": {
          "organizationName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "contactEmail": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Buyer contact who receives the onboarding link from the Seller. Treated as personal data."
          }
        }
      },
      "Price": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "currency",
          "netLicensedAmount"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "description": "ISO 4217 currency code.",
            "examples": [
              "EUR"
            ]
          },
          "netLicensedAmount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,11})\\.[0-9]{2}$",
            "description": "Net licensed amount **excluding VAT**, as a decimal string with exactly two fraction digits. Never send floating-point numbers.",
            "examples": [
              "1200.00"
            ]
          }
        }
      },
      "Validity": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "validFrom",
          "validUntil"
        ],
        "description": "License period in UTC. `validFrom` is inclusive, `validUntil` is exclusive and must be later than `validFrom`.",
        "properties": {
          "validFrom": {
            "$ref": "#/components/schemas/UtcDateTime"
          },
          "validUntil": {
            "$ref": "#/components/schemas/UtcDateTime"
          }
        }
      },
      "LicensingRequestCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "externalOrderId",
          "productRef",
          "buyer",
          "price",
          "validity"
        ],
        "description": "One licensing request for one registered product. Each request results in exactly one corporate license (one `licenceKey`) for the Buyer organization.",
        "properties": {
          "externalOrderId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9._:/-]+$",
            "description": "Your PO, contract or order reference, unique per Seller. Used for traceability, duplicate detection and reconciliation. In v1 one order maps to one product and one request; a future multi-line model would add an optional line reference without changing this field."
          },
          "productRef": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-z0-9][a-z0-9-]*$",
            "description": "Reference of a product registered on your Seller account. Its predefined terms apply."
          },
          "buyer": {
            "$ref": "#/components/schemas/Buyer"
          },
          "price": {
            "$ref": "#/components/schemas/Price"
          },
          "validity": {
            "$ref": "#/components/schemas/Validity"
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "description": "Optional seat or unit allowance **within the single corporate license** issued for this request, for example the number of named users the Buyer may assign. It does **not** create multiple licenses: the request always results in one license and one `licenceKey`. Omit it for products without a seat or unit allowance. Recorded on the license and returned as `seatLimit` in `licence.issued`. LicenChain derives no amount from it; `price.netLicensedAmount` is the net amount for the whole license."
          }
        }
      },
      "LicensingRequestCreated": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "externalOrderId",
          "productRef",
          "termsVersion",
          "status",
          "onboarding",
          "createdAt"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "requestId": {
            "type": "string",
            "pattern": "^lreq_[A-Za-z0-9]+$",
            "description": "LicenChain licensing request identifier."
          },
          "externalOrderId": {
            "type": "string"
          },
          "productRef": {
            "type": "string"
          },
          "termsVersion": {
            "type": "string",
            "description": "Product terms version the Buyer will be asked to accept."
          },
          "status": {
            "type": "string",
            "enum": [
              "awaiting_buyer",
              "onboarding_completed",
              "issued",
              "expired",
              "cancelled"
            ],
            "description": "Current state of the licensing request (not of the license)."
          },
          "onboarding": {
            "$ref": "#/components/schemas/Onboarding"
          },
          "createdAt": {
            "$ref": "#/components/schemas/UtcDateTime"
          }
        }
      },
      "Onboarding": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "expiresAt"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "Single-use Buyer onboarding link. It is a bearer secret: send it only to the Buyer contact, never log it or expose it in analytics."
          },
          "expiresAt": {
            "$ref": "#/components/schemas/UtcDateTime"
          }
        }
      },
      "LicenceVerificationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "licenceKey"
        ],
        "properties": {
          "licenceKey": {
            "$ref": "#/components/schemas/LicenceKey"
          }
        }
      },
      "LicenceVerificationResult": {
        "type": "object",
        "additionalProperties": false,
        "description": "Current state of a licence belonging to the authenticated Seller. Product, Buyer, holder and seat-allowance references come from the issued licence record, not from the caller. They do not override a fail-closed valid: false decision.",
        "required": [
          "schemaVersion",
          "licenceKey",
          "productRef",
          "buyerOrganizationId",
          "licenceHolderId",
          "seatLimit",
          "issued",
          "valid",
          "status",
          "reason",
          "validFrom",
          "validUntil",
          "checkedAt",
          "revision"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "licenceKey": {
            "$ref": "#/components/schemas/LicenceKey"
          },
          "productRef": {
            "type": "string",
            "minLength": 1,
            "description": "Registered product associated with the issued corporate licence; from issuance evidence, not a caller-supplied value."
          },
          "buyerOrganizationId": {
            "$ref": "#/components/schemas/OrganizationId",
            "description": "LicenChain ID of the purchasing Buyer organization. Returned only to its authorized Seller."
          },
          "licenceHolderId": {
            "$ref": "#/components/schemas/OrganizationId",
            "description": "LicenChain ID of the organization holding the rights. May differ from the Buyer; returned only to its authorized Seller."
          },
          "seatLimit": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Seat or unit allowance within this one corporate licence, or null for offerings without an allowance. Enforced by the Seller delivery system, not by this verification result."
          },
          "issued": {
            "type": "boolean",
            "description": "Issuance evidence exists for this license. Not the same as currently valid."
          },
          "valid": {
            "type": "boolean",
            "description": "The only field that may permit the license half of an authorization decision. `true` only for `status: active` within the validity period."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "not_yet_valid",
              "expired",
              "suspended",
              "revoked",
              "unverified"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              null,
              "lifecycle_state_missing",
              "validity_period_not_started",
              "validity_period_ended",
              "suspended",
              "revoked"
            ],
            "description": "Machine-readable reason when `valid` is `false`; `null` when valid. New values may be added: treat unknown values as deny."
          },
          "validFrom": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/UtcDateTime"
              },
              {
                "type": "null"
              }
            ]
          },
          "validUntil": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/UtcDateTime"
              },
              {
                "type": "null"
              }
            ]
          },
          "checkedAt": {
            "$ref": "#/components/schemas/UtcDateTime"
          },
          "revision": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Monotonic lifecycle revision. Use it to ignore out-of-order cached results."
          }
        }
      },
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "description": "RFC 9457 problem details. Never contains API keys, onboarding tokens or Buyer personal data.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "code": {
            "type": "string",
            "enum": [
              "validation_failed",
              "unauthenticated",
              "forbidden",
              "product_not_found",
              "licence_not_found",
              "duplicate_external_order",
              "idempotency_key_reused",
              "rate_limited",
              "service_unavailable"
            ]
          },
          "detail": {
            "type": "string"
          },
          "traceId": {
            "type": "string",
            "description": "Quote this value when contacting LicenChain support."
          },
          "existingRequestId": {
            "type": "string",
            "description": "Present on `duplicate_external_order`."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldError"
            }
          }
        }
      },
      "FieldError": {
        "type": "object",
        "required": [
          "field",
          "code",
          "message"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "Dot-separated path of the invalid field."
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "EventEnvelope": {
        "type": "object",
        "required": [
          "schemaVersion",
          "eventId",
          "type",
          "occurredAt",
          "environment",
          "externalOrderId",
          "requestId",
          "data"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "eventId": {
            "type": "string",
            "pattern": "^evt_[A-Za-z0-9]+$"
          },
          "type": {
            "type": "string"
          },
          "occurredAt": {
            "$ref": "#/components/schemas/UtcDateTime"
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "production"
            ]
          },
          "externalOrderId": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "data": {
            "type": "object"
          }
        }
      },
      "OnboardingCompletedEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventEnvelope"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "onboarding.completed"
              },
              "data": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "onboardingStatus",
                  "sellerId",
                  "productRef",
                  "buyerOrganizationId",
                  "termsVersion",
                  "termsAcceptedAt",
                  "transactions"
                ],
                "properties": {
                  "onboardingStatus": {
                    "type": "string",
                    "const": "completed"
                  },
                  "sellerId": {
                    "$ref": "#/components/schemas/SellerId"
                  },
                  "productRef": {
                    "type": "string"
                  },
                  "buyerOrganizationId": {
                    "$ref": "#/components/schemas/OrganizationId"
                  },
                  "termsVersion": {
                    "type": "string",
                    "description": "Product terms version the Buyer accepted."
                  },
                  "termsAcceptedAt": {
                    "$ref": "#/components/schemas/UtcDateTime"
                  },
                  "transactions": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "transactionId"
                      ],
                      "properties": {
                        "transactionId": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "LicenceIssuedEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventEnvelope"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "licence.issued"
              },
              "data": {
                "type": "object",
                "additionalProperties": false,
                "description": "Issuance record of the one corporate license created for this licensing request. `externalOrderId` and `requestId` in the envelope link it to the original request; `onboardingEventId`, `termsVersion` and `termsAcceptedAt` link it to the onboarding acceptance.",
                "required": [
                  "licenceKey",
                  "statusAtIssuance",
                  "validFrom",
                  "validUntil",
                  "seatLimit",
                  "sellerId",
                  "productRef",
                  "buyerOrganizationId",
                  "licenceHolderId",
                  "termsVersion",
                  "termsAcceptedAt",
                  "onboardingEventId",
                  "transactionId"
                ],
                "properties": {
                  "licenceKey": {
                    "$ref": "#/components/schemas/LicenceKey"
                  },
                  "statusAtIssuance": {
                    "type": "string",
                    "enum": [
                      "active",
                      "not_yet_valid"
                    ],
                    "description": "Initial license state at issuance: `active` if `validFrom` has passed, otherwise `not_yet_valid`. A snapshot only — the current state comes from Verify license."
                  },
                  "validFrom": {
                    "$ref": "#/components/schemas/UtcDateTime"
                  },
                  "validUntil": {
                    "$ref": "#/components/schemas/UtcDateTime"
                  },
                  "seatLimit": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "description": "Seat or unit allowance recorded on this one license (the request `quantity`), or `null` when the product has no allowance. Enforcing it against named users is the job of your delivery system."
                  },
                  "sellerId": {
                    "$ref": "#/components/schemas/SellerId"
                  },
                  "productRef": {
                    "type": "string"
                  },
                  "buyerOrganizationId": {
                    "$ref": "#/components/schemas/OrganizationId",
                    "description": "Organization that bought the license (the Buyer)."
                  },
                  "licenceHolderId": {
                    "$ref": "#/components/schemas/OrganizationId",
                    "description": "Organization assigned the rights in this license record. May differ from `buyerOrganizationId`; never assume they are equal."
                  },
                  "termsVersion": {
                    "type": "string",
                    "description": "Product terms version the Buyer accepted. Equals `termsVersion` in `onboarding.completed`."
                  },
                  "termsAcceptedAt": {
                    "$ref": "#/components/schemas/UtcDateTime"
                  },
                  "onboardingEventId": {
                    "type": "string",
                    "pattern": "^evt_[A-Za-z0-9]+$",
                    "description": "`eventId` of the `onboarding.completed` event that recorded the acceptance."
                  },
                  "transactionId": {
                    "type": "string",
                    "description": "LicenChain support reference. Not a license identifier."
                  }
                }
              }
            }
          }
        ]
      },
      "SellerId": {
        "type": "string",
        "pattern": "^sel_[A-Za-z0-9]+$",
        "description": "LicenChain Seller account identifier. Informational in events; never accepted as an authorization input."
      },
      "OrganizationId": {
        "type": "string",
        "pattern": "^org_[A-Za-z0-9]+$",
        "description": "LicenChain organization identifier. Events use identifiers instead of names or email addresses."
      }
    }
  }
}
