Skip to main content
Before you can call most Tip Stack endpoints, you need to prove who you are. Tip Stack uses two complementary authentication patterns: session-based auth for user-facing flows (registration, login, profile, payouts) and API key auth for server-side SDK and embed integrations. This page walks you through both patterns and all of the related endpoints.

Two Auth Patterns

PatternHow to pass credentialsUsed for
Session cookieCookie set automatically on login/auth/*, /payouts/*, /creators/*
Bearer API keyAuthorization: Bearer YOUR_API_KEY/sdk/init, /sdk/tip, /sdk/events

Register

Create a new Tip Stack account. A session is established immediately on success — you do not need a separate login call.
POST /auth/register

Request body

email
string
required
A valid email address. It must not already be registered on the platform.
password
string
required
Account password. Minimum 8 characters.
name
string
required
Display name for the account. Must be between 2 and 100 characters.
{
  "email": "alice@example.com",
  "password": "mypassword123",
  "name": "Alice"
}

Response 200

success
boolean
true on a successful registration.
user
object
The newly created user record.
auth
object
Session credentials.
{
  "success": true,
  "user": {
    "id": "a1b2c3d4-...",
    "email": "alice@example.com",
    "name": "Alice",
    "full_name": "Alice",
    "first_name": "Alice",
    "roles": ["user"],
    "walletAddress": "So1a...ana1",
    "walletPublicKey": "So1ana...",
    "emailVerifiedAt": null,
    "onboardingComplete": false,
    "onboarding_complete": false,
    "solDomain": null,
    "twitterHandle": null,
    "discordHandle": null
  },
  "auth": {
    "accessToken": "eyJ...",
    "tokenType": "Bearer",
    "expiresAt": "2025-12-31T00:00:00.000Z"
  }
}
Error responses
Statuserror valueCause
400"Invalid payload: Name must be 2-100 characters and password min 8 characters."Validation failure
409"Email already in use"Duplicate account

Login

Log in with an existing email and password. The server sets a session cookie and returns an access token.
POST /auth/login

Request body

email
string
required
The registered email address.
password
string
required
The account password.
{
  "email": "alice@example.com",
  "password": "mypassword123"
}

Response 200

Returns the same user and auth shape as Register above, populated with your existing account data.
{
  "success": true,
  "user": {
    "id": "a1b2c3d4-...",
    "email": "alice@example.com",
    "name": "Alice",
    "full_name": "Alice",
    "first_name": "Alice",
    "roles": ["user"],
    "walletAddress": "So1a...ana1",
    "walletPublicKey": "So1ana...",
    "emailVerifiedAt": "2025-01-15T10:30:00.000Z",
    "onboardingComplete": true,
    "onboarding_complete": true,
    "solDomain": "alice",
    "twitterHandle": "@alice",
    "discordHandle": null
  },
  "auth": {
    "accessToken": "eyJ...",
    "tokenType": "Bearer",
    "expiresAt": "2025-12-31T00:00:00.000Z"
  }
}
Error responses
Statuserror valueCause
400"Invalid payload"Missing email or password
401"Invalid credentials"Wrong password or unknown email

Get Current User

Fetch the profile of the currently authenticated user. Use this to verify a session is still valid or to refresh user data in your application.
GET /auth/me
This endpoint requires a valid session cookie or Authorization: Bearer <accessToken> header. No request body is needed.

Response 200

success
boolean
true when the session is valid.
user
object
Full profile of the authenticated user.
{
  "success": true,
  "user": {
    "id": "a1b2c3d4-...",
    "name": "Alice",
    "email": "alice@example.com",
    "walletAddress": "So1a...ana1",
    "walletPublicKey": "So1ana...",
    "roles": ["user"],
    "solDomain": "alice",
    "twitterHandle": "@alice",
    "discordHandle": null,
    "emailVerifiedAt": "2025-01-15T10:30:00.000Z",
    "onboardingComplete": true,
    "onboarding_complete": true
  }
}
Error responses
Statuserror valueCause
401"Unauthorized"No valid session found

OTP (Email Code) Login

Tip Stack supports a passwordless login flow using a one-time code sent to the user’s email. Use this as an alternative to password-based login, or as a fallback for users who have not set a password.
1

Send the code

Post the user’s email to start the OTP flow. Tip Stack generates a 6-digit code that expires in 10 minutes and emails it to the address.
POST /auth/otp/start
email
string
required
The email address to send the one-time code to.
{ "email": "alice@example.com" }
Response 200
{ "success": true }
Statuserror valueCause
400"Email required"No email provided
404"Account not found"No account matches the email
2

Verify the code

Submit the email and the 6-digit code the user received. On success, Tip Stack creates a session and returns the same user and auth payload as a standard login.
POST /auth/otp/verify
email
string
required
The same email address used in /auth/otp/start.
code
string
required
The 6-digit one-time code from the email.
{
  "email": "alice@example.com",
  "code": "847291"
}
Response 200
{
  "success": true,
  "user": {
    "id": "a1b2c3d4-...",
    "email": "alice@example.com",
    "name": "Alice",
    "full_name": "Alice",
    "roles": ["user"],
    "emailVerifiedAt": "2025-01-15T10:30:00.000Z",
    "onboardingComplete": true,
    "solDomain": "alice",
    "twitterHandle": "@alice",
    "discordHandle": null
  },
  "auth": { "accessToken": "eyJ...", "tokenType": "Bearer", "expiresAt": "..." }
}
Statuserror valueCause
400"Invalid or expired code"Wrong code or code past 10-minute expiry
429"Too many failed attempts. Account locked for 15 minutes."5 failed attempts within the lockout window
Each one-time code is valid for 10 minutes. After a successful verification the code is immediately invalidated and cannot be reused.

SDK API Key Auth

SDK endpoints (/sdk/init, /sdk/tip, /sdk/events) are designed for server-side integrations and embedded widgets. They do not use session cookies. Instead, pass your API key as a Bearer token on every request.
Authorization: Bearer YOUR_API_KEY
You can find your API key in your creator dashboard under Settings → API Keys.
Never expose your API key in client-side JavaScript. Make all /sdk/init calls from your backend only — the key grants full SDK access on your behalf.
SDK endpoints validate your API key server-side. Use a key generated from your dashboard — do not share it publicly or commit it to version control.

Initializing an SDK session

Call /sdk/init from your backend to create a short-lived session token for a tipping widget embed. Pass the token to your frontend instead of your raw API key.
POST /sdk/init
Authorization: Bearer YOUR_API_KEY
creatorId
string
required
The creator’s wallet address, .sol domain, or UUID (prefixed with auth_).
originUrl
string
required
The URL of the page embedding the widget. This must match a domain you have whitelisted in your creator dashboard. localhost and 127.0.0.1 are always permitted for local development.
theme
string
Widget theme. Defaults to "dark". Pass "light" for a light theme.
{
  "creatorId": "alice.sol",
  "originUrl": "https://yourdomain.com",
  "theme": "dark"
}
Response 200
sessionToken
string
A short-lived token in the format sdk_sess_<uuid>. Use this as the Bearer value for /sdk/tip calls from the frontend.
config
object
Widget configuration to pass to the embedded client.
{
  "success": true,
  "sessionToken": "sdk_sess_f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "config": {
    "creatorId": "a1b2c3d4-...",
    "creatorAddress": "So1a...",
    "acceptedTokens": ["SOL", "USDC"],
    "embedUrl": "https://tipstack.fun/checkout/a1b2c3d4-...?theme=dark"
  }
}
Statuserror valueCause
400"creatorId and originUrl are required"Missing required fields
401"Missing or invalid API key"No Bearer token or key too short
403"Unauthorized Origin"originUrl is not whitelisted
404"Creator not found"No creator matches the given creatorId

Using the session token

Once your backend has received a sessionToken from /sdk/init, pass it to your frontend and use it as the Bearer token for /sdk/tip calls:
Authorization: Bearer sdk_sess_<uuid>
// Safe — session token in the browser, API key stays on the server
const response = await fetch("https://tipstack.fun/api/sdk/tip", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${sessionToken}`,
  },
  body: JSON.stringify({ amount: 0.5, token: "SOL" }),
});
Treat sessionToken values as short-lived and single-use. Re-initialize a session by calling /sdk/init again from your backend whenever you render a new embed instance.