Authentication
Understanding the two authentication methods in the Whatalo plugin ecosystem — API keys for server-to-server calls and session tokens for frontend-to-backend calls.
Whatalo plugins use two distinct authentication methods depending on which layer is making a request. Understanding the difference prevents the most common security mistake plugin developers make: exposing API keys in browser code.
Two Authentication Methods
1. API Key (Server-to-Server)
Used by your plugin's backend to call the Whatalo REST API.
- Header:
X-API-Key: wk_live_*(production) orwk_test_*(development) - Issued automatically when a merchant installs your plugin
- Long-lived credential — treat it like a password
- Scoped to a single store — one merchant, one key
- Must never appear in browser JavaScript, frontend code, or client-side bundles
// Server-side only (Node.js, Edge Runtime, serverless function)
import { WhataloClient } from "@whatalo/plugin-sdk";
const client = new WhataloClient({
apiKey: process.env.WHATALO_API_KEY!,
});
const products = await client.products.list({ page: 1, per_page: 25 });2. Session Token (Frontend-to-Backend)
Used by your plugin's frontend (iframe) to authenticate requests to YOUR backend. The session token proves that the request originates from an authenticated user inside the Whatalo admin.
- Short-lived JWT (5-minute expiry)
- Issued by Whatalo when the admin loads your plugin
- Contains store ID, app ID, granted scopes, and installation info
- Verified on your backend using
WHATALO_CLIENT_SECRET - Cannot call the Whatalo API directly — it is a proof of identity, not an access token for the Whatalo API
// Frontend (plugin iframe)
import { sessionToken } from "@whatalo/plugin-sdk/bridge";
const { token } = await sessionToken();
const response = await fetch("https://your-backend.com/api/products", {
headers: { Authorization: `Bearer ${token}` },
});Common Mistakes
Exposing the API key in the frontend
// WRONG — API key is visible to anyone who can open DevTools
const client = new WhataloClient({ apiKey: "wk_live_abc123" });
const products = await client.products.list();This is wrong even if the key is loaded from an environment variable at build time, because build-time inlining puts the value in the JavaScript bundle that is shipped to the browser.
Calling the Whatalo API with a session token
// WRONG — session tokens cannot authenticate against the Whatalo API
const response = await fetch("https://api.whatalo.com/v1/products", {
headers: { Authorization: `Bearer ${sessionToken}` },
});
// Returns 401 — session tokens are for your backend, not the Whatalo APICorrect Pattern
// ─── Plugin Frontend (runs in the iframe) ─────────────────────────────────
import { sessionToken } from "@whatalo/plugin-sdk/bridge";
async function loadProducts() {
// Step 1: Get a short-lived session token from the App Bridge
const { token } = await sessionToken();
// Step 2: Send it to YOUR backend, not to the Whatalo API
const res = await fetch("/api/products", {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
}// ─── Plugin Backend (runs on your server) ─────────────────────────────────
import { verifyWhataloSessionToken } from "@whatalo/plugin-sdk/server";
import { WhataloClient } from "@whatalo/plugin-sdk";
export async function GET(request: Request) {
const authHeader = request.headers.get("Authorization") ?? "";
const token = authHeader.replace("Bearer ", "");
// Step 3: Verify the session token using your client secret
let claims;
try {
claims = verifyWhataloSessionToken(token, process.env.WHATALO_CLIENT_SECRET!);
} catch {
return new Response("Unauthorized", { status: 401 });
}
// Step 4: Use your API key to call the Whatalo API server-side
const client = new WhataloClient({ apiKey: process.env.WHATALO_API_KEY! });
const products = await client.products.list({ page: 1, per_page: 25 });
return Response.json(products);
}Environment Variables
During development, whatalo dev tunnels a single app origin. Keep your iframe frontend and backend on that same origin so relative URLs like /api/products work without extra CORS or loopback setup. If you later deploy your frontend and backend to different origins, switch those browser calls to an absolute backend URL.
Both credentials are set by the CLI during development and injected as environment variables in production.
| Variable | Description | Where It Comes From |
|---|---|---|
WHATALO_API_KEY | Store-scoped API key — used by WhataloClient | Issued on plugin installation |
WHATALO_CLIENT_SECRET | Plugin secret — used to verify session tokens and webhook signatures | Issued at plugin creation |
See Environment Variables for the full list and how to manage them across environments.
Related Pages
- Session Tokens — full session token reference, including auto-refresh and claims
- Scopes and Permissions — how to declare what data your plugin can access
- Security Best Practices — secrets management, webhook verification, and input validation