Skip to main content
Chariow provides built-in license key management for software products. Licenses are automatically generated when customers purchase license-type products and can be activated, validated, and revoked through the API.

License Object

A license contains comprehensive information about its status, activation history, and validity:
{
  "id": "lic_abc123",
  "status": "active",
  "customer": {
    "id": "cus_xyz789",
    "name": "John Doe",
    "email": "[email protected]"
  },
  "product": {
    "id": "prd_abc456",
    "name": "Premium Software License"
  },
  "license": {
    "key": "ABC-123-XYZ-789",
    "masked_key": "ABC-***-***-789"
  },
  "is_active": true,
  "is_expired": false,
  "can_activate": true,
  "activations": {
    "count": 3,
    "max": 10,
    "remaining": 7
  },
  "certificate_url": "https://api.chariow.com/certificates/lic_abc123.pdf",
  "metadata": null,
  "activated_at": "2025-01-15T10:30:00.000000Z",
  "expires_at": "2026-01-15T10:30:00.000000Z",
  "expired_at": null,
  "revoked_at": null,
  "created_at": "2025-01-15T09:00:00.000000Z",
  "updated_at": "2025-01-15T10:30:00.000000Z"
}

Key Fields

  • license.key: The unique license key string (e.g., ABC-123-XYZ-789)
  • license.masked_key: Masked version for display (e.g., ABC-***-***-789)
  • status: Current license status (see table below)
  • is_active: Boolean indicating if the license is currently active and usable
  • is_expired: Boolean indicating if the license has expired
  • can_activate: Boolean indicating if the license can be activated (has remaining slots)
  • activations.count: Current number of device activations
  • activations.max: Maximum allowed activations
  • activations.remaining: Number of activation slots remaining
  • certificate_url: URL to download the license certificate (if available)
  • activated_at: When the license was first activated (null if never activated)
  • expires_at: When the license will expire (null for lifetime licenses)
  • revoked_at: When the license was revoked (null if not revoked)

License Statuses

StatusDescription
pending_activationLicense created but not yet activated
activeLicense is activated and valid for use
expiredLicense has passed its expiration date
revokedLicense has been permanently revoked

Listing Licenses

Retrieve all issued licenses for your store:
curl -X GET "https://api.chariow.com/v1/licenses" \
  -H "Authorization: Bearer sk_live_your_api_key"

Query Parameters

ParameterTypeDescription
per_pageintegerNumber of licenses per page (default 50, max 100)
cursorstringPagination cursor from previous response
statusstringFilter by status (pending_activation, active, expired, revoked)
customer_idstringFilter by customer public ID (e.g., cus_abc123)
product_idstringFilter by product public ID (e.g., prd_def456)

Filtering Examples

# Get active licenses only
curl -X GET "https://api.chariow.com/v1/licenses?status=active" \
  -H "Authorization: Bearer sk_live_your_api_key"

# Get licenses for a specific product
curl -X GET "https://api.chariow.com/v1/licenses?product_id=prd_abc123" \
  -H "Authorization: Bearer sk_live_your_api_key"

# Get licenses for a specific customer
curl -X GET "https://api.chariow.com/v1/licenses?customer_id=cus_xyz789" \
  -H "Authorization: Bearer sk_live_your_api_key"

Getting a License by Key

Retrieve a specific license using its license key:
curl -X GET "https://api.chariow.com/v1/licenses/ABC-123-XYZ-789" \
  -H "Authorization: Bearer sk_live_your_api_key"

Validating a License

To validate a license in your application, retrieve it and check the status:
async function validateLicense(licenseKey) {
  const response = await fetch(
    `https://api.chariow.com/v1/licenses/${licenseKey}`,
    { headers: { 'Authorization': 'Bearer sk_live_your_api_key' }}
  );

  if (!response.ok) {
    return { valid: false, reason: 'License not found' };
  }

  const { data } = await response.json();

  if (!data.is_active) {
    return { valid: false, reason: 'License is not active' };
  }

  if (data.is_expired) {
    return { valid: false, reason: 'License has expired' };
  }

  return { valid: true, license: data };
}

Activating a License

Activate a license on a device. The system automatically captures IP address and user agent:
curl -X POST "https://api.chariow.com/v1/licenses/ABC-123-XYZ-789/activate" \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "device_identifier": "00:1B:44:11:3A:B7"
  }'

First Activation Behaviour

On the first activation:
  • Status changes from pending_activation to active
  • activated_at timestamp is set
  • expires_at is calculated based on product validity period
  • activation_count increments to 1

Subsequent Activations

On additional activations:
  • activation_count is incremented
  • A new activation record is created
  • License status remains active

Activation Limits

Each license has a maximum number of activations (max_activations). When the limit is reached, further activations will fail:
{
  "message": "Activation limit reached",
  "data": [],
  "errors": []
}
Check can_activate and activations_remaining before attempting activation.

Revoking a License

Revoke a license to prevent further use:
curl -X POST "https://api.chariow.com/v1/licenses/ABC-123-XYZ-789/revoke" \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Customer requested refund"
  }'
Revoking a license is permanent. The license cannot be reactivated after revocation.

Getting Activation History

View all activations for a specific license with detailed device tracking:
curl -X GET "https://api.chariow.com/v1/licenses/ABC-123-XYZ-789/activations?per_page=20" \
  -H "Authorization: Bearer sk_live_your_api_key"

Response

{
  "data": [
    {
      "id": "act_abc123",
      "activated_by": {
        "ip": {
          "value": "203.0.113.45",
          "country": {
            "code": "US",
            "name": "United States"
          }
        },
        "user_agent": {
          "browser": "Chrome",
          "platform": "Windows",
          "device": "desktop"
        },
        "device": "00:1B:44:11:3A:B7"
      },
      "metadata": null,
      "activated_at": "2025-01-18T15:42:00.000000Z",
      "created_at": "2025-01-18T15:42:00.000000Z"
    },
    {
      "id": "act_def456",
      "activated_by": {
        "ip": {
          "value": "198.51.100.12",
          "country": {
            "code": "FR",
            "name": "France"
          }
        },
        "user_agent": {
          "browser": "Safari",
          "platform": "macOS",
          "device": "desktop"
        },
        "device": "A4:5E:60:D8:2F:11"
      },
      "metadata": null,
      "activated_at": "2025-01-16T09:15:00.000000Z",
      "created_at": "2025-01-16T09:15:00.000000Z"
    }
  ],
  "pagination": {
    "count": 2,
    "per_page": 20,
    "next_cursor": null,
    "prev_cursor": null,
    "has_more_pages": false
  }
}

What’s Tracked

Each activation record contains:
  • IP Address: Automatically captured from the activation request, with geolocation
  • User Agent: Parsed browser and platform information
  • Device Identifier: Optional identifier you provide (MAC address, UUID, etc.)
  • Timestamp: When the activation occurred
  • Metadata: Optional custom data
This is useful for:
  • Auditing license usage
  • Detecting suspicious activity
  • Providing customers with device management
  • Debugging activation issues

Implementation Example

Here’s a complete example of license validation in a desktop application:
class LicenseManager {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://api.chariow.com/v1';
  }

  async validate(licenseKey) {
    const response = await fetch(
      `${this.baseUrl}/licenses/${licenseKey}`,
      { headers: { 'Authorization': `Bearer ${this.apiKey}` }}
    );

    if (!response.ok) {
      throw new Error('Invalid license key');
    }

    const { data } = await response.json();

    if (!data.can_activate) {
      throw new Error('License cannot be activated');
    }

    return data;
  }

  async activate(licenseKey, deviceId) {
    const response = await fetch(
      `${this.baseUrl}/licenses/${licenseKey}/activate`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ device_identifier: deviceId })
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message);
    }

    return response.json();
  }
}

// Usage
const manager = new LicenseManager('sk_live_your_api_key');

try {
  const license = await manager.validate('ABC-123-XYZ-789');
  console.log('License valid:', license.is_active);

  await manager.activate('ABC-123-XYZ-789', getDeviceId());
  console.log('License activated successfully');
} catch (error) {
  console.error('License error:', error.message);
}

API Endpoints Summary

EndpointMethodDescription
/v1/licensesGETList all licenses with filtering
/v1/licenses/{licenseKey}GETGet specific license details
/v1/licenses/{licenseKey}/activatePOSTActivate license on a device
/v1/licenses/{licenseKey}/revokePOSTPermanently revoke a license
/v1/licenses/{licenseKey}/activationsGETGet activation history

Best Practices

Security

  • Never expose your API key in client-side code
  • Use API keys server-side only
  • Validate licenses server-side before granting access
  • Store license keys securely on the customer’s device

Device Identification

  • Use unique, persistent device identifiers (MAC address, hardware UUID)
  • Don’t use user-changeable identifiers (computer name, username)
  • Consider platform-specific identifiers (Windows: machine GUID, macOS: hardware UUID)

Activation Management

  • Check can_activate before attempting activation
  • Display activations_remaining to users
  • Implement device management UI for customers
  • Handle activation errors gracefully

Validation Frequency

  • Validate on application startup
  • Re-validate periodically (e.g., every 24 hours)
  • Cache validation results locally
  • Implement offline grace period

Error Handling

  • Handle network errors gracefully
  • Provide clear error messages to users
  • Implement retry logic with exponential backoff
  • Log activation attempts for debugging