Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fluveo.com/llms.txt

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

Fluveo apps use the OAuth 2.0 authorization code flow to obtain access tokens on behalf of merchants. This guide covers every step with detailed curl examples.

Flow overview

1. Your app redirects the merchant to the Fluveo authorization page
2. The merchant reviews scopes and approves (or denies)
3. Fluveo redirects back to your app with an authorization code
4. Your app exchanges the code for an access token and refresh token
5. Your app uses the access token to call the API
6. When the access token expires, use the refresh token to get a new one

Step 1: Redirect to authorization

Build the authorization URL and redirect the merchant’s browser to it. The Fluveo dashboard renders a consent screen showing your app’s name and requested scopes. The authorization URL format is:
https://dashboard.leanrails.com/marketplace/authorize
  ?client_id=ca_live_abc123def456
  &redirect_uri=https://yourapp.example.com/oauth/callback
  &scope=read_products,read_inventory,write_inventory
  &state=random_csrf_token_123
ParameterRequiredDescription
client_idYesYour app’s OAuth client ID
redirect_uriYesMust match one of your registered redirect URIs
scopeYesComma-separated list of requested scopes
stateRecommendedAn opaque value to prevent CSRF attacks. Returned unchanged in the callback.

Step 2: Merchant grants or denies

When the merchant clicks “Approve”, the Fluveo API creates an authorization code and returns a redirect URL.

Grant (server-side call from the dashboard)

curl -X POST https://api.leanrails.com/v1/oauth/authorize/grant \
  -H "Content-Type: application/json" \
  -H "X-Merchant-ID: merch_abc123" \
  -d '{
    "client_id": "ca_live_abc123def456",
    "scopes": ["read_products", "read_inventory", "write_inventory"],
    "redirect_uri": "https://yourapp.example.com/oauth/callback",
    "state": "random_csrf_token_123"
  }'
Response:
{
  "redirect_url": "https://yourapp.example.com/oauth/callback?code=authcode_abc123def456&state=random_csrf_token_123",
  "code": "authcode_abc123def456"
}
The merchant’s browser is redirected to your redirect_uri with the code and state parameters.

Deny

If the merchant clicks “Deny”:
curl -X POST https://api.leanrails.com/v1/oauth/authorize/deny \
  -H "Content-Type: application/json" \
  -H "X-Merchant-ID: merch_abc123" \
  -d '{
    "redirect_uri": "https://yourapp.example.com/oauth/callback",
    "state": "random_csrf_token_123"
  }'
Response:
{
  "redirect_url": "https://yourapp.example.com/oauth/callback?error=access_denied&error_description=The+merchant+denied+the+authorization+request.&state=random_csrf_token_123"
}
Your app should handle the error=access_denied case gracefully and show an appropriate message.

Step 3: Exchange the authorization code for tokens

When your callback URL receives the code parameter, exchange it for an access token and refresh token. Authenticate with your client_id and client_secret via HTTP Basic Auth.
curl -X POST https://api.leanrails.com/v1/oauth/token \
  -u "ca_live_abc123def456:cs_live_secret_value" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&code=authcode_abc123def456&redirect_uri=https://yourapp.example.com/oauth/callback"
Response:
{
  "access_token": "at_live_abc123def456ghi789",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "rt_live_xyz789abc123def456",
  "scope": "read_products read_inventory write_inventory",
  "merchant_id": "merch_abc123"
}
Authorization codes are single-use and expire after 10 minutes. Exchange them immediately.
You can also pass client credentials in the request body instead of Basic Auth:
curl -X POST https://api.leanrails.com/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&code=authcode_abc123def456&redirect_uri=https://yourapp.example.com/oauth/callback&client_id=ca_live_abc123def456&client_secret=cs_live_secret_value"

Step 4: Use the access token

Make API calls on behalf of the merchant using the access token as a Bearer token:
curl https://api.leanrails.com/v1/products?limit=10 \
  -H "Authorization: Bearer at_live_abc123def456ghi789"
The token’s scopes limit which endpoints are accessible. Attempting to access an endpoint outside the granted scopes returns a 403 error.

Step 5: Refresh the access token

Access tokens expire after the duration specified in expires_in (typically 1 hour). Use the refresh token to obtain a new access token without requiring the merchant to re-authorize.
curl -X POST https://api.leanrails.com/v1/oauth/token \
  -u "ca_live_abc123def456:cs_live_secret_value" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token&refresh_token=rt_live_xyz789abc123def456"
Response:
{
  "access_token": "at_live_new_token_abc123",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "rt_live_new_refresh_xyz789",
  "scope": "read_products read_inventory write_inventory",
  "merchant_id": "merch_abc123"
}
Each refresh returns a new refresh token. Always store the latest refresh token and discard the old one.

Step 6: Revoke a token

When a merchant uninstalls your app or you need to invalidate tokens, call the revoke endpoint:
curl -X POST https://api.leanrails.com/v1/oauth/revoke \
  -u "ca_live_abc123def456:cs_live_secret_value" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=at_live_abc123def456ghi789"
Response:
{
  "revoked": true
}

Error handling

Token exchange errors return standard OAuth error responses:
ErrorDescription
invalid_clientThe client_id or client_secret is incorrect.
invalid_requestMissing required parameters like code or redirect_uri.
invalid_grantThe authorization code is expired, already used, or invalid.
unsupported_grant_typeOnly authorization_code and refresh_token are supported.
invalid_scopeOne or more requested scopes are not declared by the app.
Example error response:
{
  "error": "invalid_client",
  "error_description": "Client credentials required."
}

Security best practices

  • Always validate the state parameter in the callback to prevent CSRF attacks.
  • Store client_secret and refresh tokens in a secure secrets manager, never in client-side code.
  • Exchange authorization codes immediately — they expire in 10 minutes.
  • Implement token refresh proactively before the access token expires.
  • Use HTTPS for all redirect URIs.

Next steps