Get Started
Minds Connect SDK
Connect your application to Hello Minds. Your users sign in with their Minds account, or create one on the spot, and your app can see, message, and work with their Minds.
The Minds Connect SDK is the TypeScript package that makes that connection. Add
Register an OAuth client in the Builder console, copy the client ID into the SDK, and add a callback route at the redirect URI you registered. The examples below are TypeScript.
@animocabrands/minds-connect to your web app. Your users sign in on Hello Minds, including someone creating an account for the first time. Login and consent stay there. When they come back, oauth.client is the Minds Client Library , ready to list their Minds, send messages, and do the rest of what they allowed. Connect keeps that session fresh before each call.Your screens stay yours. Connect handles the redirect, the saved session, and sign-out from your app.Install
Types ship with the package. It runs in the browser. Load it from Vite, webpack, or another bundler. On the Next.js App Router, put the React bindings in a"use client" boundary.
npm install @animocabrands/minds-connect| Import | Use when |
|---|---|
@animocabrands/minds-connect | TypeScript without React |
@animocabrands/minds-connect/react | React. Needs react and react-dom 18 or newer as an optional peer |
Register an OAuth client
Create a client on the OAuth Client tab. Copy the client ID intoclientId. The ID is public. There is no client secret to store.
The redirect URI is the page in your app that finishes sign-in. The authorized origin is the site allowed to start it. Both have to match the values you register, including scheme, host, and port.
Keep the user in the same browser tab. Connect stores a one-time check in sessionStorage for that tab. A new tab, a popup, or a bookmarked callback URL cannot finish sign-in.
The tab lists Available scopes. That is this client's allowlist. You cannot edit it there. Pass scopes from that list with MindsScope. Sign-in fails if you ask for one that is not on it. MindsScope.Email is the signed-in user's account email, and it is on the allowlist. MindsScope.MindsEmail is a different scope: the address of one of their Minds.
React
Wrap the router once withMindsConnect, so the home page and the callback share one client. Options are read when the provider mounts. Reload the page to change them.
Put LoginCallback on the same path as redirectUri. signIn() sends the user to Hello Minds. After they return, call the Builder API on oauth.client.
import { BrowserRouter, Route, Routes, useNavigate } from "react-router-dom";
import {
LoginCallback,
MindsConnect,
MindsScope,
useMindsConnect,
type MindsOAuthOptions,
} from "@animocabrands/minds-connect/react";
const opts: MindsOAuthOptions = {
clientId: "YOUR_CLIENT_ID",
redirectUri: `${window.location.origin}/callback`,
scopes: [MindsScope.Email, MindsScope.MindsList],
};
export function Root() {
return (
<BrowserRouter>
<MindsConnect opts={opts}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/callback" element={<Callback />} />
</Routes>
</MindsConnect>
</BrowserRouter>
);
}
function Home() {
const { isInitialized, isAuthenticated, signIn, signOut, oauth } = useMindsConnect();
if (!isInitialized || !oauth) return <p>Loading…</p>;
if (!isAuthenticated) {
return (
<button type="button" onClick={() => signIn()}>
Sign in with Minds
</button>
);
}
async function listMinds() {
const minds = await oauth.client.listMinds();
console.log(minds);
}
return (
<>
<button type="button" onClick={() => listMinds()}>
List minds
</button>
<button type="button" onClick={() => signOut()}>
Log out
</button>
</>
);
}
function Callback() {
const navigate = useNavigate();
return (
<LoginCallback
onSuccess={() => navigate("/", { replace: true })}
onError={(err) => console.error(err)}
/>
);
}LoginCallback without children to show its built-in status. If you pass children, they always render. Send the user home from onSuccess.
useMindsConnect() also gives you hasScopes, tokens, and getAccessToken.
Without React
The steps are the same. Share one options module, start sign-in from a button, and finish it on the callback page. The callback is a new page load, so constructMindsOAuth again with the same clientId and redirectUri.
onTokensChanged runs in this tab when Connect writes the session: sign-out, a refresh, or a failed refresh that clears the store. It does not run on a fresh page load. Read oauth.tokens when you paint the first view.
// oauth.ts
import { MindsOAuth, MindsScope, type MindsOAuthOptions } from "@animocabrands/minds-connect";
export const opts: MindsOAuthOptions = {
clientId: "YOUR_CLIENT_ID",
redirectUri: `${window.location.origin}/callback`,
scopes: [MindsScope.Email, MindsScope.MindsList],
};
export const oauth = new MindsOAuth(opts);// main.ts — home page
import { oauth } from "./oauth";
const connectBtn = document.getElementById("connect");
const listBtn = document.getElementById("list");
const logoutBtn = document.getElementById("logout");
oauth.onTokensChanged((tokens) => {
const signedIn = Boolean(tokens);
if (connectBtn) connectBtn.hidden = signedIn;
if (listBtn) listBtn.hidden = !signedIn;
if (logoutBtn) logoutBtn.hidden = !signedIn;
});
connectBtn?.addEventListener("click", async () => {
await oauth.signIn();
});
listBtn?.addEventListener("click", async () => {
const minds = await oauth.client.listMinds();
console.log(minds);
});
logoutBtn?.addEventListener("click", async () => {
await oauth.signOut();
});// callback.ts — same path as redirectUri
import { OAuthRedirectError } from "@animocabrands/minds-connect";
import { oauth } from "./oauth";
try {
await oauth.handleRedirect();
window.location.replace("/");
} catch (err) {
if (err instanceof OAuthRedirectError) {
console.error(err.error, err.errorDescription);
} else {
console.error(err);
}
}handleRedirect() checks the returned state, exchanges the code, and stores the tokens. If the user denies access, or the state does not match, it throws OAuthRedirectError.
Scopes
A scope is what the signed-in user allows your app to do. Put the full set onopts.scopes when you construct the SDK. MindsScope is the set of TypeScript constants for those ids. Both entry points export it.
To ask for more later, call signIn({ scopes }) with the full list you want, including scopes they already granted. hasScopes tells you whether that trip is needed.
import { MindsScope } from "@animocabrands/minds-connect";
const scopes = [MindsScope.MindsList, MindsScope.ConversationsList, MindsScope.MessagingSend];
if (oauth.tokens && !oauth.hasScopes(scopes)) {
await oauth.signIn({ scopes });
}isAuthenticated, hasScopes, and signIn.
oauth.client can only call what that grant includes. listMinds() needs MindsScope.MindsList. Sending a message needs MindsScope.MessagingSend. The method list is on the client library page.
MindsScope | Scope | What the user allows |
|---|---|---|
MindsScope.Email | email | See their email |
MindsScope.MindsList | minds:list | See their Minds |
MindsScope.MindsStatus | minds:status | See whether their Minds are on |
MindsScope.MindsCognition | minds:cognition | See their Minds' cognition |
MindsScope.MindsSkillsList | minds:skills:list | See their Minds' skills |
MindsScope.MindsToolsList | minds:tools:list | See their Minds' tools |
MindsScope.MindsAppsList | minds:apps:list | See their Minds' apps |
MindsScope.MindsEmail | minds:email | See their Minds' email |
MindsScope.MindsWallets | minds:wallets | See their Minds' wallet addresses |
MindsScope.MindsAwaken | minds:awaken | Awaken Minds for them |
MindsScope.MindsEnable | minds:enable | Turn their Minds on |
MindsScope.MindsDisable | minds:disable | Turn their Minds off |
MindsScope.MindsSkillsEquip | minds:skills:equip | Equip skills on their Minds |
MindsScope.MindsSkillsUnequip | minds:skills:unequip | Unequip skills on their Minds |
MindsScope.MindsToolsEquip | minds:tools:equip | Equip tools on their Minds |
MindsScope.MindsToolsUnequip | minds:tools:unequip | Unequip tools on their Minds |
MindsScope.MindsAppsEquip | minds:apps:equip | Equip apps on their Minds |
MindsScope.MindsAppsUnequip | minds:apps:unequip | Unequip apps on their Minds |
MindsScope.ConversationsCreate | conversations:create | Start chats with their Minds |
MindsScope.ConversationsList | conversations:list | See their chats |
MindsScope.ConversationsRead | conversations:read | See a chat with their Minds |
MindsScope.MessagingSend | messaging:send | Send messages as them |
MindsScope.MessagingBeacon | messaging:beacon | Nudge their Minds |
MindsScope.MessagingHistory | messaging:history | See their chat history |
MindsScope.MessagingActivityStream | messaging:activity:stream | Watch activity for one of their Minds |
MindsScope.MessagingStream | messaging:stream | Watch messaging events across their Minds |
Where the calls run
A web app with no backend can stay in Connect.oauth.client is the client library, already aimed at the signed-in user. Connect depends on @animocabrands/minds-client-lib, so you do not install that package yourself for browser calls.
const minds = await oauth.client.listMinds();import { createMindsClient } from "@animocabrands/minds-client-lib";
const accessToken = req.headers.authorization?.replace(/^Bearer\s+/i, "");
const minds = await createMindsClient({ accessToken }).listMinds();getAccessToken() returns a token that is still valid, refreshing when it is within 60 seconds of expiry. It returns null when there is no session. oauth.tokens is the stored snapshot, and that snapshot can already be expired, so use getAccessToken() when you send the token to your backend.
Sign out
signOut() clears the session in your app and revokes the refresh token. Their Hello Minds account stays signed in, so the next visit can continue without creating an account again.
await oauth.signOut();Session
The session is stored inlocalStorage, under minds_oauth_session: plus your client ID. It survives reload and other tabs on this origin. Any script on the page can read it.
Pass storage when you want a different store. Extend TokenStore and implement get, set, and clear. signOut() clears that store.
| Field | Meaning |
|---|---|
accessToken | Bearer token for Builder API calls |
refreshToken | Replaced on each refresh. The store keeps the latest |
expiresIn | Seconds remaining when you read the store |
scope | Granted scopes, separated by spaces. Use hasScopes to check coverage |
Options
The same object goes tonew MindsOAuth(opts) and to MindsConnect.
| Option | Required | Default | What you pass |
|---|---|---|---|
clientId | yes | — | Public client ID from the OAuth Client tab |
redirectUri | yes | — | Callback URL you registered, including origin |
scopes | yes | — | Full MindsScope list for the first signIn(). Must not be empty. Include MindsScope.Email when you need the account email |
storage | no | "localStorage" | "localStorage", or your own TokenStore |
signIn() can also take state (a string or object you get back from handleRedirect) and scopes (a full list that replaces opts.scopes for that trip).
Methods
| Area | What you call |
|---|---|
| Sign-in | signIn, handleRedirect |
| Session | getAccessToken, tokens, hasScopes, onTokensChanged, signOut |
| Builder API | client — the client library (listMinds, sendMessage, and the rest) |
| React | MindsConnect, useMindsConnect, LoginCallback |
MindsOAuth, MindsScope, OAuthRedirectError, TokenStore, and the types MindsOAuthOptions, TokenSuccess, SignInOptions, and OAuthSession are exported from @animocabrands/minds-connect. The React entry re-exports MindsScope.