OAuth apps
Publish an integration that any ShopFast store can connect, with OAuth 2.1, PKCE, scoped tokens and rotating refresh tokens.
Use OAuth when other sellers install your integration. For your own store, an API key is simpler.
Register an app
In Settings → Developers → OAuth apps, choose New app and fill in:
- Name, description, homepage: shown on the consent screen with your store’s name as the publisher.
- Redirect URIs: matched exactly, character for character.
httpsonly, excepthttp://127.0.0.1,http://localhostandhttp://[::1]for native and CLI apps. No fragments. - The most it may ask for: scopes per area, like API keys. A request for more is quietly capped.
You get a client id (sfc_…) and a client secret (sfcs_…, shown once). Rotating the secret stops the old one
at once. Turning the app off stops every token it issued, everywhere, until you turn it back on.
1. Send the seller to the consent screen
Create a PKCE verifier (43 to 128 random URL-safe characters) and its S256 challenge, then redirect:
GET https://app.shopfa.st/oauth/authorize
?client_id=sfc_…
&redirect_uri=https://your-app.example/callback
&response_type=code
&scope=catalog:read orders:read
&state=<random, tied to the browser session>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S256
The seller signs in if needed, picks one of their stores (owners and admins can approve) and sees exactly what your
app may do. Cancel redirects with error=access_denied.
2. Exchange the code
On approval the browser lands on your redirect URI with code, your state and store_id. Check state, then
exchange the code within 10 minutes. It works once.
curl -X POST https://api.shopfa.st/v1/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://your-app.example/callback \
-d code_verifier="$VERIFIER"
const res = await fetch('https://api.shopfa.st/v1/oauth/token', {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code', code, redirect_uri: REDIRECT_URI, code_verifier: verifier,
client_id: CLIENT_ID, client_secret: CLIENT_SECRET,
}),
});
const tokens = await res.json(); // { access_token, refresh_token, expires_in: 3600, scope, store_id }
{
"access_token": "sfo_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "sfr_…",
"scope": "catalog:read orders:read",
"store_id": "st_…"
}
Client authentication works with HTTP Basic or client_id / client_secret in the body. The body can be form-encoded
or JSON.
3. Call the API
curl https://api.shopfa.st/v1/store/products -H "Authorization: Bearer sfo_…"
The token reaches only the store that approved it and only the granted scopes, and it acts as the teammate who
approved. Because it names its store, /v1/store/… needs no store id; the store_id in the token response is
there if you want the explicit form (/v1/stores/st_…/products). Calls show up in that store’s request log under your app’s name.
4. Refresh
Access tokens last an hour. Refresh before they expire:
curl -X POST https://api.shopfa.st/v1/oauth/token -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=refresh_token -d refresh_token="sfr_…"
Every refresh returns a new refresh token and retires the old one. Presenting a retired refresh token again means someone else has a copy: ShopFast revokes the whole connection and the store has to approve your app again. Store refresh tokens like passwords and refresh from one place at a time.
Revoke
POST /v1/oauth/revoke with token and your client credentials (RFC 7009). Revoking a refresh token ends the
connection; revoking an access token ends just that token. Sellers can disconnect your app from
Settings → Developers → OAuth apps → Connected apps at any time.
Errors
The token endpoint answers RFC 6749 errors: invalid_request, invalid_client (401), invalid_grant (bad, used or
expired code, mismatched redirect_uri or code_verifier, retired refresh token) and slow_down (429).
Clients without a secret, and MCP
Native, desktop and CLI apps cannot keep a secret. They can register themselves and connect with PKCE alone:
curl -X POST https://api.shopfa.st/v1/oauth/register -H "Content-Type: application/json" \
-d '{"client_name":"My CLI","redirect_uris":["http://127.0.0.1:53682/callback"],"token_endpoint_auth_method":"none"}'
Redirect URIs may be https, http on a loopback address (any port, per RFC 8252), or a private-use scheme such as
myapp://callback. Such clients show as Unverified on the consent screen, with the address the browser goes back
to. Exchange the code with client_id and code_verifier only.
Everything a client needs is in the metadata: https://api.shopfa.st/.well-known/oauth-authorization-server
(RFC 8414). A client whose client_id is an https URL to its own metadata document is registered from it on first use.
To call the MCP server, send resource=https://api.shopfa.st/mcp (RFC 8707) when you authorize and
exchange the code. Those tokens work on the MCP server only, and the MCP server accepts no other token; AI tools that
speak MCP do all of this for you.