API Client

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) or wk_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 API

Correct 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.

VariableDescriptionWhere It Comes From
WHATALO_API_KEYStore-scoped API key — used by WhataloClientIssued on plugin installation
WHATALO_CLIENT_SECRETPlugin secret — used to verify session tokens and webhook signaturesIssued at plugin creation

See Environment Variables for the full list and how to manage them across environments.

On this page