OAuth 2.0 for apps: authorization, scopes, and refresh tokens
LaunchMyStore apps use the OAuth 2.0 authorization code flow to gain scoped access to a merchant's store. This guide walks you through the full handshake: building the authorize URL, handling the redirect, exchanging the code for tokens, and refreshing access before it expires.
The overall flow
- Merchant clicks Install on your app's listing.
- Their admin redirects to your authorize URL with the app's
client_id, requestedscope, yourredirect_uri, and a CSRFstate. - Merchant approves the scopes; LaunchMyStore redirects back to your
redirect_uriwith a one-timecodeand the samestate. - Your server exchanges
codefor an access token (24h lifetime) and a refresh token (30 day lifetime). - Your server uses the access token to call
/api/v1/*endpoints. When it nears expiry, exchange the refresh token for a fresh pair.
Authorization codes themselves are short-lived (10 minutes in Redis) and one-time use — consume them immediately.
Step 1: Build the authorize URL
https://api.launchmystore.io/apps/oauth/authorize?
client_id=YOUR_API_KEY&
scope=read_products,write_products,read_orders&
redirect_uri=https://app.example.com/oauth/callback&
state=RANDOM_CSRF_TOKEN&
shop=MERCHANT_STORE_HANDLE
Always generate a fresh, unguessable state per install (e.g. crypto.randomBytes(32).toString('hex')) and store it server-side keyed by session. Verify it matches when the merchant returns — this prevents CSRF.
Step 2: Handle the redirect
The merchant lands on your redirect_uri with:
https://app.example.com/oauth/callback?
code=ONE_TIME_CODE&
state=RANDOM_CSRF_TOKEN&
shop=merchant-store
Verify state matches what you stored. If not, abort — you're being attacked.
Step 3: Exchange code for tokens
POST https://api.launchmystore.io/apps/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"client_id": "YOUR_API_KEY",
"client_secret": "YOUR_CLIENT_SECRET",
"code": "ONE_TIME_CODE",
"redirect_uri": "https://app.example.com/oauth/callback"
}
Successful response:
{
"access_token": "lms_at_...",
"token_type": "Bearer",
"expires_in": 86400,
"refresh_token": "lms_rt_...",
"scope": "read_products,write_products,read_orders"
}
Store both tokens, keyed by the merchant’s store handle (or merchant id). Store securely — they grant the same access as a logged-in merchant within the requested scopes.
Step 4: Call the API
fetch('https://api.launchmystore.io/api/v1/products', {
headers: { Authorization: 'Bearer lms_at_...' },
});
All app-scoped endpoints live under /api/v1/. They enforce the scope you were granted and apply tier-based rate limiting (FREE 20 req/s, BASIC 40, PRO 100, ENTERPRISE 500) via a sliding window per app + store.
Step 5: Refresh before expiry
Access tokens last 24h. Before they expire (or on the first 401), exchange the refresh token:
POST https://api.launchmystore.io/apps/oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"client_id": "YOUR_API_KEY",
"client_secret": "YOUR_CLIENT_SECRET",
"refresh_token": "lms_rt_..."
}
You'll receive a fresh access token and a fresh refresh token. Discard the old pair. The refresh window resets to 30 days from the new exchange, so an active app effectively stays installed forever.
Scopes catalog
Request only what you need — reviewers reject apps that ask for surplus scope, and merchants see the full list at install time. Common scopes:
read_products,write_productsread_orders,write_ordersread_customers,write_customersread_metafields,write_metafields— required for both definitions and values.read_inventory,write_inventoryread_themes,write_themesread_discounts,write_discountsread_checkoutsread_analytics
The full list lives in the developer documentation. Write scopes implicitly grant read, so you don't need to request both.
Webhooks and uninstall
The host fires an app/uninstalled webhook (one of 31 supported topics) when a merchant removes your app. Treat it as authoritative — the access token is revoked immediately. Webhooks have 3 retries with exponential backoff (1m / 5m / 15m), so reply 200 as soon as you've queued the work.
FAQ
What scopes does my app need to read metafields?
read_metafields for read access to both definitions and values; add write_metafields if you need to create or update either. Scopes are shared between definitions and value endpoints — one pair covers both.
How long does the install code stay valid?
10 minutes, and it's one-time-use. The state is stored in Redis with that TTL. Always exchange immediately on receipt — don't queue it.
Can I have more than one redirect URI?
Yes. List every variant in your app configuration (production, staging, localhost). The token endpoint requires the redirect_uri to exactly match one of the registered ones — trailing slashes count.
What happens if my refresh token expires?
After 30 days of inactivity, the refresh token expires and you'll get an invalid_grant error. You'll need the merchant to reinstall (re-run the authorize flow). Plan to refresh well before the 30-day window if your app is dormant for long stretches.
How do I revoke a token?
Call the uninstall endpoint or simply uninstall the app. Tokens are also revoked automatically when the merchant uninstalls. There's no "revoke single token" endpoint — rotating client_secret invalidates every JWT signed with it, which is the closest equivalent.
Why am I getting 429 errors?
You're exceeding your tier's rate limit on /api/v1/. Each app+store pair has a per-second sliding window: FREE 20, BASIC 40, PRO 100, ENTERPRISE 500. Back off and retry; or upgrade your tier in the developer portal.