Paddle setup
Keylight integrates with Paddle through a one-way webhook destination: every time a transaction completes, a subscription state changes, or a refund is issued, Paddle posts the event to Keylight and Keylight mints, updates, or refunds the license. There is no OAuth — only the signed webhook plus an API key for resolving customer emails (Paddle doesn’t send them in webhook payloads).
1. Enable Paddle in the dashboard
Section titled “1. Enable Paddle in the dashboard”From your dashboard, go to Settings → Integrations → Paddle and click Enable Paddle. Keylight reveals the webhook URL:
https://api.keylight.dev/webhooks/paddle/<your-tenant-id>Paste this URL into Paddle’s notification settings (next section). The signing secret you’ll paste back into Keylight is what verifies the Paddle-Signature header on every incoming request.
2. Map Paddle Price IDs to Keylight apps
Section titled “2. Map Paddle Price IDs to Keylight apps”In Settings → Integrations → Paddle, add one row per Paddle Price ID. A Paddle product can have multiple prices (one-time, monthly, annual) — each gets its own row.
| Paddle Price ID | Keylight app | Key type |
|---|---|---|
pri_01h…abc | myapp | pro |
pri_01h…xyz | myapp | team-annual |
Keylight reads data.items[0].price.id from each event to pick the row. Unmapped Price IDs cause the webhook to 4xx instead of minting against an unknown app.
3. Configure the webhook in Paddle
Section titled “3. Configure the webhook in Paddle”In the Paddle dashboard:
- Go to Developer Tools → Notifications.
- Click New destination → Webhook.
- Paste the Keylight URL from step 1 into the URL field.
- Subscribe to these event types (Keylight ignores anything else):
transaction.completedsubscription.createdsubscription.updatedsubscription.resumedsubscription.pausedsubscription.canceledadjustment.created
- Save. Paddle reveals the signing secret — copy it.
Back in Keylight (Settings → Integrations → Paddle), paste the signing secret into the Webhook signing secret field and save.
4. Add your Paddle API key (required for upgrades + emails)
Section titled “4. Add your Paddle API key (required for upgrades + emails)”Paddle’s transaction webhooks contain a customer_id but not the customer email. Keylight needs the email to mint a license and send it. In the same panel, paste a Paddle API key with read access to Customers:
- Paddle dashboard → Developer Tools → Authentication.
- Generate a key (Read-only on Customers is sufficient).
- Paste it into Keylight → Save Paddle API key. Stored encrypted at rest.
Without this key the integration will reject events with 400 customer_email_unresolvable.
5. Test the connection
Section titled “5. Test the connection”Use Paddle’s Test event button on the webhook destination. Send a transaction.completed test:
- A
200 OKfrom Keylight with a fresh license record in Dashboard → Licenses means everything is wired correctly. - A
400 invalid_signaturemeans the signing secret in Keylight doesn’t match the one in Paddle — re-copy. - A
400 unmapped_pricemeans the test event’s Price ID isn’t in your map.
What happens on a real sale
Section titled “What happens on a real sale”When a customer completes checkout, Paddle fires transaction.completed → Keylight verifies the Paddle-Signature, resolves the customer email via the Paddle API key, looks up the app+key-type from the Price ID, mints a license, and records the payment. Subscription renewals re-extend the license; subscription.canceled flips it to cancelled at the end of the current period; an adjustment.created refund event triggers a refund.
Upgrades
Section titled “Upgrades”To send a customer to a Paddle checkout that upgrades their existing license, append custom_data[keylight_upgrade_key]=<license-key> to the Upgrade checkout URL you set on the key type. Paddle echoes custom_data back in the webhook and Keylight links the new transaction to the existing license.
Disconnecting
Section titled “Disconnecting”Settings → Integrations → Paddle → Disconnect clears the signing secret, the Paddle API key, and the Price ID map. Existing licenses are untouched — only new webhook deliveries stop minting.