Build with Dots
Connect your app to Dots.
Sign-in, usernames, notes, files, and context that follow the user. Choose what your app does to see the credentials, permissions, and code you need.
01 / Install code
An SDK is optional.
npm install gives you a library. You can also use OAuth and HTTP directly.
02 / Register your app
A client ID identifies it.
Public apps use PKCE. Apps with a confidential server exchange also use a server secret.
03 / Ask the user
A token grants access.
The user signs in and consents to scopes. Installing a package never grants data access.
What are you building?
Your setup
Client ID · no secret
Register a public OAuth client. Use Authorization Code + PKCE S256 in the browser.
Optional: npm install @wrldbld/dots-core@0.2.0-alpha.0. You can also use an OAuth library and HTTP.
Request these scopes
openid profileEnable them on the client, then request user consent. Registration alone grants no access.
Access-token resource
https://www.dots.id/apiAn app calling both surfaces requests both resources during OAuth. The published browser SDK defaults to API access.
Browser app setup example
npm install @wrldbld/dots-core@0.2.0-alpha.0
// Browser code only. Register once, then save this client ID in app config.
import { createDotsClient, createWebStorage } from "@wrldbld/dots-core";
const issuer = "https://www.dots.id";
const clientId = "YOUR_PUBLIC_CLIENT_ID";
const redirectUri = "https://your-app.example/dots/callback";
export const dots = createDotsClient({
issuer, clientId,
storage: createWebStorage(window.sessionStorage, "dots:" + clientId + ":session"),
transactionStorage: createWebStorage(window.sessionStorage, "dots:" + clientId + ":pkce"),
});
// On normal app pages, restore before using protected features.
export async function restoreSignIn() {
await dots.session.restore();
return dots.session.current()?.identity ?? null;
}
// Bind to the sign-in button. Display errors from each handler in your UI.
export async function signIn() {
window.location.assign(await dots.authorize(redirectUri, "openid profile", ["api"]));
}
// Call once on the callback page; cache this promise across React effect replays.
let callbackPromise: Promise<unknown> | undefined;
export function finishSignIn() {
return callbackPromise ??= (async () => {
await dots.handleCallback(window.location.href);
window.history.replaceState(null, "", "/");
// UserInfo-verified identity. Account key: (identity.issuer, identity.sub).
// Use dots.whoami() for the permitted bootstrap/context snapshot.
// A backend must verify identity before creating its own app session.
return dots.session.current()!.identity;
})();
}
export async function signOut() {
await dots.logout();
}The browser example shows separate sign-in, callback, and sign-out handlers. Run the callback once, handle failures, and initialize only in browser code. sessionStorage is accessible to JavaScript; use a server session with HttpOnly cookies when your app has a backend.
Package versions & the newer core SDK
New apps use @wrldbld/dots-core@0.2.0-alpha.0 with createDotsClient({ clientId, storage, transactionStorage }). The matching @wrldbld/dots-cli@0.2.0-alpha.0 provides app registration and device sign-in. Both are alpha releases.
Existing apps using @wrldbld/dots@0.1.0-alpha.0 can retain its createDots({ apiKey }) API. It has no /react export. Use the core package for new agent, notes, grants and WebMCP integrations; keep each package’s APIs separate. See the agent guide for setup.
Register once. Configure each environment.
- Open your OAuth clients. Reuse the app's existing registration or create one. Select its client type, platform, framework, callbacks, and allowed scopes.
- Copy the public
client_id. Confidential apps also get aclient_secret, shown once, for server environment variables. Neither value replaces the user's access token. - Register the exact callback for production and development. Locally, use the full HTTPS origin emitted by Portless, including its proxy port. HTTP subdomains such as
my-app.localhostare not currently accepted callbacks. - Download the client's install spec from its dashboard for the exact configuration and Expo example. This owner-only file is available at
/api/oauth/clients/{clientId}/llms-install.txt.
Agents can register a public client through /api/oauth/register, registerDotsApp(), or dots app register in the published SDK/CLI alpha release. Check missingScopes and save the client ID. Unowned dynamic registrations cannot be edited in your dashboard; create an owned client there when you need restricted scopes or ongoing management. Device-only registrations need no callback. Dots' service credentials stay with Dots.
Add a username when your app needs one.
Basic sign-in uses openid profile. To let a person create their first Dots username, select the username feature above, use an owned client with username creation enabled, and request username:create. The example includes availability and claim handlers. Keep people signed in if a name is unavailable; later changes use Dots settings.
Public agent registration may omit this restricted permission. Inspect missingScopes and configure an owned client. Store accounts by (issuer, sub); usernames are profile claims.
For agents
One guide for building and querying.
Give your agent the install guide before it edits your app. The reusable skill explains how to choose credentials, map functions to scopes, connect MCP, and verify the integration.
Connected MCP hosts can read dots://docs/install through resources/list and resources/read. Hosts that do not expose resources can read /install/llms.txt over HTTPS. Save the skill in your agent's supported skill directory as dots-integrate/SKILL.md; connecting MCP does not automatically install it.
Check the integration.
- Sign in, validate the callback state, and exchange the code once with the original PKCE verifier. If consuming ID tokens, validate signature via JWKS, issuer, client audience, expiry, and nonce using an OIDC library.
- Call userinfo with the access token, then bootstrap for context. Store accounts by the immutable (issuer, sub), with a unique constraint. Never merge by email, wallet, or username.
- Verify the returned scope includes each feature's permissions. Client registration sets a ceiling; the user still has to consent. Handle 401 by refreshing or reconnecting, and 403 by checking scopes and grants.
- Confirm context:read cannot retrieve another app's private context. Test cross-app reads only with explicit context:read:all consent and shared notes only after grant acceptance.
- Test signup with a new human account and sign-in with an existing account. If username creation is selected, verify client enablement, consent, availability, an initial claim, and taken/reserved/already-set errors without losing the session.
- If offline_access was requested, refresh and save the rotated token. Sign out, revoke server tokens, clear local state, and confirm protected access fails.