Earnings and pricing
Price your exclusive and shared offerings, get paid atomically in the trader's own transaction, and understand revocation and ratings.
What you sell
Prices are set in SOL per epoch (one Solana epoch is 432,000 slots, roughly 2 days). Traders book 1 to 30 epochs per lease. You sell up to two offerings on the same listing:
| Exclusive | Shared | |
|---|---|---|
| Availability | Always on for every live listing | Opt-in |
| Traders per epoch | Exactly one | Up to your seat cap (>= 1) |
| Bandwidth | Full capacity | Split across co-tenants |
| Handshake quota per lease | Full budget (default 100/min) | max(1, floor(budget / seats)) |
| Price | Your exclusive price | Separate, usually lower, price |
Exclusive offering
Exclusive is the default mode and every listing must have an exclusive price to go live. One trader holds your validator for the booked epoch window with the full bandwidth and the full handshake budget. While an exclusive lease is pending payment or active, no other lease of either mode can overlap those epochs.
Shared offering
Shared is opt-in. You set a shared price per epoch and a seat cap
(max_concurrent_shared_leases, at least 1). Up to that many traders can
hold shared leases over the same epoch, each paying your shared price.
Each shared lease's JWT carries a per-seat handshake quota of
max(1, floor(budget / seats)), where the budget is the platform default
of 100 handshakes per minute. With 4 seats that is 25/min per trader. The
host-proxy enforces the quota per lease, so one co-tenant cannot crowd
out the others.
A trader pays less for shared because they get less: co-tenants on the
same epochs, a fraction of the handshake quota, and no exclusivity. For
you it is a way to monetize epochs no exclusive trader booked, with a
revenue ceiling of seats x shared price per epoch instead of one
exclusive price.
Admission is enforced in Postgres, not in application code. Exclusive
and shared leases mutually exclude each other on overlapping epochs:
an exclusive booking against any taken epoch fails with 409 lease-slot-taken, a shared booking when every seat is sold fails with
409 shared-capacity-full, and a shared booking against a listing with
the shared offering disabled fails with 422 shared-not-offered. See
error reference.
Configure at registration
validator-cli register takes the exclusive price as the required
--price flag. The shared offering is the optional
--shared-price / --shared-seats pair, and each flag requires the
other:
validator-cli register \
--identity-keypair /path/to/validator-keypair.json \
--endpoint-url https://tee.example.com:443 \
--price 2.5 \
--shared-price 0.8 \
--shared-seats 4 \
--bio 'Frankfurt validator, 1.2M SOL stake' \
--payout-wallet <BASE58_ADDRESS>
If only one of the pair reaches the API, registration fails with 422 register-shared-config-incomplete. A non-positive shared price is 422 register-shared-price-invalid and a seat cap below 1 is 422 register-shared-seats-invalid. Omit both flags to launch
exclusive-only. You can add the shared offering later.
Change it later
Two ways, both owner-only:
- Dashboard quick toggle: the Offering card on /validator shows your exclusive price, shared price, and seat cap, with a one-click checkbox to enable or disable shared bookings. The toggle only appears once a shared price and seat cap are stored. An unconfigured listing shows a link to the Edit listing form instead.
- Edit listing form at
/validator/listing/edit: change the exclusive price, shared price, seat cap, bio, and endpoint URL.
Both drive the same API call:
curl -X PATCH https://api.swqos.dev/v1/validators/{id} \
-H "Authorization: Bearer <api key>" \
-H "Content-Type: application/json" \
-d '{"shared_enabled": false}'
PATCH semantics:
| Patch | Result |
|---|---|
{"shared_enabled": false} | Stops new shared bookings. The stored shared price and seat cap are kept, so re-enabling later is one toggle. Active shared leases are unaffected. |
{"shared_enabled": true} | Works only if a shared price and seat cap are stored or supplied in the same patch, otherwise 422 shared-config-incomplete. |
{"shared_enabled": true, "shared_price_sol": 0.8, "max_concurrent_shared_leases": 4} | Configures and enables in one call. |
{"price_sol": 3.0} | New exclusive price. Must be greater than zero. |
{} | 422 patch-validator-empty. |
When price changes apply
Price changes apply to new bookings only. At booking time the
per-epoch price is frozen onto the lease row
(price_per_epoch_at_book_lamports), and everything derived from it
stays frozen for that lease's lifetime:
- Existing leases are never repriced, up or down.
- Lease extensions are charged at the lease's frozen per-epoch price, not your current listing price.
- The platform fee percentage and your payout wallet are also stamped on the lease at booking and never change afterwards.
How you get paid
There is no custodial holding and no payout schedule. The trader pays through a Solana Pay transaction request, and that single transaction contains two transfer legs:
- 90% to your payout wallet (
validator_share = total - fee) - 10% to the platform wallet (
fee = floor(total x 1000 / 10000), the 10% take-rate frozen at booking)
The settlement worker confirms the finalized transaction on-chain and verifies both legs before activating the lease. Your share lands in your wallet the moment the trader's payment finalizes, up front for the whole lease, not spread over the epochs. The dashboard's Revenue received card lists each settlement with an explorer link.
Payout wallet
The payout wallet defaults to your validator identity pubkey. Set a
different one at registration with --payout-wallet, or rotate it later:
validator-cli set-payout-wallet \
--identity-keypair /path/to/validator-keypair.json \
--payout-wallet <BASE58_ADDRESS>
The CLI signs a challenge with your identity keypair and calls PUT /v1/validators/payout-wallet. The change applies to future bookings
only: the wallet is frozen onto each lease at booking, so leases
already booked keep paying the wallet they were booked with. The
dashboard's Payout wallet card shows the current address and a prefilled
command.
Revoking a lease
You can cut off a trader (abuse, security concern, capacity emergency)
from the dashboard: each active lease in the Capacity card has a
Revoke button that opens a dialog with a required free-text reason
and a preview of the refund the platform will book. The API equivalent
is POST /v1/validators/{id}/leases/{lease_id}/revoke with an
Idempotency-Key header.
What happens:
- The lease flips to
revokedimmediately. The marketplace stops issuing JWTs for it, and the trader's current JWT dies at itsexp(at most 10 minutes). - The trader gets a prorated refund:
floor(remaining_epochs x total_amount / total_epochs), where remaining epochs run from the current epoch (inclusive) through the lease end. Before the lease starts that is a full refund, after it ends it is zero. - The refund is paid from the platform-held SOL buffer (funded by the 10% fee leg), processed manually within 24 hours. Your already-received 90% share is not clawed back.
- Only
activeleases can be revoked (422 lease-not-active). Revoking twice returns409 lease-already-revokedwith the original refund unchanged.
Treat revocation as a last resort. The reason you enter is permanently audit-logged, the trader loses service they paid for, and the reputational damage is yours, not theirs. The platform absorbs the refund, you absorb the trust hit.
Ratings and reputation
Your reputation score drives your ranking on the browse page. It is recomputed daily as:
reputation = 0.50 x uptime_score + 0.30 x latency_score + 0.20 x ratings_score
- Uptime (50%): 30-day rolling probe uptime, mapped linearly from 95% (score 0) to 99.9% (score 100).
- Sign latency (30%): 30-day p50 probe latency, mapped linearly from 200ms (score 0) down to 30ms (score 100).
- Trader ratings (20%): average stars over 5. Zero until you have ratings.
Rating rules:
- One rating per lease, and only for leases that actually ran: the lease
window must have elapsed and the lease must have served at least
10,000 TEE signatures (real signing activity, not token mints).
Anything else is
422 rating-not-yet-eligible. - You get one public reply per rating, via the dashboard ratings
panel or
POST /v1/ratings/{rating_id}/reply.
Register your validator and set your first prices through the web funnel or the CLI.
Full reference for PATCH /v1/validators/{id}, payout wallet, and the revoke endpoint.
See your offering from the buyer's side, including how exclusive and shared bookings collide.