diff --git a/CLAUDE.md b/CLAUDE.md
index 205f402..ba81977 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -34,13 +34,7 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
### Known Bugs
-_No known bugs at this time._
-
-### Version 0.0.9
-
-
-
-**Note for AI Assistants:** Cleanup the versions in this section after implementation, but keep this notice!
+_No known bugs at this time.
## Technical Stack
@@ -462,3 +456,27 @@ Full API documentation available in `openapi.json` (OpenAPI 3.1 specification).
- Admins can customize subject, heading, additional content, and enable/disable via WC settings
- Expiration warning schedule (days before) remains in plugin settings
- Email enable/disable is controlled through WooCommerce email settings
+
+### 2026-01-21 - Version 0.0.9 Features
+
+**Implemented:**
+
+- Created API client examples for multiple programming languages
+- Documentation for REST API usage with code examples
+
+**New files:**
+
+- `docs/client-examples/README.md` - API documentation and overview
+- `docs/client-examples/curl.sh` - cURL command examples
+- `docs/client-examples/php-client.php` - PHP client class with examples
+- `docs/client-examples/python-client.py` - Python client class with examples
+- `docs/client-examples/javascript-client.js` - JavaScript/Node.js client class
+- `docs/client-examples/csharp-client.cs` - C# client class with examples
+
+**Technical notes:**
+
+- All client examples include error handling for rate limiting (HTTP 429)
+- Examples demonstrate all three API endpoints: validate, status, activate
+- JavaScript client works in both browser and Node.js environments
+- Python client uses dataclasses for type-safe responses
+- C# client uses async/await patterns and System.Text.Json
diff --git a/docs/client-examples/README.md b/docs/client-examples/README.md
new file mode 100644
index 0000000..a0480b3
--- /dev/null
+++ b/docs/client-examples/README.md
@@ -0,0 +1,69 @@
+# WC Licensed Product API Client Examples
+
+This directory contains example API clients for integrating with the WC Licensed Product REST API.
+
+## API Base URL
+
+```shell
+https://your-site.com/wp-json/wc-licensed-product/v1
+```
+
+## Available Endpoints
+
+| Endpoint | Method | Description |
+|----------|--------|-------------|
+| `/validate` | POST | Validate a license key for a specific domain |
+| `/status` | POST | Get the current status of a license |
+| `/activate` | POST | Activate a license on a domain |
+
+## Rate Limiting
+
+The API is rate-limited to 30 requests per minute per IP address. If you exceed this limit, you'll receive a `429 Too Many Requests` response with a `Retry-After` header indicating when you can retry.
+
+## Example Files
+
+- [curl.sh](curl.sh) - cURL command examples
+- [php-client.php](php-client.php) - PHP client class
+- [python-client.py](python-client.py) - Python client class
+- [javascript-client.js](javascript-client.js) - JavaScript/Node.js client class
+- [csharp-client.cs](csharp-client.cs) - C# client class
+
+## Response Format
+
+All endpoints return JSON responses with the following structure:
+
+### Success Response (Validate)
+
+```json
+{
+ "valid": true,
+ "license": {
+ "status": "active",
+ "domain": "example.com",
+ "expires_at": "2025-12-31",
+ "product_id": 123
+ }
+}
+```
+
+### Error Response
+
+```json
+{
+ "valid": false,
+ "error": "license_not_found",
+ "message": "License key not found."
+}
+```
+
+## Error Codes
+
+| Code | Description |
+| ------ | ------------- |
+| `license_not_found` | The license key does not exist |
+| `license_expired` | The license has expired |
+| `license_revoked` | The license has been revoked |
+| `license_inactive` | The license is inactive |
+| `domain_mismatch` | The license is not valid for the provided domain |
+| `max_activations_reached` | Maximum number of domain activations reached |
+| `rate_limit_exceeded` | Too many requests, please wait and retry |
diff --git a/docs/client-examples/csharp-client.cs b/docs/client-examples/csharp-client.cs
new file mode 100644
index 0000000..e8985f8
--- /dev/null
+++ b/docs/client-examples/csharp-client.cs
@@ -0,0 +1,352 @@
+/**
+ * WC Licensed Product API Client for C#
+ *
+ * A C# client for interacting with the WC Licensed Product REST API.
+ *
+ * Requirements:
+ * - .NET 6.0+ or .NET Framework 4.7.2+
+ * - System.Net.Http
+ * - System.Text.Json (or Newtonsoft.Json)
+ *
+ * Usage:
+ * var client = new WcLicensedProductClient("https://your-site.com");
+ * var result = await client.ValidateAsync("XXXX-XXXX-XXXX-XXXX", "example.com");
+ */
+
+using System;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace WcLicensedProduct
+{
+ ///
+ /// License status response data
+ ///
+ public class LicenseStatus
+ {
+ [JsonPropertyName("valid")]
+ public bool Valid { get; set; }
+
+ [JsonPropertyName("status")]
+ public string Status { get; set; }
+
+ [JsonPropertyName("domain")]
+ public string Domain { get; set; }
+
+ [JsonPropertyName("expires_at")]
+ public string ExpiresAt { get; set; }
+
+ [JsonPropertyName("activations_count")]
+ public int ActivationsCount { get; set; }
+
+ [JsonPropertyName("max_activations")]
+ public int MaxActivations { get; set; }
+ }
+
+ ///
+ /// Validation response data
+ ///
+ public class ValidationResult
+ {
+ [JsonPropertyName("valid")]
+ public bool Valid { get; set; }
+
+ [JsonPropertyName("error")]
+ public string Error { get; set; }
+
+ [JsonPropertyName("message")]
+ public string Message { get; set; }
+
+ [JsonPropertyName("license")]
+ public LicenseInfo License { get; set; }
+ }
+
+ ///
+ /// License information in validation response
+ ///
+ public class LicenseInfo
+ {
+ [JsonPropertyName("status")]
+ public string Status { get; set; }
+
+ [JsonPropertyName("domain")]
+ public string Domain { get; set; }
+
+ [JsonPropertyName("expires_at")]
+ public string ExpiresAt { get; set; }
+
+ [JsonPropertyName("product_id")]
+ public int ProductId { get; set; }
+ }
+
+ ///
+ /// Activation response data
+ ///
+ public class ActivationResult
+ {
+ [JsonPropertyName("success")]
+ public bool Success { get; set; }
+
+ [JsonPropertyName("error")]
+ public string Error { get; set; }
+
+ [JsonPropertyName("message")]
+ public string Message { get; set; }
+ }
+
+ ///
+ /// Exception for API errors
+ ///
+ public class WcLicensedProductException : Exception
+ {
+ public string ErrorCode { get; }
+ public int HttpCode { get; }
+
+ public WcLicensedProductException(string message, string errorCode = null, int httpCode = 0)
+ : base(message)
+ {
+ ErrorCode = errorCode;
+ HttpCode = httpCode;
+ }
+ }
+
+ ///
+ /// Exception for rate limit errors
+ ///
+ public class RateLimitException : WcLicensedProductException
+ {
+ public int RetryAfter { get; }
+
+ public RateLimitException(int retryAfter)
+ : base($"Rate limit exceeded. Retry after {retryAfter} seconds.")
+ {
+ RetryAfter = retryAfter;
+ }
+ }
+
+ ///
+ /// Client for the WC Licensed Product REST API
+ ///
+ public class WcLicensedProductClient : IDisposable
+ {
+ private readonly HttpClient _httpClient;
+ private readonly string _baseUrl;
+ private readonly JsonSerializerOptions _jsonOptions;
+
+ ///
+ /// Initialize the client
+ ///
+ /// Your WordPress site URL (e.g., "https://example.com")
+ /// Request timeout (default: 30 seconds)
+ public WcLicensedProductClient(string siteUrl, TimeSpan? timeout = null)
+ {
+ _baseUrl = siteUrl.TrimEnd('/') + "/wp-json/wc-licensed-product/v1";
+
+ _httpClient = new HttpClient
+ {
+ Timeout = timeout ?? TimeSpan.FromSeconds(30)
+ };
+ _httpClient.DefaultRequestHeaders.Accept.Add(
+ new MediaTypeWithQualityHeaderValue("application/json"));
+
+ _jsonOptions = new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true
+ };
+ }
+
+ ///
+ /// Validate a license key for a specific domain
+ ///
+ /// The license key to validate
+ /// The domain to validate against
+ /// Validation result
+ public async Task ValidateAsync(string licenseKey, string domain)
+ {
+ var data = new { license_key = licenseKey, domain = domain };
+ return await RequestAsync("/validate", data);
+ }
+
+ ///
+ /// Get the status of a license
+ ///
+ /// The license key to check
+ /// License status
+ public async Task StatusAsync(string licenseKey)
+ {
+ var data = new { license_key = licenseKey };
+ return await RequestAsync("/status", data);
+ }
+
+ ///
+ /// Activate a license on a domain
+ ///
+ /// The license key to activate
+ /// The domain to activate on
+ /// Activation result
+ public async Task ActivateAsync(string licenseKey, string domain)
+ {
+ var data = new { license_key = licenseKey, domain = domain };
+ return await RequestAsync("/activate", data);
+ }
+
+ ///
+ /// Make an API request
+ ///
+ private async Task RequestAsync(string endpoint, object data)
+ {
+ var url = _baseUrl + endpoint;
+ var json = JsonSerializer.Serialize(data);
+ var content = new StringContent(json, Encoding.UTF8, "application/json");
+
+ HttpResponseMessage response;
+ try
+ {
+ response = await _httpClient.PostAsync(url, content);
+ }
+ catch (TaskCanceledException)
+ {
+ throw new WcLicensedProductException("Request timeout");
+ }
+ catch (HttpRequestException ex)
+ {
+ throw new WcLicensedProductException($"Request failed: {ex.Message}");
+ }
+
+ // Handle rate limiting
+ if ((int)response.StatusCode == 429)
+ {
+ var retryAfter = 60;
+ if (response.Headers.TryGetValues("Retry-After", out var values))
+ {
+ int.TryParse(string.Join("", values), out retryAfter);
+ }
+ throw new RateLimitException(retryAfter);
+ }
+
+ var responseBody = await response.Content.ReadAsStringAsync();
+ T result;
+
+ try
+ {
+ result = JsonSerializer.Deserialize(responseBody, _jsonOptions);
+ }
+ catch (JsonException)
+ {
+ throw new WcLicensedProductException("Invalid JSON response");
+ }
+
+ // Check for API errors (for error responses)
+ if (!response.IsSuccessStatusCode)
+ {
+ var errorResult = JsonSerializer.Deserialize(responseBody, _jsonOptions);
+ if (errorResult?.Error != null)
+ {
+ throw new WcLicensedProductException(
+ errorResult.Message ?? "Unknown error",
+ errorResult.Error,
+ (int)response.StatusCode
+ );
+ }
+ }
+
+ return result;
+ }
+
+ public void Dispose()
+ {
+ _httpClient?.Dispose();
+ }
+ }
+
+ // =========================================================================
+ // Usage Examples
+ // =========================================================================
+
+ public class Program
+ {
+ public static async Task Main(string[] args)
+ {
+ using var client = new WcLicensedProductClient("https://your-wordpress-site.com");
+
+ // Example 1: Validate a license
+ Console.WriteLine("=== Validate License ===");
+ try
+ {
+ var result = await client.ValidateAsync("XXXX-XXXX-XXXX-XXXX", "myapp.example.com");
+
+ if (result.Valid)
+ {
+ Console.WriteLine("License is valid!");
+ Console.WriteLine($"Status: {result.License?.Status ?? "active"}");
+ Console.WriteLine($"Expires: {result.License?.ExpiresAt ?? "Never"}");
+ }
+ else
+ {
+ Console.WriteLine($"License is not valid: {result.Message ?? "Unknown error"}");
+ }
+ }
+ catch (RateLimitException ex)
+ {
+ Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
+ }
+ catch (WcLicensedProductException ex)
+ {
+ Console.WriteLine($"Error: {ex.Message}");
+ }
+
+ Console.WriteLine();
+
+ // Example 2: Check license status
+ Console.WriteLine("=== Check License Status ===");
+ try
+ {
+ var status = await client.StatusAsync("XXXX-XXXX-XXXX-XXXX");
+
+ Console.WriteLine($"Valid: {(status.Valid ? "Yes" : "No")}");
+ Console.WriteLine($"Status: {status.Status ?? "unknown"}");
+ Console.WriteLine($"Domain: {status.Domain ?? "None"}");
+ Console.WriteLine($"Expires: {status.ExpiresAt ?? "Never"}");
+ Console.WriteLine($"Activations: {status.ActivationsCount}/{status.MaxActivations}");
+ }
+ catch (RateLimitException ex)
+ {
+ Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
+ }
+ catch (WcLicensedProductException ex)
+ {
+ Console.WriteLine($"Error: {ex.Message}");
+ }
+
+ Console.WriteLine();
+
+ // Example 3: Activate license on a domain
+ Console.WriteLine("=== Activate License ===");
+ try
+ {
+ var result = await client.ActivateAsync("XXXX-XXXX-XXXX-XXXX", "newsite.example.com");
+
+ if (result.Success)
+ {
+ Console.WriteLine("License activated successfully!");
+ }
+ else
+ {
+ Console.WriteLine($"Activation failed: {result.Message ?? "Unknown error"}");
+ }
+ }
+ catch (RateLimitException ex)
+ {
+ Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
+ }
+ catch (WcLicensedProductException ex)
+ {
+ Console.WriteLine($"Error ({ex.ErrorCode}): {ex.Message}");
+ }
+ }
+ }
+}
diff --git a/docs/client-examples/curl.sh b/docs/client-examples/curl.sh
new file mode 100644
index 0000000..ef60e12
--- /dev/null
+++ b/docs/client-examples/curl.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# WC Licensed Product API - cURL Examples
+#
+# Replace YOUR_SITE_URL with your WordPress site URL
+# Replace YOUR_LICENSE_KEY with your actual license key
+# Replace YOUR_DOMAIN with the domain to validate/activate
+
+BASE_URL="https://YOUR_SITE_URL/wp-json/wc-licensed-product/v1"
+LICENSE_KEY="XXXX-XXXX-XXXX-XXXX"
+DOMAIN="example.com"
+
+# ------------------------------------------------------------------------------
+# Validate License
+# ------------------------------------------------------------------------------
+# Validates if a license key is valid for a specific domain
+# Returns: valid (bool), license details, or error message
+
+echo "=== Validate License ==="
+curl -X POST "${BASE_URL}/validate" \
+ -H "Content-Type: application/json" \
+ -d "{
+ \"license_key\": \"${LICENSE_KEY}\",
+ \"domain\": \"${DOMAIN}\"
+ }"
+echo -e "\n"
+
+# ------------------------------------------------------------------------------
+# Check License Status
+# ------------------------------------------------------------------------------
+# Gets the current status of a license without domain validation
+# Returns: status, domain, expiration date, activation count
+
+echo "=== Check License Status ==="
+curl -X POST "${BASE_URL}/status" \
+ -H "Content-Type: application/json" \
+ -d "{
+ \"license_key\": \"${LICENSE_KEY}\"
+ }"
+echo -e "\n"
+
+# ------------------------------------------------------------------------------
+# Activate License
+# ------------------------------------------------------------------------------
+# Activates a license on a specific domain
+# Returns: success (bool), message
+
+echo "=== Activate License ==="
+curl -X POST "${BASE_URL}/activate" \
+ -H "Content-Type: application/json" \
+ -d "{
+ \"license_key\": \"${LICENSE_KEY}\",
+ \"domain\": \"${DOMAIN}\"
+ }"
+echo -e "\n"
+
+# ------------------------------------------------------------------------------
+# Example with verbose output for debugging
+# ------------------------------------------------------------------------------
+echo "=== Verbose Request (for debugging) ==="
+curl -v -X POST "${BASE_URL}/validate" \
+ -H "Content-Type: application/json" \
+ -d "{
+ \"license_key\": \"${LICENSE_KEY}\",
+ \"domain\": \"${DOMAIN}\"
+ }" 2>&1
+
+# ------------------------------------------------------------------------------
+# Example handling rate limit (429 response)
+# ------------------------------------------------------------------------------
+# The API returns a Retry-After header when rate limited
+# You can use this header to determine when to retry
+
+echo "=== Handling Rate Limits ==="
+response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/validate" \
+ -H "Content-Type: application/json" \
+ -d "{
+ \"license_key\": \"${LICENSE_KEY}\",
+ \"domain\": \"${DOMAIN}\"
+ }")
+
+http_code=$(echo "$response" | tail -n1)
+body=$(echo "$response" | sed '$d')
+
+if [ "$http_code" == "429" ]; then
+ echo "Rate limited. Please wait before retrying."
+ echo "Response: $body"
+else
+ echo "HTTP $http_code: $body"
+fi
diff --git a/docs/client-examples/javascript-client.js b/docs/client-examples/javascript-client.js
new file mode 100644
index 0000000..e682165
--- /dev/null
+++ b/docs/client-examples/javascript-client.js
@@ -0,0 +1,220 @@
+/**
+ * WC Licensed Product API Client for JavaScript
+ *
+ * A JavaScript client for interacting with the WC Licensed Product REST API.
+ * Works in both browser and Node.js environments.
+ *
+ * Browser usage:
+ *
+ * const client = new WcLicensedProductClient('https://your-site.com');
+ * const result = await client.validate('XXXX-XXXX-XXXX-XXXX', 'example.com');
+ *
+ * Node.js usage:
+ * const WcLicensedProductClient = require('./javascript-client');
+ * const client = new WcLicensedProductClient('https://your-site.com');
+ */
+
+class WcLicensedProductError extends Error {
+ constructor(message, errorCode = null, httpCode = null) {
+ super(message);
+ this.name = 'WcLicensedProductError';
+ this.errorCode = errorCode;
+ this.httpCode = httpCode;
+ }
+}
+
+class RateLimitError extends WcLicensedProductError {
+ constructor(retryAfter) {
+ super(`Rate limit exceeded. Retry after ${retryAfter} seconds.`);
+ this.name = 'RateLimitError';
+ this.retryAfter = retryAfter;
+ }
+}
+
+class WcLicensedProductClient {
+ /**
+ * Initialize the client
+ * @param {string} siteUrl - Your WordPress site URL (e.g., 'https://example.com')
+ * @param {Object} options - Optional configuration
+ * @param {number} options.timeout - Request timeout in milliseconds (default: 30000)
+ */
+ constructor(siteUrl, options = {}) {
+ this.baseUrl = siteUrl.replace(/\/$/, '') + '/wp-json/wc-licensed-product/v1';
+ this.timeout = options.timeout || 30000;
+ }
+
+ /**
+ * Validate a license key for a specific domain
+ * @param {string} licenseKey - The license key to validate
+ * @param {string} domain - The domain to validate against
+ * @returns {Promise