Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt

Use this file to discover all available pages before exploring further.

API Authentication

The LaunchMyStore API uses OAuth 2.0 for authentication. This page covers the authentication methods available and how to use them in your API requests.

Authentication Methods

Access Tokens

OAuth 2.0 access tokens for server-to-server API calls

Session Tokens

JWTs for embedded app API calls

Access Tokens

Access tokens are obtained through the OAuth 2.0 flow and used for server-to-server API calls.

Obtaining Access Tokens

Complete the OAuth authorization flow to get an access token:
// 1. Redirect merchant to authorization URL
const authUrl = new URL('https://api.launchmystore.io/oauth/authorize');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('scope', 'read_products,write_products,read_orders');
authUrl.searchParams.set('redirect_uri', 'https://your-app.com/callback');
authUrl.searchParams.set('state', generateRandomState());

res.redirect(authUrl.toString());

// 2. Exchange authorization code for tokens
const tokenResponse = await fetch('https://api.launchmystore.io/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    code: authorizationCode,
    grant_type: 'authorization_code',
    redirect_uri: 'https://your-app.com/callback'
  })
});

const { access_token, refresh_token, expires_in } = await tokenResponse.json();

Using Access Tokens

Include the access token in the Authorization header:
curl -X GET "https://api.launchmystore.io/api/v1/products" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json"
const response = await fetch('https://api.launchmystore.io/api/v1/products', {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  }
});

Token Expiration

Access tokens expire after 24 hours. Use the refresh token to get a new access token:
async function refreshAccessToken(refreshToken) {
  const response = await fetch('https://api.launchmystore.io/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      refresh_token: refreshToken,
      grant_type: 'refresh_token'
    })
  });
  
  const data = await response.json();
  
  // Store new tokens
  await storeTokens(data.access_token, data.refresh_token);
  
  return data.access_token;
}
Refresh tokens expire after 30 days. If a refresh token expires, the merchant must re-authorize your app.

Token Management

Implement a token manager for automatic refresh:
class TokenManager {
  constructor(shopId) {
    this.shopId = shopId;
  }
  
  async getAccessToken() {
    const tokens = await this.loadTokens();
    
    // Check if access token is expired (with 5 min buffer)
    if (tokens.expiresAt < Date.now() + 300000) {
      return this.refreshToken(tokens.refreshToken);
    }
    
    return tokens.accessToken;
  }
  
  async refreshToken(refreshToken) {
    try {
      const response = await fetch('https://api.launchmystore.io/oauth/token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          client_id: process.env.APP_CLIENT_ID,
          client_secret: process.env.APP_CLIENT_SECRET,
          refresh_token: refreshToken,
          grant_type: 'refresh_token'
        })
      });
      
      if (!response.ok) {
        throw new Error('Token refresh failed');
      }
      
      const data = await response.json();
      
      await this.storeTokens({
        accessToken: data.access_token,
        refreshToken: data.refresh_token,
        expiresAt: Date.now() + (data.expires_in * 1000)
      });
      
      return data.access_token;
    } catch (error) {
      // Refresh token may be expired - need re-authorization
      await this.markNeedsReauth();
      throw error;
    }
  }
  
  async loadTokens() {
    // Load from database
    return db.tokens.findOne({ shopId: this.shopId });
  }
  
  async storeTokens(tokens) {
    await db.tokens.updateOne(
      { shopId: this.shopId },
      { $set: tokens },
      { upsert: true }
    );
  }
  
  async markNeedsReauth() {
    await db.shops.updateOne(
      { id: this.shopId },
      { $set: { needsReauth: true } }
    );
  }
}

Session Tokens

Session tokens are JWTs used for embedded app API calls. They’re obtained through App Bridge.

Getting Session Tokens

import { createApp } from '@launchmystore/app-bridge';

const app = createApp({
  apiKey: 'your-client-id',
  host: new URLSearchParams(location.search).get('host')
});

const token = await app.getSessionToken();

Using Session Tokens

Include in the Authorization header:
const token = await app.getSessionToken();

const response = await fetch('https://your-app.com/api/data', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

Verifying Session Tokens

Verify session tokens on your backend:
import jwt from 'jsonwebtoken';

function verifySessionToken(token) {
  try {
    const decoded = jwt.verify(token, process.env.APP_CLIENT_SECRET, {
      algorithms: ['HS256'],
      audience: process.env.APP_CLIENT_ID,
      issuer: 'launchmystore'
    });
    
    return {
      valid: true,
      shopId: decoded.sub,
      shopDomain: decoded.dest
    };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}
See Session Tokens for detailed documentation.

Scopes

Request only the scopes your app needs:

Store Data Scopes

ScopeAccess
read_shopView store information
write_shopModify store settings
read_productsView products and variants
write_productsCreate, update, delete products
read_collectionsView collections
write_collectionsManage collections
read_inventoryView inventory levels
write_inventoryAdjust inventory

Order Scopes

ScopeAccess
read_ordersView orders and transactions
write_ordersCreate, update, fulfill orders
read_fulfillmentsView fulfillment data
write_fulfillmentsCreate, update fulfillments
read_draft_ordersView draft orders
write_draft_ordersManage draft orders

Customer Scopes

ScopeAccess
read_customersView customer data
write_customersCreate, update customers
read_customer_groupsView customer groups
write_customer_groupsManage customer groups

Content Scopes

ScopeAccess
read_contentView pages, blogs, articles
write_contentManage pages, blogs, articles
read_themesView theme files
write_themesModify theme files
read_metafieldsView metafield data
write_metafieldsCreate, update metafields

Admin Scopes

ScopeAccess
read_discountsView discount codes
write_discountsManage discounts
read_analyticsView store analytics
read_gift_cardsView gift cards
write_gift_cardsManage gift cards
read_shippingView shipping settings
write_shippingManage shipping settings

Rate Limits

API requests are rate limited based on your app’s billing tier:
TierRequests/SecondBurst
Free2040
Basic4080
Pro100200
Enterprise5001000

Rate Limit Headers

Every response includes rate limit information:
X-RateLimit-Limit: 40
X-RateLimit-Remaining: 38
X-RateLimit-Reset: 1705312800

Handling Rate Limits

async function apiCall(url, options, retries = 3) {
  const response = await fetch(url, options);
  
  if (response.status === 429) {
    if (retries > 0) {
      const resetTime = response.headers.get('X-RateLimit-Reset');
      const waitMs = (resetTime * 1000) - Date.now() + 100;
      
      await sleep(Math.min(waitMs, 60000));
      return apiCall(url, options, retries - 1);
    }
    
    throw new Error('Rate limit exceeded');
  }
  
  return response;
}

Error Responses

Authentication Errors

StatusCodeDescription
401UNAUTHORIZEDMissing or invalid token
401TOKEN_EXPIREDAccess token has expired
403FORBIDDENInsufficient scopes
403APP_UNINSTALLEDApp has been uninstalled

Error Response Format

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired access token",
    "details": {
      "reason": "token_expired"
    }
  }
}

Handling Errors

async function makeApiRequest(endpoint, options = {}) {
  const response = await fetch(
    `https://api.launchmystore.io/api/v1${endpoint}`,
    {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': `Bearer ${await tokenManager.getAccessToken()}`,
        'Content-Type': 'application/json'
      }
    }
  );
  
  if (!response.ok) {
    const error = await response.json();
    
    switch (error.error.code) {
      case 'UNAUTHORIZED':
      case 'TOKEN_EXPIRED':
        // Try to refresh token
        await tokenManager.refreshToken();
        return makeApiRequest(endpoint, options);
        
      case 'FORBIDDEN':
        throw new Error('Insufficient permissions');
        
      case 'APP_UNINSTALLED':
        await handleAppUninstalled();
        throw new Error('App has been uninstalled');
        
      default:
        throw new Error(error.error.message);
    }
  }
  
  return response.json();
}

Security Best Practices

Never include your client secret in client-side code. Keep it on your backend only.
Always use HTTPS for API calls and webhook endpoints.
Always validate the state parameter in OAuth callbacks to prevent CSRF attacks.
Encrypt access and refresh tokens in your database. Never log them.
Only request the scopes your app actually needs. Merchants trust apps with fewer permissions.
Tokens can be revoked if a merchant uninstalls your app. Handle this gracefully.

Testing Authentication

Test Mode

Use test credentials for development:
const config = {
  clientId: process.env.NODE_ENV === 'production'
    ? process.env.APP_CLIENT_ID
    : process.env.APP_TEST_CLIENT_ID,
  clientSecret: process.env.NODE_ENV === 'production'
    ? process.env.APP_CLIENT_SECRET
    : process.env.APP_TEST_CLIENT_SECRET
};

Mock Tokens

For unit testing, create mock tokens:
import jwt from 'jsonwebtoken';

function createMockSessionToken(payload = {}) {
  return jwt.sign(
    {
      iss: 'launchmystore',
      aud: 'test-client-id',
      sub: 'test-shop-id',
      dest: 'https://test-store.launchmystore.io',
      exp: Math.floor(Date.now() / 1000) + 3600,
      iat: Math.floor(Date.now() / 1000),
      ...payload
    },
    'test-client-secret',
    { algorithm: 'HS256' }
  );
}

See Also