Skip to content

License Server — Internal

The license server (littlebig-license-server) is a Cloudflare Worker that handles all licensing for LittleBig: key generation, domain activation, plugin update delivery, credit management, the product catalog, Paddle provisioning, community access and the daily check.

Most day-to-day work now happens in the Product Admin, which calls the endpoints below. The raw curl examples remain for debugging and scripts.

Base URL: https://license.littlebig.co (the old littlebig-license-server.andredaus.workers.dev address still answers)
Auth: All admin endpoints require Authorization: Bearer {ADMIN_TOKEN}.


Terminal window
curl -X POST https://littlebig-license-server.andredaus.workers.dev/v1/admin/licenses \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod-wpl-01",
"tier_id": "tier-wpl-silver",
"customer_email": "customer@example.com",
"customer_name": "Customer Name",
"seat_limit": 3,
"billing_cycle": "annual",
"source": "manual",
"notes": ""
}'

The server generates a LBL-XXXX-XXXX-XXXX-XXXX key and auto-seeds credits from the tier configuration. Response:

{
"id": "lic-...",
"key": "LBL-XXXX-XXXX-XXXX-XXXX",
"product_id": "prod-wpl-01",
"tier_id": "tier-wpl-silver",
"customer_email": "customer@example.com",
"status": "active",
"seat_limit": 3,
"credits_seeded": 50
}
Product product_id Tier tier_id Credits seeded Seats
WP Luminary prod-wpl-01 Bronze tier-wpl-bronze 0 (own API key) 1
WP Luminary prod-wpl-01 Silver tier-wpl-silver 50 3
WP Luminary prod-wpl-01 Gold tier-wpl-gold unlimited 10

Terminal window
curl "https://littlebig-license-server.andredaus.workers.dev/v1/admin/licenses?product_id=prod-wpl-01&status=active&limit=50" \
-H "Authorization: Bearer {ADMIN_TOKEN}"

Query parameters: product_id, status (active | suspended | expired), customer_email, limit (default 20), offset.


Change status, expiry, seat limit, feature overrides, or notes on an existing license:

Terminal window
curl -X PATCH "https://littlebig-license-server.andredaus.workers.dev/v1/admin/licenses/{LICENSE_ID}" \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"status": "suspended",
"notes": "Chargeback — suspended pending resolution"
}'

Patchable fields: status, expires_at, seat_limit, features_override (JSON string), notes.


Terminal window
curl -X POST https://littlebig-license-server.andredaus.workers.dev/v1/admin/credits/grant \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"license_id": "lic-...",
"delta": 25,
"reason": "top-up after support case"
}'

Use a negative delta to deduct credits. The reason field is logged in credit_transactions for the audit trail.


Register a plugin version (update delivery)

Section titled “Register a plugin version (update delivery)”

After CI pushes a ZIP to R2, register the version so GET /v1/update/:slug returns it:

Terminal window
curl -X POST https://littlebig-license-server.andredaus.workers.dev/v1/admin/versions \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod-wpl-01",
"version": "1.3.0",
"zip_key": "wp-luminary/wp-luminary-1.3.0.zip",
"changelog": "Silver tier and threat telemetry.",
"min_wp": "6.4",
"min_php": "8.2",
"tested_wp": "6.7",
"released_at": "2026-06-30T00:00:00Z"
}'

The zip_key is the R2 object key in the littlebig-plugin-zips bucket. The update endpoint returns a 1-hour signed URL for this key.


The plugin calls this on activation and periodically to refresh seat/credit state. Not normally called manually, but useful for debugging:

Terminal window
curl -X POST https://littlebig-license-server.andredaus.workers.dev/v1/license/validate \
-H "Content-Type: application/json" \
-d '{ "key": "LBL-XXXX-XXXX-XXXX-XXXX", "domain": "example.com" }'

Local domains (localhost, *.local, *.test, *.ddev.site, *.lndo.site, 127.0.0.1, ::1) are auto-detected and do not consume a seat.


Catalog, Paddle and community (added October 2026)

Section titled “Catalog, Paddle and community (added October 2026)”

Migrations 0018 to 0022 in littlebig-core/schema/migrations extend the database. All additive.

Migration Adds
0018_catalog Product status, description, attributes, community kind and slug; tier status, trial_days, attributes; price amount_minor, currency, status; admin_audit_log
0019_product_sales_channel products.sold_via_paddle (1 = sold through Paddle)
0020_paddle_provisioning product_paddle (Paddle product per environment), provisioning_steps
0021_community_grants community_grants ledger
0022_reconcile_runs reconcile_runs

(There is no 0017: the unapplied Cloudflare Course migration was dropped. Create that product in the Product Admin.)

  • transaction.completed and subscription.created issue a license, then enrol the buyer in the community. Plugin buyers get a key email; course and service buyers do not.
  • subscription.canceled cancels the license and removes the community access (unless another active license grants it).
  • The buyer’s email is checked first. If it cannot receive mail, the license is still issued with an ATTENTION note, but nothing is mailed to it, no community account is created, and the administrator gets a report email.
Name Kind Purpose
ADMIN_TOKEN Secrets Store Authorises /v1/admin/*
PADDLE_API_KEY Secrets Store Paddle API (sandbox)
PADDLE_WEBHOOK_SECRET Worker secret Verifies Paddle webhooks
TOSEND_API_KEY Secrets Store (tosend-api-key) All outgoing mail
USERCHECK_API_KEY, USERCHECK_CACHE Secrets Store, KV Domain checks; the cache is shared with the auth server and the newsletter
COMMUNITY_API_USER, COMMUNITY_API_PASSWORD Worker secrets FluentCommunity / WordPress REST API
RECONCILE_APPLY, RECONCILE_MAX_REMOVALS, REPORT_EMAIL, COMMUNITY_COURSE_OWNER_ID Variables See Product Admin

All mail goes through toSend (src/lib/mail.ts) from licenses@notify.littlebig.co, replies to the contact address. ZeptoMail is no longer configured on this Worker. If toSend fails, the failure is logged and shown on the test-email button.

bun test in packages/license-server (the Paddle webhook suite creates its own local Secrets Store entries; it needs CLOUDFLARE_ACCOUNT_ID set).


  • Scheduled daily check is off. The Workers Free plan allows 5 cron triggers and they are all used. Uncomment [triggers] in wrangler.toml after upgrading.
  • Paddle production does not exist yet. Everything runs in the sandbox, and “publish to live” is not built.
  • Paddle to license issuing in the daily check is not built: it compares subscription status only.

Other pending features:

  • License portal — self-service customer view of keys, activations, and credit balance (not yet built)
  • Webhook on status change — notify external systems when a license is suspended or expires
  • x402 credit top-up — POST /v1/proxy/topup stub returns 501; full USDC payment flow not yet implemented