Synergy Games Jam 2026

Developers

Sign in with Synergy Login

Reward players whose devices run on green electricity. Synergy Login is standard OAuth 2.0 (authorization code + PKCE): your app sends the user to Umweltify, they approve in ThingsApp on their device, and you receive a signed token that says how green that device is — never the device id or any personal data.

Build with Synergy OS

1. Register your app

In Creator Studio → Synergy Login, register a client:

Your appClient typeYou get
Has a backend that can keep a secretConfidentialclient id + secret
Browser game, desktop or mobile app without a backendPublicclient id (PKCE only)

Redirect URIs match exactly: https only, except http://127.0.0.1:{port}/… for desktop apps (register the exact port) and custom schemes for mobile apps.

2. Endpoints

Authorizehttps://api.umweltify.com/connect/authorize
Tokenhttps://api.umweltify.com/connect/token
Revokehttps://api.umweltify.com/connect/revoke
JWKShttps://api.umweltify.com/.well-known/jwks
Discoveryhttps://api.umweltify.com/.well-known/openid-configuration
Current statusGET https://api.umweltify.com/v4/partner/synergy-status
Live statushttps://api.umweltify.com/hubs/synergy-status (SignalR)

Scopes: synergy_status (required) and offline_access (refresh tokens). PKCE with S256 is required for every client.

Integrations built before the Synergy Status rename (October 2026) keep working unchanged: the climate_status scope, /v4/partner/climate-status, /hubs/climate-status and the climate_status claim are still accepted, and every status message also carries the number as climateStatus.

3. The flow

1. Your app → browser: https://api.umweltify.com/connect/authorize
     ?client_id=…&redirect_uri=…&response_type=code
     &scope=synergy_status%20offline_access&state=…
     &code_challenge=…&code_challenge_method=S256
2. Umweltify shows a waiting page and opens ThingsApp on the user's computer.
3. The user approves your app (by its registered name) in ThingsApp.
4. The browser goes to redirect_uri?code=…&state=…      (check state!)
   — or redirect_uri?error=access_denied&state=… if they declined.
5. POST /connect/token  grant_type=authorization_code&code=…&redirect_uri=…
     &client_id=…[&client_secret=…]&code_verifier=…
   ← { access_token, refresh_token, expires_in: 900 }

Returning players. Once a browser has signed in with a device, Umweltify remembers the pair for 180 days after its last sign-in. If that device already approved your app, the next sign-in in that browser skips steps 2–3 and goes straight back to redirect_uri with a code. Send prompt=consent to ask the device anyway, or prompt=none for a quiet check (for example when your game loads): you get a code when no prompt is needed, and error=login_required otherwise, with no page shown.

Users can disconnect your app any time in ThingsApp → About → Connected apps.

4. The access token

An ES256 JWT, valid for 15 minutes. Verify it against the JWKS (issuer https://api.umweltify.com/) and read:

subAnonymous device id for your app — stable, and different for every app
tiergreen · amber · red · unknown
synergy_statusDetailed status 0–9 (prefer tier)
last_verified_atUnix time the status was last computed from fresh data
tierMeaningSay
greenPowered by renewable electricity (green contract or EACs)Green
amberWithin a carbon budget only — not renewable1.5°C aligned
redNeitherNot green
unknownNot verified yetNot verified

Most apps turn rewards on for green and amber. Treat an old last_verified_at (e.g. over 24 h) as unknown, and never call amber "green".

5. Keeping the status fresh

  • Refresh the token to get the current tier. It fails with invalid_grant once the user disconnected your app.
  • When your game starts, refresh the token or call status. That is how a game opening is counted in your app’s Synergy Logins on +in: a sign-in, or any call after 30 quiet minutes, counts as one.
  • Actions: send a player who isn’t green to https://in.umweltify.com/gems/go?src=app:{your-app-slug}. If they sign up on GEMS through it and submit a contract, that counts as an Action for your app. /thingsapp/get?src=app:{your-app-slug} does the same for a ThingsApp download. Both links are ready to copy on your app’s page in Creator Studio.
  • Pull GET /v4/partner/synergy-status with the access token.
  • Webhooks: set a webhook URL on your client to receive device.status_changed and grant.revoked. Verify Umweltify-Signature: t=…,v1=… — v1 = HMAC-SHA256(webhook secret, "{t}.{raw body}") — in constant time and reject timestamps older than 5 minutes. Retries: 1 m, 5 m, 30 m, 2 h, 12 h.
  • Live (games without a backend): SignalR to /hubs/synergy-status?access_token=…, messages status and revoked.

6. Browser example (public client)

const verifier = b64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = b64url(new Uint8Array(await crypto.subtle.digest("SHA-256",
  new TextEncoder().encode(verifier))));
const state = b64url(crypto.getRandomValues(new Uint8Array(16)));
sessionStorage.setItem("gl", JSON.stringify({ verifier, state }));
location.assign("https://api.umweltify.com/connect/authorize?" + new URLSearchParams({
  client_id: CLIENT_ID, redirect_uri: REDIRECT_URI, response_type: "code",
  scope: "synergy_status offline_access", state,
  code_challenge: challenge, code_challenge_method: "S256" }));

// on REDIRECT_URI
const { verifier: v, state: s } = JSON.parse(sessionStorage.getItem("gl"));
const q = new URLSearchParams(location.search);
if (q.get("state") !== s) throw new Error("state mismatch");
const tokens = await (await fetch("https://api.umweltify.com/connect/token", {
  method: "POST", body: new URLSearchParams({ grant_type: "authorization_code",
    code: q.get("code"), redirect_uri: REDIRECT_URI, client_id: CLIENT_ID, code_verifier: v })
})).json();

function b64url(bytes) {
  return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

Decoding the token in the browser is fine for UI. Anything that grants value — coins, items — should be decided on a server that verifies the token's signature.

7. Errors

error=access_deniedThe user declined in ThingsApp, or the request expired
invalid_grantCode used or expired, wrong verifier, or the user disconnected your app
401Token missing or expired — refresh
403Missing synergy_status scope, or access was revoked

We use essential cookies to make +in work, and optional analytics cookies to understand how the site is used so we can improve it. You can accept all cookies or reject non-essential ones at any time. See our cookie policy for details.