Back to Developer

OAuth Apps

Build third-party applications that connect to WhaleTools stores. OAuth 2.0 with PKCE for secure authorization.

Overview

The WhaleTools OAuth platform lets third-party developers build applications that access store data on behalf of merchants. The flow uses OAuth 2.0 Authorization Code with Proof Key for Code Exchange (PKCE), ensuring secure authorization without exposing client secrets in public clients.

  1. 1.Register your application and receive a client_id.
  2. 2.Redirect the merchant to the authorization endpoint with requested scopes.
  3. 3.The merchant approves access and is redirected back with an authorization code.
  4. 4.Exchange the code for an access token and refresh token.
  5. 5.Use the access token to call the WhaleTools API on behalf of the merchant.

Register an Application

Register your OAuth application to receive a client_id. Provide one or more redirect URIs and the scopes your app requires.

whale.oauth.applications.create({
  name: "My Commerce App",
  redirect_uris: [
    "https://myapp.com/callback",
    "http://localhost:3000/callback"
  ],
  scopes: ["read:products", "write:orders", "read:customers"]
})

// Response
{
  "object": "oauth_application",
  "id": "app_01HX7G8K2M...",
  "name": "My Commerce App",
  "client_id": "woa_live_a1b2c3d4e5f6...",
  "redirect_uris": [
    "https://myapp.com/callback",
    "http://localhost:3000/callback"
  ],
  "scopes": ["read:products", "write:orders", "read:customers"],
  "created_at": "2026-03-10T12:00:00.000Z"
}

Authorization Flow

WhaleTools uses the Authorization Code flow with PKCE. Generate a code verifier and challenge before redirecting the user.

Step 1: Generate PKCE Parameters

import crypto from 'crypto';

// Generate a random code verifier (43-128 chars)
const codeVerifier = crypto.randomBytes(32).toString('base64url');

// Create the code challenge (S256)
const codeChallenge = crypto
  .createHash('sha256')
  .update(codeVerifier)
  .digest('base64url');

// Store codeVerifier in session — you'll need it in Step 3

Step 2: Redirect to Authorization

Send the merchant to the WhaleTools authorization page. Include a state parameter to prevent CSRF attacks.

https://vm.whaletools.cloud/oauth/authorize?
  client_id=woa_live_a1b2c3d4e5f6...&
  redirect_uri=https://myapp.com/callback&
  scope=read:products write:orders read:customers&
  state=random_csrf_token&
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256&
  response_type=code

Step 3: Handle the Callback

After the merchant approves, they are redirected to your redirect_uri with an authorization code. Verify the state parameter matches what you sent.

// Callback URL:
// https://myapp.com/callback?code=auth_code_here&state=random_csrf_token

// Verify state matches your session
if (params.state !== session.oauthState) {
  throw new Error('CSRF validation failed');
}

Step 4: Exchange Code for Tokens

const response = await fetch('https://vm.whaletools.cloud/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: 'auth_code_here',
    client_id: 'woa_live_a1b2c3d4e5f6...',
    redirect_uri: 'https://myapp.com/callback',
    code_verifier: codeVerifier  // from Step 1
  })
});

// Response
{
  "access_token": "wat_a1b2c3d4...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "wrt_x9y8z7w6...",
  "scope": "read:products write:orders read:customers",
  "store_id": "cd2e1122-d511-4edb-be5d-..."
}

Scopes

Request only the scopes your application needs. Merchants see exactly which permissions your app is requesting during the authorization flow.

ScopeDescription
read:productsView products, variants, and collections.
write:productsCreate, update, and delete products.
read:ordersView orders, line items, and fulfillments.
write:ordersCreate, update, and cancel orders.
read:customersView customer profiles and segments.
write:customersCreate and update customer records.
read:inventoryView stock levels and locations.
write:inventoryAdjust stock and transfer inventory.
read:analyticsView sales, traffic, and conversion data.
read:mediaView media files and metadata.
write:mediaUpload and manage media assets.
read:workflowsView automation workflows.
write:workflowsCreate and manage workflows.
read:agentsView AI agent configurations.
write:agentsCreate and configure AI agents.

Token Lifecycle

Access tokens are short-lived for security. Use refresh tokens to obtain new access tokens without re-prompting the merchant.

TokenLifetimeNotes
Access token1 hourUsed in Authorization: Bearer header.
Refresh token30 daysSingle-use. A new refresh token is issued with each refresh.
Authorization code10 minutesSingle-use. Must be exchanged promptly.

Refreshing Tokens

const response = await fetch('https://vm.whaletools.cloud/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'refresh_token',
    client_id: 'woa_live_a1b2c3d4e5f6...',
    refresh_token: 'wrt_x9y8z7w6...'
  })
});

// Response
{
  "access_token": "wat_new_token...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "wrt_new_refresh...",
  "scope": "read:products write:orders read:customers"
}

Audit Log

Monitor all OAuth authorization events for your application. The audit log tracks grants, revocations, token refreshes, and failed authorization attempts.

GET /v1/oauth/audit-log?application_id=app_01HX7G8K2M...

// Response
{
  "object": "list",
  "data": [
    {
      "id": "evt_01HX...",
      "type": "oauth.token.granted",
      "application_id": "app_01HX7G8K2M...",
      "store_id": "cd2e1122-d511-...",
      "scopes": ["read:products", "write:orders"],
      "ip_address": "203.0.113.42",
      "created_at": "2026-03-10T14:30:00.000Z"
    },
    {
      "id": "evt_01HY...",
      "type": "oauth.token.refreshed",
      "application_id": "app_01HX7G8K2M...",
      "store_id": "cd2e1122-d511-...",
      "created_at": "2026-03-10T15:31:00.000Z"
    },
    {
      "id": "evt_01HZ...",
      "type": "oauth.authorization.revoked",
      "application_id": "app_01HX7G8K2M...",
      "store_id": "cd2e1122-d511-...",
      "revoked_by": "merchant",
      "created_at": "2026-03-10T16:00:00.000Z"
    }
  ]
}

API Reference

MethodPathDescription
POST/v1/oauth/applicationsRegister a new OAuth application.
GET/v1/oauth/applicationsList your registered applications.
GET/v1/oauth/applications/:idGet application details.
PATCH/v1/oauth/applications/:idUpdate application settings.
DELETE/v1/oauth/applications/:idDelete an application.
GET/oauth/authorizeAuthorization endpoint (user-facing).
POST/oauth/tokenExchange code or refresh token for access token.
POST/oauth/revokeRevoke an access or refresh token.
GET/v1/oauth/applications/:id/authorizationsList active authorizations for an app.
GET/v1/oauth/audit-logView OAuth authorization events.