bring-your-own-pollen
GitHub实现用户付费(BYOP)功能,通过 OAuth 流程获取用户授权密钥,支持 Web、CLI 等多种场景,使应用能调用用户自有余额而非开发者密钥。
Trigger Scenarios
Install
npx skills add pollinations/pollinations --skill bring-your-own-pollen -g -y
SKILL.md
Frontmatter
{
"name": "bring-your-own-pollen",
"description": "Add Bring Your Own Pollen (BYOP) to an app with Pollinations Connect so each user pays with their own Pollen instead of the app's key. Use when building an app, CLI, extension or agent tool on Pollinations and you need the user-authorized sk_ key, when you must choose between the OAuth code flow, the fragment flow and the device flow, or when an app key's usage must be attributed and monetized."
}
bring-your-own-pollen — user-paid Pollinations apps
BYOP lets your users authorize your app to spend their Pollen. The app
sends users to the consent screen with a publishable App Key (pk_...)
and gets back a scoped user key (sk_...) for generation calls. The app
owner never pays for user usage, and users keep control of budget, expiry and
revocation.
Full reference: https://gen.pollinations.ai/docs/llm.txt?section=byop and
https://enter.pollinations.ai/.well-known/oauth-authorization-server.
Pick the flow first
| Your app | Flow | Who exchanges the code |
|---|---|---|
| Web app with a backend | OAuth authorization-code + PKCE | Backend (keeps secret) |
| Static / browser-only web app | OAuth authorization-code + PKCE | Browser via fetch (PKCE replaces the secret) |
| Legacy web app | Fragment redirect | No exchange; key arrives in #api_key= |
| CLI, bot, extension, anything headless | Device flow | The app polls the token endpoint |
If you are unsure, default to the OAuth code flow with PKCE for new web
work; it keeps sk_... out of the browser callback URL.
Step 1 — App Key (client_id)
Create one at https://enter.pollinations.ai/keys → Apps → Create,
or via the API:
curl -X POST https://gen.pollinations.ai/account/keys \
-H 'Authorization: Bearer sk_yoursecretkey' \
-H 'Content-Type: application/json' \
-d '{"name":"my-app","type":"publishable","redirectUris":["https://myapp.com/callback"],"earningsEnabled":true}'
redirectUrismust match the callback exactly, query string included. Loopbackhttp://localhostmatches any port.earningsEnabled: trueopts into developer earnings: users pay 25% over base rates and the markup credits your balance, in the same bucket the user paid from.
Step 2 — OAuth code flow with PKCE
Build the auth link (generate a fresh verifier per attempt):
https://enter.pollinations.ai/authorize
?response_type=code
&client_id=pk_yourkey
&redirect_uri=https://myapp.com/callback
&scope=profile%20usage
&state=random-csrf-token
&code_challenge=BASE64URL_SHA256_VERIFIER
&code_challenge_method=S256
Optional restrictions: models=image,text (or individual IDs),
budget=10 (Pollen cap, default 5, at most 10 from a link), expiry=7 (days,
default 7). The user can change these on the consent screen. A requested
machines scope starts unticked there; keys is only offered in the device
flow and on redirects to http://localhost or http://127.0.0.1.
Handle the callback — validate state, then exchange the single-use code
(10-minute TTL):
curl -X POST https://enter.pollinations.ai/api/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=oauth_code' \
-d 'client_id=pk_yourkey' \
-d 'redirect_uri=https://myapp.com/callback' \
-d 'code_verifier=YOUR_PKCE_VERIFIER'
# -> { "access_token": "sk_...", "token_type": "bearer", "expires_in": 604800, "scope": "profile usage" }
There are no refresh tokens — re-run the flow when the key expires.
Store the token in memory or sessionStorage; never in localStorage, a URL,
analytics or logs.
Step 3 — Call the API with the user key
await fetch('https://gen.pollinations.ai/v1/chat/completions', {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ model: 'openai/gpt-5.4-nano', messages: [{ role: 'user', content: 'yo' }] }),
});
Generation needs no scope — spending is bounded by the budget and
expiry the user approved. Scopes gate account data only:
profile (name + email), usage (balance + usage), keys (create/list/revoke
keys; device flow and localhost redirects only).
Device flow — the headless path
Use this for CLIs, bots and extensions that cannot show the consent page.
# 1. request a device code (pass client_id for attribution)
curl -X POST https://enter.pollinations.ai/api/device/code \
-H 'Content-Type: application/json' \
-d '{"client_id": "pk_yourkey"}'
# -> { "device_code": "...", "user_code": "ABCD-1234",
# "verification_uri": "https://enter.pollinations.ai/device",
# "verification_uri_complete": "https://enter.pollinations.ai/device?user_code=ABCD-1234",
# "expires_in": 1800, "interval": 5 }
# 2. show verification_uri_complete to the user, then poll every `interval` seconds
curl -X POST https://enter.pollinations.ai/api/device/token \
-H 'Content-Type: application/json' \
-d '{"device_code": "..."}'
# pending -> { "error": "authorization_pending" }
# done -> { "access_token": "sk_...", "token_type": "bearer" }
The same exchange is available at the standard endpoint with
grant_type=urn:ietf:params:oauth:grant-type:device_code.
Fragment flow (legacy web)
https://enter.pollinations.ai/authorize?redirect_uri=https://myapp.com/callback&client_id=pk_yourkey&scope=usage
The key returns in the URL fragment (#api_key=sk_...&state=...), so it never
hits server logs. On denial the fragment is #error=access_denied.
const apiKey = new URLSearchParams(location.hash.slice(1)).get('api_key');
Identify the key holder
curl https://enter.pollinations.ai/api/device/userinfo \
-H 'Authorization: Bearer sk_...'
# -> { "sub": "user-id", "preferred_username": "voodoohop", "picture": "..." }
# with `profile` scope: also "name", "email"
Checklist
- App Key created with the exact
redirect_uriandearningsEnabledif you monetize. - Fresh PKCE verifier + S256 challenge per attempt.
-
statevalidated on the callback. - Token kept in memory/
sessionStorage; expiry handled by re-running the flow. - Device flow used for headless apps, polling at the returned
interval.
Version History
-
5ac44fc
Current 2026-11-03 06:06
统一使用 Pollinations Pay 和 Connect 命名;修复重定向流程无法获取 keys 权限及预算上限问题;优化控制台按钮标签。
- 5cb4f4b 2026-10-19 13:07


