Appearance
OAuth Token Endpoint
The OAuth token endpoint issues JWT access tokens for authenticating with ShopHero APIs.
Endpoint
POST /oauth/tokenRequest
Headers
| Header | Value | Required |
|---|---|---|
Content-Type | application/x-www-form-urlencoded | Yes |
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Must be client_credentials |
client_id | string | Yes | Your service account client ID |
client_secret | string | Yes | Your service account client secret |
scope | string | No | Space-separated list of scopes to request |
Example Request
bash
curl -X POST https://identity.retailsuccessplatform.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=9a1b2c3d-4e5f-6789-abcd-ef0123456789" \
-d "client_secret=Wk3mPqR7vX9bN2cF5hJ8tL0wA4sD6gY1uI3oE" \
-d "scope=engagehq:content.view engagehq:circulars.view"Response
Success Response (200 OK)
json
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 86400,
"expires_at": 1705411200
}| Field | Type | Description |
|---|---|---|
access_token | string | JWT token to use for API authentication |
token_type | string | Always Bearer |
expires_in | integer | Token validity in seconds (86400 = 24 hours) |
expires_at | integer | Expiration as Unix timestamp |
Error Responses
Invalid Grant Type (400)
json
{
"error": "unsupported_grant_type",
"error_description": "The authorization grant type is not supported. Use 'client_credentials'."
}Missing Parameters (400)
json
{
"error": "invalid_request",
"error_description": "The request is missing required parameters: client_id, client_secret"
}Invalid Client (401)
json
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}Revoked Client (401)
json
{
"error": "invalid_client",
"error_description": "The client has been revoked"
}Invalid Scope (400)
json
{
"error": "invalid_scope",
"error_description": "The requested scope is invalid or not authorized"
}Rate Limited (429)
json
{
"error": "rate_limit_exceeded",
"error_description": "Too many requests. Please wait before trying again.",
"retry_after": 60
}Using the Token
Include the access token in the Authorization header of API requests:
http
GET /public/v1/content HTTP/1.1
Host: api.engagehq.retailsuccessplatform.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...cURL Example
bash
curl https://api.engagehq.retailsuccessplatform.com/public/v1/content \
-H "Authorization: Bearer ${ACCESS_TOKEN}"JavaScript Example
javascript
const response = await fetch('https://api.engagehq.retailsuccessplatform.com/public/v1/content', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});Token Expiration
Access tokens expire after 24 hours (86400 seconds). When a token expires:
- API requests return
401 Unauthorized - Request a new token using the same credentials
- Update your cached token
Handling Expiration
javascript
async function makeApiCall(url) {
let response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.status === 401) {
// Token expired, get a new one
token = await getNewToken();
// Retry the request
response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
}
return response.json();
}Rate Limiting
The token endpoint is rate limited to prevent abuse:
| Limit | Value |
|---|---|
| Requests per minute | 60 |
| Per client ID | Yes |
If you exceed the rate limit, implement exponential backoff:
javascript
async function getTokenWithRetry(maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await getToken();
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const waitTime = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
throw error;
}
}
}Scope Filtering
The token endpoint filters requested scopes against the service account's allowed scopes:
- If you request scopes that aren't allowed, you'll receive an
invalid_scopeerror - If you don't specify scopes, all allowed scopes are granted
- You can request a subset of your allowed scopes
Examples
bash
# Request specific scopes
curl -X POST /oauth/token \
-d "scope=engagehq:content.view"
# Request all allowed scopes (no scope parameter)
curl -X POST /oauth/token \
-d "grant_type=client_credentials&client_id=...&client_secret=..."Security Considerations
- Use HTTPS - Never send credentials over unencrypted connections
- Protect secrets - Never expose
client_secretin client-side code - Validate tokens - APIs validate the token signature and expiration
- Monitor usage - Check for unauthorized access in audit logs
- Rotate regularly - Rotate credentials every 90 days
Changelog
| Date | Change |
|---|---|
| 2026-02-27 | Documentation corrections. |
| 2026-01-15 | Initial publication. |