Skip to content

Adding Verification

The license verification library lets your script confirm that the user has purchased it before allowing execution.

Before you begin

License verification requires a network connection. The check will fail if:

  • The Cereal licensing server is unreachable, or
  • The server response indicates the user does not hold a valid license.

Design your onStart implementation to handle both cases explicitly (see the example below).

Step 1: Create a script in the Developer Console

Go to the Cereal Developer Console and create a new script entry. Inside the script details, open the Licensing section to find the public key for your script. You will need this key in your code.

Step 2: Add the Gradle dependency

Add the Cereal Maven repository to your root build.gradle.kts:

allprojects {
    repositories {
        // ...
        maven {
            url = uri("https://maven.cereal-automation.com/releases")
        }
    }
}

Then add the library to your module's build.gradle.kts:

dependencies {
    implementation("com.cereal-automation:cereal-licensing:<latest_version>")
}

Replace <latest_version> with the latest version available on the Cereal Maven repository.

Step 3: Add the license check

Call LicenseChecker.checkAccess() in onStart. The method returns a LicenseState:

Result Meaning
LicenseState.Licensed User holds a valid license. Allow the script to run.
LicenseState.Unlicensed User is not licensed, or the response failed signature verification. Block execution.
LicenseState.ErrorValidatingLicense The check could not be completed — the server was unreachable, returned a non-success status, or sent a response that could not be read. Carries a message describing the cause. Return false so the user can retry.
class MySampleScript : Script<MySampleConfiguration> {

    // Replace with the public key from the Cereal Developer Console.
    private val SCRIPT_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" +
        "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtL7rXEYD9WcQCGl8D9Ph\n" +
        "wj0WiPG/01+Y3rJyX5TRBfZLNE3hoLOPFDUzQOSy280e90Qv64Ux5plyUuts1Wbk\n" +
        "5vOH5q/TXEhdPixlwrVIAiMayIvV+t8mYCpOJBqaD+cvPQ1DYehUQ3hzax2XSd5O\n" +
        "K3N3r5iPJwtaLBLfSf8E5OnlCcADj8++3q52keTYkpJCrrJVwdJSs23oTq2aQEYj\n" +
        "WeQenq3Pl/J922kWqI8vZJiIb7kmKzcBdZR0zE39/d363dh/KU2c9v5DKFKG2HI6\n" +
        "I3eUkYGTUUqL+pLw9NtY4/tPmHN7FZXJ9rUvAaPk7oQzjSL2cJ1chmtcipUsZAy3\n" +
        "Fneh2HYlmAQpAc0V60DMzw9tQS2UQ5kQDGcC7h7xuAYHZT6jKhnuZon89Bek9qT+\n" +
        "ULgRMjuGTL4rpiMUabPj1IbGVZ6vTwYOjcltERh19MT8QchPo/UBB8W1CK4T3aLf\n" +
        "O3MHnGBeVTlhpBts57lAUGKP8RmGKLpmjL5lA4nw1B7BVzeJ2VuSy8Jhheq75IFp\n" +
        "kGoSrlqfxtA7SE8negMUEq6fca4J/Y5bABH6KHUrMiVaJGLa51Ert4qdOCvfJBlL\n" +
        "Ho/42AejYUJDi/P/fRiC99i6ObNPGXhQ9bz1Quz6F6VAzMjMmHo+OwQ5R2SHq2Yn\n" +
        "KmW5+hWaT3sqkxMw1a2JfTUCAwEAAQ==\n" +
        "-----END PUBLIC KEY-----\n"

    private var isLicensed = false

    override suspend fun onStart(
        configuration: MySampleConfiguration,
        provider: ComponentProvider
    ): Boolean {
        val licenseChecker = LicenseChecker(
            "com.cereal-automation.sample",
            SCRIPT_PUBLIC_KEY,
            provider.license()
        )
        val licenseResult = licenseChecker.checkAccess()
        isLicensed = licenseResult is LicenseState.Licensed

        // If the server was unreachable, return false so Cereal stops the script.
        // The user can then retry after restoring their connection.
        return licenseResult !is LicenseState.ErrorValidatingLicense
    }

    override suspend fun execute(
        configuration: MySampleConfiguration,
        provider: ComponentProvider,
        statusUpdate: suspend (message: String) -> Unit
    ): ExecutionResult {
        if (!isLicensed) {
            return ExecutionResult.Error("Unlicensed")
        }
        // Your script logic here
        return ExecutionResult.Success("Done")
    }
}

Step 4: Read the license capacity (optional)

Some scripts are sold in tiers, where a license entitles the holder to a bounded amount of something — 500 records, 10 accounts, and so on. Use checkLicense() instead of checkAccess() to read that entitlement. It returns a LicenseResult carrying both the state and the capacity:

val result = licenseChecker.checkLicense()

val maxRecords = when (val capacity = result.capacity) {
    is LicenseCapacity.Limited -> {
        provider.logger().info("Licensed for ${capacity.amount} ${capacity.unit}.")
        capacity.amount
    }
    is LicenseCapacity.Unlimited -> {
        provider.logger().info("Licensed for unlimited ${capacity.unit}.")
        Int.MAX_VALUE
    }
    LicenseCapacity.None -> Int.MAX_VALUE
}

return result.state !is LicenseState.ErrorValidatingLicense
Capacity Meaning
LicenseCapacity.None The script has no capacity concept (a free or single-price script). Impose no cap.
LicenseCapacity.Unlimited(unit) A tiered script whose license carries no cap on unit.
LicenseCapacity.Limited(amount, unit) The license entitles the holder to at most amount of unit.

capacity is only meaningful when state is LicenseState.Licensed; it is always None otherwise. Enforcing the cap is your script's responsibility — Cereal does not enforce it for you.

checkAccess() remains available and is equivalent to checkLicense().state.

ProGuard

The Script Template ships ProGuard rules that already cover the licensing library, so no extra configuration is needed when you use the template. If you maintain your own ProGuard configuration, make sure it keeps the SDK surface the library depends on:

-keep class com.cereal.sdk.** { *; }
-keepattributes Signature
-keep class kotlin.Metadata

Next steps