All endpoints except POST /webhook require an Authorization header:
Authorization: Bearer <api_token>

Licenses


POST /licenses Create a license
Request body (JSON)
{
  "license_key":            "XXXX-XXXX-XXXX",  // omit to auto-generate
  "product_code":           "tiny_fontsize_yearly",
  "status":                 "active",           // default: active
  "customer_id":            "user@example.com",
  "expires_at":             1780000000,         // unix timestamp, null = never
  "stripe_subscription_id": "sub_xxx",
  "stripe_customer_id":     "cus_xxx"
}
Responses
HTTPBody
201{"success": true, "license_key": "..."}
409{"error": "License key already exists"}
401{"error": "Unauthorized"}
POST /licenses?verify Verify a license key
Request body (JSON)
{"license_key": "XXXX-XXXX-XXXX"}
Responses
HTTPBody
200
{
  "license_key":            "...",
  "product_code":           "...",
  "status":                 "active",
  "valid":                  true,
  "expires_at":             1780000000,
  "stripe_subscription_id": "sub_xxx",
  "stripe_customer_id":     "cus_xxx",
  "stripe_price_id":        "price_xxx",
  "customer_id":            "user@example.com"
}
404{"error": "License not found", "valid": false}
400{"error": "license_key is required"}

If an active license is past its expires_at, its status is automatically updated to expired on verify.

PUT /licenses?key={license_key} Update a license
Request body (JSON — all fields optional)
{
  "product_code":           "tiny_fontsize_yearly",
  "status":                 "inactive",
  "customer_id":            "user@example.com",
  "expires_at":             1780000000,
  "stripe_subscription_id": "sub_xxx",
  "stripe_customer_id":     "cus_xxx",
  "stripe_price_id":        "price_xxx"
}
Responses
HTTPBody
200{"success": true}
404{"error": "License not found"}
400{"error": "license_key required in URL"}
DELETE /licenses?key={license_key} Delete a license
Responses
HTTPBody
200{"success": true}
404{"error": "License not found"}

Stripe Webhook


POST /webhook Receive Stripe events — no Bearer token, verified via Stripe-Signature
Handled events
EventAction
checkout.session.completedCreate or update license, set status active
invoice.payment_succeededRenew license, extend expires_at, set active
invoice.payment_failedSet license status past_due
customer.subscription.deletedSet license status canceled
customer.subscription.updatedSync status and expires_at from Stripe

All other event types are acknowledged and ignored. Webhook log: licensing/data/webhook.log

Product Codes


CodeNameDurationRecurring
tiny_fontsize_monthly Tiny FontSize 30 days yes
tiny_fontsize_yearly Tiny FontSize 365 days yes
tiny_fontsize_oneoff Tiny FontSize no
tiny_fontfamily_monthly Tiny FontFamily 30 days yes
tiny_fontfamily_yearly Tiny FontFamily 365 days yes
tiny_fontfamily_oneoff Tiny FontFamily no
tiny_fileimport_monthly Tiny FileImport 30 days yes
tiny_fileimport_yearly Tiny FileImport 365 days yes
tiny_fileimport_oneoff Tiny FileImport no

License Status Values


StatusMeaning
activeLicense is valid and within its expiry window
inactiveManually deactivated
expiredPast expires_at, set automatically on verify
canceledStripe subscription was canceled
past_dueStripe invoice payment failed