Developer documentation
This page summarizes the minimal steps to add Tsukutta login, cloud save, and leaderboards to external apps.
<script src="https://tsukutta.app/sdk/v1.js" data-app-id="YOUR_APP_ID"></script>
<div data-tsukutta-login></div>This is how it looks
Sign in, write a note, and it is saved to your Tsukutta account — reopen it on another device or browser and it is still there. Built with just Sign in with Tsukutta and cloud storage; no server or database of your own. Try it first.
Open the live demo →The HTML of this demo page is the full, copy-paste source (use your browser's View Source to copy it). Change data-app-id to your own app ID to make it yours.
No coding. Just paste the one line below into your AI (Cursor / Claude / v0, etc.).
Read https://tsukutta.app/sdk/llms.txt and add a "Sign in with Tsukutta" button to my app. The app-id is "YOUR_APP_ID".Even when you need server-side verification (accounts, payments), the prompt above is enough for the AI to implement it correctly. The details below are for people wiring it up by hand.
For apps that render dynamically, call it in one line targeting an element. The return value cleans up (unsubscribe + remove the button).
const cleanup = Tsukutta.renderLoginButton("#login", {
theme: "light", // "light" | "dark"
label: "Sign in with", // optional label text
redirect: "/home", // navigate after a successful login (optional)
onLogin: (user) => setUser(user),
});Handle the sign-in state on the client like this:
// Current user (null when signed out)
const user = Tsukutta.user; // { id, name, avatarUrl }
// React to sign-in / sign-out
Tsukutta.onAuthChange((user) => { updateUI(user); });
// Sign out
Tsukutta.logout();To link "Login with Tsukutta" to your own account system, send the token obtained in the browser to your own server, then verify it against the verification endpoint. Never trust data coming from the browser as-is; always verify on your server.
// 1) Browser: send the SDK token to your own server
const user = await Tsukutta.login();
await fetch("/api/auth/tsukutta", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: Tsukutta.token }),
});
// 2) Your server: verify the token with Tsukutta
const res = await fetch("https://tsukutta.app/api/sdk/v1/verify", {
method: "POST",
headers: { Authorization: "Bearer " + token },
});
if (!res.ok) throw new Error("verify failed");
const data = await res.json();
// 3) CRITICAL: check the token audience
if (data.appId !== MY_APP_ID) throw new Error("audience mismatch");
// 4) Link data.userId to your own account systemCritical: check appId (audience)
Always confirm that the appId in the verify response matches your own app ID. Tokens are issued per app, so if you skip this check, a malicious app could replay a token it legitimately obtained for itself and impersonate your users in your app.
Flow example:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Tsukutta SDK sample</title>
<script src="https://tsukutta.app/sdk/v1.js" data-app-id="YOUR_APP_ID"></script>
</head>
<body>
<button id="login-button">Login with Tsukutta</button>
<button id="save-button">Save progress</button>
<button id="score-button">Submit score</button>
<pre id="log"></pre>
<script>
const log = (message) => {
const target = document.getElementById("log");
if (target) target.textContent += message + "\n";
};
document.getElementById("login-button")?.addEventListener("click", async () => {
try {
const user = await Tsukutta.login();
log("login: " + user.id);
} catch (error) {
log("login error: " + error);
}
});
document.getElementById("save-button")?.addEventListener("click", async () => {
try {
await Tsukutta.storage.set("progress/level", { level: 3 });
log("storage.set: ok");
} catch (error) {
log("storage.set error: " + error);
}
});
document.getElementById("score-button")?.addEventListener("click", async () => {
try {
const best = await Tsukutta.scores.submit("global", 1200, { stage: 3 });
log("scores.submit: " + best);
} catch (error) {
log("scores.submit error: " + error);
}
});
(async () => {
const top = await Tsukutta.scores.top("global", { limit: 5 });
const views = await Tsukutta.counters.get("visits");
log("scores.top top[0]: " + JSON.stringify(top[0] ?? null));
log("counters.get: " + views);
})();
</script>
</body>
</html>scores.top and counters.get can be called without login. Both require app_id query parameter.
| Code | HTTP | Meaning and remedy |
|---|---|---|
| unauthorized | 401 | Token is missing, invalid, expired, or the grant has been revoked. Log in again to resolve |
| forbidden | 403 | SDK integration is disabled for this app. Check the SDK settings on your mypage |
| not_found | 404 | Target does not exist, e.g. reading an unsaved key with storage.get |
| invalid_arg | 400 | Invalid argument (key name, value size, type, etc.) |
| quota_exceeded | 413 / 429 | Quota exceeded (413 for storage size / key count, 429 for the app's daily call limit) |
| rate_limited | 429 | Rate limit reached (per-minute / per-day). Retry later |
| server_error | 500 | Server error on the Tsukutta side. Retry later |
| timeout | - | Network timeout (raised on the client side) |
In My Page, open your app and turn on "SDK integration" — the ID appears there. Copy it before asking an AI to implement, or the work will stall.
Add your dev URL (e.g. http://localhost:3000) to "allowed origins". Unregistered origins have the login popup blocked. Don't forget your production domain too.
Use data-theme (light / dark) and data-label. For a fully custom button, call Tsukutta.login() from your own button's click handler.
The user ID, display name, and avatar URL. No email or other personal data is shared.
Any app that runs on the web. Plain HTML or frameworks like React/Vue — the same two lines work everywhere.
Cloud save (storage), leaderboards (scores), and counters. See the collapsible sections and the copy-paste sample above for usage.