Registration and configuration Developer

Last updated Aug 16, 2026

Registration and configuration

Before you integrate, create an OAuth / OIDC client in the Developer portal (the page calls it an identifier), configure redirect URIs and scopes, and collect credentials for the client type. Protocol integration (authorization code, tokens, consent page, and so on) is covered in Integration overview and the docs that follow in this directory.

11 August 2026: Identifier scopes can be edited in the portal. Required scopes need a reason and a privacy policy. Users must confirm authorization on every sign-in. See 11 August 2026: scopes and consent platform update under Updates in this directory.

Entry

  1. Sign in to develop.swaymoon.com with Swaymoon Account.
  2. From Planned resources on the home page, open Identifiers (the sidebar has the same entry).
  3. Click + in the upper left to open the Register an app identifier wizard.
  4. Complete the wizard in order: Description → Icon → Redirect URI → Capabilities → Confirm.

The Developer portal manages clients through the Swaymoon Account API (https://api-passport.swaymoon.com/api/v1/developer/...). Sign in to Swaymoon Account before you perform these operations.

Client types

TypeclientTypeToken endpoint authTypical use
Confidential clientconfidential (default)client_secret_basicWeb / server apps that can keep client_secret on the backend
Public clientpublicnone (PKCE only)Pure HTML / static sites / SPAs, native clients, and similar: runtime must not hold client_secret
JWT assertion clientprivate_key_jwtprivate_key_jwtKeep an EC P-256 private key on your server; the portal stores only the public key. Use client_assertion at the token endpoint

All three types require PKCE (S256). The user must explicitly confirm on the authorization page before sign-in completes (prior consent is not silently reused). Client type cannot be switched after creation.

What JWT assertion is

private_key_jwt is an OAuth 2.0 / OpenID Connect client authentication method (RFC 7523). At the token endpoint, your server signs a short-lived JWT (client_assertion) with a private key you keep; Swaymoon Account verifies it with the public key registered in the portal. It is still a confidential client — the private key must not leave your server — but identity is proven with an asymmetric key instead of a shared client_secret.

CompareConfidential (client_secret_basic)JWT assertion (private_key_jwt)Public (none)
Use whenYou have a backend that can keep a shared secretYou have a backend, do not want the portal to see a private key, and want rotation by kidSPA / static / native: runtime must not hold a secret
Portal storesCannot re-show client_secret plaintextPublic key onlyNo secret
Token exchangeHTTP Basic + PKCEclient_assertion + PKCEForm client_id + PKCE
Refresh TokenYesYesNo

Choose JWT assertion for server apps that should not keep a long-lived client_secret in env vars, or that need zero-downtime key rotation.
Do not choose it for a pure browser SPA or a static site that cannot keep a private key — use a public client.

Required client_assertion claims and the request shape are in Authorization code and PKCE and Examples and troubleshooting.

Wizard fields

StepFieldNotes
DescriptionDescription, client type, public JWKDescription is the app name shown on the consent page. Types are in the table above (type is read-only when editing). JWT assertion requires an EC P-256 public JWK with a kid
IconDefault icon or upload and cropShown on the consent page. A default can be generated from the app’s first character. You can also upload JPEG / PNG / WebP and crop to about 512×512 square JPEG (max 512KB)
Redirect URIRedirect URI allowlistAddress the user’s browser returns to after success or cancel. Register at least one, and it must match redirect_uri in the authorization request exactly (you can add more). Local development may use http://127.0.0.1
Redirect URIData deletion callback URL (optional)Address the Swaymoon Account server POSTs a signed payload to when the user requests data deletion (not a browser redirect). See “Data deletion callback URL” below
Redirect URIPrivacy policy URLVisible on the consent page and in App authorizations details. Before opening, the user is told the domain is not under Swaymoon control. Required when you request any required scope other than openid. See “Privacy policy URL” below
CapabilitiesRequired / optional scope and reasonsConfigurable on create and edit. openid is always required. email and profile field scopes can be required, optional, or not requested. Required scopes need a user-visible reason and a privacy policy. See Scopes and consent
ConfirmCreate with Register. Edit with Save to update name / icon / callbacks / data deletion URL / privacy policy / scopes and reasons

After creation

Confidential client

FieldNotes
client_idClient identifier starting with swm_. Safe to appear in authorization URLs
client_secretShown only once at creation or rotation. Store it in a secrets manager immediately. If it is lost, rotate it from identifier details — you do not need to delete and recreate

Configuration:

  • Token endpoint auth: client_secret_basic
  • Grant types: authorization_code, refresh_token
  • Access Token about 5 minutes; Refresh Token about 180 days (the previous Refresh Token is not reused after a refresh)

Public client

FieldNotes
client_idClient identifier starting with swm_
client_secretNot issued

Configuration:

  • Token endpoint auth: none
  • Grant types: authorization_code only (no refresh_token; after the Access Token expires, run the authorization-code flow again)
  • Access Token about 5 minutes
  • When exchanging the code, send client_id and code_verifier in the request body. Do not use HTTP Basic
  • Runnable walkthrough: Minimal Python example. AI-assisted: Integrate a confidential client with generative AI and Integrate a public client with generative AI

JWT assertion client

FieldNotes
client_idClient identifier starting with swm_
client_secretNot issued
Public key kidThe EC P-256 public key registered at creation; you can add more keys from the detail page for rotation

Configuration:

  • Token endpoint auth: private_key_jwt (client_assertion + ES256)
  • Grant types: authorization_code, refresh_token
  • Access Token about 5 minutes; Refresh Token about 180 days
  • When exchanging the code, send client_id, code_verifier, and client_assertion. Do not use HTTP Basic
  • Keep the private key on your server; the portal rejects private JWKs

Redirect URI rules

  • Provide at least one non-empty URI.
  • redirect_uri in the authorization request must match a registered value character for character, including scheme, host, port, path, and query.
  • Do not use #fragment in a Redirect URI (the protocol forbids it).
  • Production should use https://. Local development may use loopback such as http://127.0.0.1:..., subject to the server’s validation policy.
  • Wildcard domains are not supported. Register a URI per environment.
  • http://127.0.0.1 and http://localhost are different addresses.
  • Who visits: after authorization, the user’s browser navigates here. If you register http://127.0.0.1:8766/callback, the login callback reaches the local demo as long as the browser and the demo are on the same computer.

Data deletion callback URL

Optional. When a user chooses Revoke and request data deletion in the Swaymoon Account privacy center, Swaymoon Account will:

  1. Revoke authorization for that app;
  2. Email an explanation to the app owner’s bound address;
  3. If this URL is configured, the machine running the Swaymoon Account API process POSTs to it (signing is described in Scopes and consent).
ItemNotes
Required?No. If unset, only email is sent; no HTTP callback
Allowed URLOnly https://… reachable by the Swaymoon Account server
Forbiddenhttp:// (any), 127.0.0.1, localhost, ::1, and other loopback addresses
Who visitsThe Passport server, not the browser (do not reuse a loopback login Redirect)
Local testingExpose a local receiver as HTTPS with ngrok, Cloudflare Tunnel, or similar, and register the tunnel URL here. The tunnel and receiver must be running when the user taps delete
vs RedirectLogin Redirect may still use http://127.0.0.1 (browser navigation). Deletion callback and login callback are different paths
Signing keyConfidential: plaintext client_secret issued at creation or rotation. Public / JWT (callback configured): webhookSecret issued at creation or rotation. Verify the raw body; see Scopes and consent

Runnable walkthrough: “Demo data deletion callback” in the confidential-client section of Minimal Python example.

Privacy policy URL

Optional. After you register it, users can view and open it from:

  • The authorization consent page (if configured);
  • Account PrivacyApp authorizations → app details;

Before the jump, Swaymoon Account shows: This domain is not controlled by Swaymoon. Its content is not managed by Swaymoon.

ItemNotes
Required?Required when you request any required scope other than openid; otherwise optional
Allowed URLPublic https://…. Local testing may use http://127.0.0.1 / http://localhost (https:// is also fine)
Forbiddenhttp:// to a non-loopback host
Who visitsThe user’s browser (same as login Redirect; not server egress)
API fieldprivacyPolicyUri in the create / update client request body

Before production launch, publish an HTTPS privacy policy page so users can see how you handle their data before they authorize.

Scope recommendations

NeedSuggested scope
Sign-in only, create a local accountopenid
Display a nameopenid + name (add nickname / picture if needed; optional or required)
Contact emailopenid + email (required or optional in the portal; honor cancel when optional)
Store the username in your appopenid + preferred_username (if required, provide a reason and privacy policy)

openid is always required. If email or a profile field is required, provide a user-visible reason and a privacy policy URL. Optional scopes are checked by default on the consent page; the user can uncheck them. See Scopes and consent.

Management

  • List: view created identifiers, names, client_id, client type and token endpoint auth, required / optional scopes.
  • Details: view callbacks, privacy policy, scopes, whether a webhook secret has been issued, and JWT public-key kids.
  • Edit: you can change description (name), icon, redirect URIs, data deletion callback URL, privacy policy URL, required / optional scopes, and reasons for required scopes.
  • Rotate secrets: confidential clients can rotate client_secret from the detail page (this also updates the deletion-callback signing key). Public / JWT clients can issue or rotate webhookSecret.
  • Add public key: JWT assertion clients can register another public key (kid) from the detail page for zero-downtime rotation.
  • Not editable: client type (confidential / public / JWT assertion) is locked after creation. To change type, delete the old identifier, register again, and update your app.
  • Delete: after deletion, issued tokens no longer work for that client, and user-side authorizations become invalid.

Compatibility: existing production clients do not need code changes because the platform now supports editable scopes and required authorization confirmation. Develop new clients against the latest docs. See Scopes and consent.

To create a client, call POST /api/v1/developer/clients with JSON including clientType, requiredScopes / optionalScopes, scopeReasons, and optional dataDeletionUri, privacyPolicyUri. To update an existing client, call PUT /api/v1/developer/clients/{clientId} (scopes and reasons can be updated).

Next: read Integration overview in this directory, then implement Authorization code and PKCE.