Skip to main content

Endpoint

PUT https://api.gumroad.com/v2/licenses/rotate
Generates a new license key for an existing purchase while maintaining all other purchase data. This is useful when a license key has been compromised or shared publicly, and you need to issue a replacement key to the legitimate customer.
This endpoint requires authentication with the edit_products scope.
After rotation, the old license key will no longer work. Make sure to communicate the new key to your customer.

Authentication

This endpoint requires an OAuth access token with the edit_products scope. Include your access token in the request:
?access_token=YOUR_ACCESS_TOKEN

Request Parameters

access_token
string
required
Your OAuth access token with edit_products scope
license_key
string
required
The current license key to rotate
product_id
string
The unique identifier for your product (recommended)
The product’s permalink (either unique or custom). Can be used instead of product_id.

Response Fields

success
boolean
Whether the operation was successful
uses
integer
The current usage count for this license (preserved from the old key)
purchase
object
Complete purchase information with the new license key
purchase.license_key
string
The newly generated license key (this replaces the old one)
Other fields follow the same structure as the verify endpoint

Examples

Rotate a license key

curl -X PUT https://api.gumroad.com/v2/licenses/rotate \
  -d "access_token=YOUR_ACCESS_TOKEN" \
  -d "product_id=your-product-id" \
  -d "license_key=ABCD1234-EFGH5678-IJKL9012-MNOP3456"

Success Response

{
  "success": true,
  "uses": 5,
  "purchase": {
    "id": "encrypted_purchase_id",
    "product_name": "My Software",
    "product_id": "encrypted_product_id",
    "product_permalink": "https://gumroad.com/l/my-software",
    "short_product_id": "my-software",
    "email": "[email protected]",
    "price": 2999,
    "currency": "usd",
    "quantity": 1,
    "created_at": "2024-01-15T10:30:00Z",
    "sale_timestamp": "2024-01-15T10:30:00Z",
    "order_number": 123456,
    "license_key": "WXYZ9876-STUV5432-PQRS1098-NMLK6543",
    "variants": "",
    "custom_fields": [],
    "refunded": false,
    "chargebacked": false,
    "subscription_ended_at": null,
    "subscription_cancelled_at": null,
    "subscription_failed_at": null,
    "can_contact": true
  }
}

Error Responses

Unauthorized (401)

{
  "error": "The access token is invalid"
}

Forbidden (403)

Returned when the access token doesn’t have the edit_products scope.
{
  "error": "Forbidden"
}

Not Found (404)

Returned when the license doesn’t exist or doesn’t belong to your product.
{
  "success": false,
  "message": "That license does not exist for the provided product."
}

Common Use Cases

Respond to Compromised Keys

If a customer’s license key is shared publicly (e.g., on social media or piracy sites), you can rotate it to invalidate the old key and provide them with a new one.

Customer Support

When a customer reports unauthorized use of their license, you can rotate the key as part of your resolution process. This ensures the unauthorized user loses access while the legitimate customer gets a working key.

Security Best Practices

Some organizations require periodic key rotation as a security measure. This endpoint makes it easy to implement such policies.

Implementation Example

import requests

class LicenseSecurityManager:
    def __init__(self, access_token, product_id):
        self.access_token = access_token
        self.product_id = product_id
        self.base_url = 'https://api.gumroad.com/v2/licenses'
    
    def handle_compromised_license(self, old_license_key, customer_email):
        """Rotate a compromised license and notify the customer"""
        
        # First, disable the old key immediately
        requests.put(
            f'{self.base_url}/disable',
            data={
                'access_token': self.access_token,
                'product_id': self.product_id,
                'license_key': old_license_key
            }
        )
        
        # Then rotate to get a new key
        response = requests.put(
            f'{self.base_url}/rotate',
            data={
                'access_token': self.access_token,
                'product_id': self.product_id,
                'license_key': old_license_key
            }
        )
        
        if response.ok:
            data = response.json()
            new_key = data['purchase']['license_key']
            
            # Send the new key to your customer
            self.send_new_license_email(customer_email, new_key)
            
            return new_key
        else:
            raise Exception(f"Failed to rotate license: {response.text}")
    
    def send_new_license_email(self, email, new_key):
        # Your email sending logic here
        pass
The new license key is automatically generated using a secure random algorithm. You cannot specify a custom key format.
Only the product owner can rotate licenses for their products. You must authenticate with an access token from the product owner’s account.

License Key Format

Generated license keys follow the format: XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX
  • 32 hexadecimal characters (0-9, A-F)
  • Divided into 4 groups of 8 characters
  • Separated by hyphens
  • Always uppercase

Build docs developers (and LLMs) love