Docs
Dashboard
View as Markdown
On this page
  1. Register an app
  2. 1. Send the seller to the consent screen
  3. 2. Exchange the code
  4. 3. Call the API
  5. 4. Refresh
  6. Revoke
  7. Errors
  8. Clients without a secret, and MCP

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. https only, except http://127.0.0.1, http://localhost and http://[::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.

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.

Was this page helpful?