Create and Assign Custom Licenses
JFrog Catalog allows you to define custom licenses and assign them to packages or package versions. Custom licenses formalize internal legal frameworks, proprietary agreements, or niche third-party licenses that standard scanners don't detect.
Once assigned, custom licenses are enforced by both Curation and Xray license-based policies, accurately reflected in generated SBOMs, and queryable through the GraphQL and REST APIs.
Before You Begin
You need the following:
- Admin permissions in the JFrog Platform.
- Access to JFrog Catalog.
- At least one curated remote repository configured (for Curation policy enforcement).
Manage Custom Licenses
A centralized management screen is available under Catalog > License Settings. The list view displays all custom licenses with the following details:
- Name
- Created date
- Creator
- Last modified
- Expiration date
- Associated package count
From this screen, you can create, edit, and delete custom licenses, and assign a single license to multiple packages or versions (Global Flow).
Create a Custom License
To create a custom license:
- Navigate to Catalog > License Settings.
- Click Create License.
- Fill in the following fields:
| Field | Required | Description |
|---|---|---|
| Name | Yes | Unique identifier for the license. Supports special characters per SPDX expressions. |
| Full Text | Yes | The full legal agreement text. Supports rich text and Markdown. |
| Category | Yes | Select from the predefined dropdown list (for example, Permissive, Copyleft, Proprietary). |
| Expiration Date | No | Optional date for temporal entitlements. After expiration, the license is no longer enforced in policy evaluations. |
- Click Create.
Note
License names must be unique. You can't create a license with a name that already exists.
Assign a Custom License to a Package or Version
There are two workflows for assigning custom licenses to packages.
Global Flow (License Settings Page)
Use this flow to assign a single license to multiple packages or versions at once.
- Navigate to Catalog > License Settings.
- Select the license you want to assign.
- Click Assign to Packages.
- Search and select the target packages or versions.
- Click Assign.
Package Flow (Package Version Page)
Use this flow to assign a license to a specific package version, a range of versions, or all versions.
- Navigate to the package detail page in JFrog Catalog.
- Open the Licenses tab.
- Click Assign Custom License.
- Select the custom license from the list.
- Choose the assignment scope:
- A specific version.
- A range of versions.
- All versions.
- Choose the assignment mode:
- Add — The custom license coexists with detected public licenses.
- Replace — The custom license overrides the detected public license.
- Click Assign.
Note
If you replace a public license, the UI still shows an indication that the original license was detected but has been overridden. You can't assign the same license to a package twice.
License Expressions
When you assign multiple licenses to a single package, you must define the relationship between them using a license expression:
- And — The package is governed by all assigned licenses simultaneously.
- Or — The package is governed by any one of the assigned licenses.
Expiration and Notifications
Custom licenses support an optional expiration date for temporal entitlements. When a license approaches its expiration:
- Visual warnings appear at 90, 60, 30, and 10 days before the expiration date.
- Expiring licenses are highlighted in the License Settings page and on the package Licenses tab.
When a license expires:
- It's no longer enforced in Curation or Xray policy evaluations.
- It remains visible in the Catalog for audit purposes but is marked as expired.
Policy Enforcement
Custom licenses are evaluated by both Curation and Xray license-based policies.
Evaluation Logic
When a package enters the system or is scanned:
- The system checks the Catalog for a custom license assignment.
- If a custom license exists, it's used for policy evaluation (respecting the Add or Replace mode).
- If no custom license is assigned, the standard detected OSS license is used.
Curation Policies
Custom licenses appear in the license selection dropdown when configuring Block list by License or Allow list by License conditions. For more information, see List of Available Conditions.
Xray Policies
Custom licenses are also available in Xray policy wizards under the License criteria. When a custom license is assigned with Replace mode, Xray uses the custom license instead of the detected license for violation checks.
SBOM Consistency
Custom licenses assigned in the Catalog are reflected in SBOMs generated by Xray:
- Add mode — The custom license appears alongside the detected license in the SBOM.
- Replace mode — The custom license replaces the detected license in the SBOM output.
Changes made in the Catalog propagate to Xray and are reflected in subsequent SBOM exports.
REST API
You can manage custom licenses programmatically using the REST API.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
GET | /api/v1/catalog/licenses | List all custom licenses | View |
GET | /api/v1/catalog/licenses/:name | Get a specific license by name | View |
POST | /api/v1/catalog/licenses | Create a new custom license | Admin |
PUT | /api/v1/catalog/licenses/:name | Update an existing license | Admin |
DELETE | /api/v1/catalog/licenses/:name | Delete a license (fails if assigned to packages) | Admin |
Example: Create a Custom License
curl -X POST \
'https://<JFROG_URL>/api/v1/catalog/licenses' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"name": "My-Internal-License",
"license_text": "Full license agreement text...",
"category": "Proprietary"
}'Example: List All Custom Licenses
curl -X GET \
'https://<JFROG_URL>/api/v1/catalog/licenses' \
-H 'Authorization: Bearer <TOKEN>'GraphQL API
You can query and manage custom licenses using the Catalog GraphQL API.
Search Custom Licenses
{
customLegalInfo {
searchCustomAssignedLicenses(
first: 10
where: { nameContainsFold: "internal" }
orderBy: { field: NAME, direction: ASC }
) {
edges {
node {
name
expiresAt
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
}Get Custom Licenses for a Package
{
publicPackage {
getPackage(name: "my_package", type: "npm") {
name
type
customLegalInfo {
customAssignedLicensesConnection(first: 10) {
edges {
node {
name
expiresAt
}
}
}
}
}
}
}Get Custom Licenses for a Package Version
{
publicPackageVersion {
getVersion(name: "my_package", type: "npm", version: "1.0.0") {
version
customLegalInfo {
customAssignedLicensesConnection(first: 10) {
edges {
node {
name
expiresAt
}
}
}
}
}
}
}Create a Custom License
mutation {
userDefinedLicense {
createUserDefinedLicense(
input: {
name: "My-Internal-License"
licenseText: "Full license agreement text..."
category: "Proprietary"
}
) {
name
licenseText
category
}
}
}Assign a Custom License to a Package Version
mutation {
userDefinedLicense {
assignUserDefinedLicenseToPackageVersion(
input: {
licenseName: "My-Internal-License"
packageVersion: {
publicPackage: { name: "my_package", type: "npm" }
version: "1.0.0"
}
}
)
}
}Remove a Custom License from a Package Version
mutation {
userDefinedLicense {
removeUserDefinedLicenseFromPackageVersion(
input: {
licenseName: "My-Internal-License"
packageVersion: {
publicPackage: { name: "my_package", type: "npm" }
version: "1.0.0"
}
}
)
}
}Delete a Custom License
mutation {
userDefinedLicense {
deleteUserDefinedLicense(input: { name: "My-Internal-License" })
}
}Filter Options
The following table describes the available filter options for custom license queries.
| Field | Type | Description |
|---|---|---|
nameContainsFold | String | Case-insensitive name substring match |
nameEqualFold | String | Case-insensitive exact name match |
hasExpired | Boolean | Filter by expiration status |
Updated about 2 months ago
