Developer Payouts
Overview
Extension developers earn a revenue share that is credited to their balance. A payout moves the whole balance to the developer, with a self-billed credit note in the same transaction (see Developer Credit Notes). Only developers with a verified identity are paid.
A payout is created in one of three ways, always as pending, with the balance debited:
| Source | When |
|---|---|
| Request | The developer clicks Request payout (requestPayout / POST /v1/developer/payouts/request). The balance must reach the minimum of the payout method. |
| Closing payout | The developer schedules a profile deletion with a grace period; the remaining balance is paid out even below the minimum. |
| Payout cycle | Once per calendar month (Europe/Berlin) Lumio pays out every balance that reaches the method's minimum. |
A developer has at most one pending or processing payout at a time. While one is open, a profile deletion waits for it (409 DEVELOPER_FUNDS_OUTSTANDING).
Payout cycle
The API runs the payout cycle every 15 minutes on every replica; running it on several replicas at once is safe. Each run has two steps.
-
Monthly scheduling. Every verified developer whose balance reaches the minimum of their method (
min_payout_stripe,min_payout_paypal,min_payout_bank) gets a payout, unless they already had a payout this calendar month (requested, closing, failed or scheduled). Stripe and PayPal developers are only scheduled while that provider's credentials are configured, so no balance is booked that Lumio cannot send. SEPA payouts are always scheduled; an operator transfers them. -
Sending. Every
pendingStripe or PayPal payout that was never sent, whether scheduled, requested or a closing payout, is sent:Method Call Result Stripe Transfer to the developer's Connect account completed; a transfer is final when it is createdPayPal Batch payout to the developer's PayPal e-mail processing, thencompletedorfailedfrom the PayPal payouts webhookSEPA ( bank)none stays pendinguntil an operator records the transfer
A payout is sent at most once. Before calling the provider the cycle marks it (execution_started_at) and saves the mark. Then:
| Provider answer | What happens |
|---|---|
| Accepted | completed (Stripe) or processing (PayPal) |
| Refused (for example an invalid destination, or insufficient platform funds) | failed: the balance is credited back and the credit note cancelled. The developer can request again |
| Not reached (connection refused, rate limited, no PayPal token) | The mark is removed; the next run sends it |
| Unknown (timeout, provider error 5xx, duplicate request) | Stays pending with the mark and is never sent again. An operator checks with the provider and settles it |
Operator settlement
Operators settle what the cycle cannot: SEPA transfers and payouts whose answer is unknown. Such a payout is listed with needs_operator: true. In the admin app they are settled on the Developer Payouts page; the API:
| Action | GraphQL | REST | Permission |
|---|---|---|---|
List payouts (newest first, optional status filter) | adminDeveloperPayouts | GET /v1/admin/developer-payouts | billing:read |
| Mark paid | completeDeveloperPayout | POST /v1/admin/developer-payouts/{id}/complete | billing:edit |
| Mark failed (balance credited back, credit note cancelled) | failDeveloperPayout | POST /v1/admin/developer-payouts/{id}/fail | billing:edit |
Only a pending or processing payout can be settled (409 PAYOUT_NOT_IN_FLIGHT otherwise). A payout the cycle marked less than 15 minutes ago cannot be marked failed (409 PAYOUT_BEING_SENT): the provider call may still be running, and marking it failed could pay it twice. Mark it failed only after you confirmed with Stripe or PayPal that nothing arrived.
Configuration
[payments] in the API config:
| Key | ENV | Default | Meaning |
|---|---|---|---|
payout_cycle_enabled | LUMIO__PAYMENTS__PAYOUT_CYCLE_ENABLED | true | Run the cycle. With false nothing is scheduled or sent; requested payouts wait for an operator |
stripe_secret_key | LUMIO__PAYMENTS__STRIPE_SECRET_KEY | empty | Without it Stripe payouts are neither scheduled nor sent |
paypal_client_id, paypal_client_secret | LUMIO__PAYMENTS__PAYPAL_CLIENT_ID, …_SECRET | empty | Without both, PayPal payouts are neither scheduled nor sent |
paypal_environment | LUMIO__PAYMENTS__PAYPAL_ENVIRONMENT | sandbox | live for real payouts |
min_payout_stripe, min_payout_paypal, min_payout_bank | LUMIO__PAYMENTS__MIN_PAYOUT_* | 100, 2500, 5000 | Minimum balance in cents |
Without a complete [billing.issuer] no credit note and so no payout can be issued; the cycle then schedules nothing.
Audit events
| Event | Scope | When |
|---|---|---|
developer:payout_requested | user | The developer requested a payout, or the closing payout of a profile deletion was created (source: request, closing) |
system:developer_payout_scheduled | system | The monthly cycle created a payout |
system:developer_payout_sent | system | The cycle handed a payout to Stripe or PayPal |
system:developer_payout_completed | system | An operator marked a payout paid, or the PayPal webhook confirmed it (source: operator, webhook) |
system:developer_payout_failed | system | The provider refused it, an operator marked it failed, or the PayPal webhook reported it (source: cycle, operator, webhook) |
Metadata: payout_id, developer_id, method, amount, currency, status, the masked destination payout_reference, the provider ids, and for a failure the refunded amount and the cancellation note's number. It never contains the full PayPal e-mail, IBAN, or the payee's name and address.