Skip to content

Getting Started

This guide walks you through authenticating with the Identity API and making your first authenticated API call.

Prerequisites

Before you begin, ensure you have:

  • A ShopHero service account with client credentials
  • Access to a backend server (Node.js, PHP, Python, etc.)
  • An understanding of OAuth 2.0 client credentials flow

Step 1: Obtain Credentials

Service account credentials are created by your organization administrator through the CommerceCore admin interface.

When a service account is created, you'll receive:

json
{
  "client_id": "9a1b2c3d-4e5f-6789-abcd-ef0123456789",
  "client_secret": "Wk3mPqR7vX9bN2cF5hJ8tL0wA4sD6gY1uI3oE",
  "grant_type": "client_credentials",
  "scopes": ["engagehq:content.view", "engagehq:circulars.view"]
}

Store Credentials Securely

The client_secret is only shown once at creation time. Store it immediately in a secure location like:

  • Environment variables
  • Secret management service (AWS Secrets Manager, HashiCorp Vault)
  • Encrypted configuration files

Never commit credentials to source control.

Step 2: Request an Access Token

Exchange your credentials for a JWT access token:

Request

http
POST /oauth/token HTTP/1.1
Host: identity.retailsuccessplatform.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=9a1b2c3d-4e5f-6789-abcd-ef0123456789&client_secret=Wk3mPqR7vX9bN2cF5hJ8tL0wA4sD6gY1uI3oE&scope=engagehq:content.view engagehq:circulars.view

cURL Example

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=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=engagehq:content.view engagehq:circulars.view"

Response

json
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "expires_at": 1705411200
}

Step 3: Use the Token

Include the access token in the Authorization header of API requests:

bash
curl https://api.engagehq.retailsuccessplatform.com/public/v1/content \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."

Code Examples

JavaScript / Node.js

javascript
// token-service.js
class TokenService {
  constructor(clientId, clientSecret) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.baseUrl = process.env.IDENTITY_URL || 'https://identity.retailsuccessplatform.com';
    this.token = null;
    this.tokenExpiry = null;
  }

  async getToken() {
    // Return cached token if still valid (with 5 min buffer)
    if (this.token && this.tokenExpiry > Date.now() + 300000) {
      return this.token;
    }

    const response = await fetch(`${this.baseUrl}/oauth/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: this.clientId,
        client_secret: this.clientSecret,
        scope: 'engagehq:content.view engagehq:circulars.view',
      }),
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Token request failed: ${error.message || response.statusText}`);
    }

    const data = await response.json();
    this.token = data.access_token;
    this.tokenExpiry = Date.now() + (data.expires_in * 1000);

    return this.token;
  }
}

// Usage
const tokenService = new TokenService(
  process.env.SHOPHERO_CLIENT_ID,
  process.env.SHOPHERO_CLIENT_SECRET
);

async function fetchContent() {
  const token = await tokenService.getToken();

  const response = await fetch('https://api.engagehq.retailsuccessplatform.com/public/v1/content', {
    headers: {
      'Authorization': `Bearer ${token}`,
    },
  });

  return response.json();
}

PHP

php
<?php

class TokenService
{
    private string $clientId;
    private string $clientSecret;
    private string $baseUrl;
    private ?string $token = null;
    private ?int $tokenExpiry = null;

    public function __construct(string $clientId, string $clientSecret)
    {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->baseUrl = getenv('IDENTITY_URL') ?: 'https://identity.retailsuccessplatform.com';
    }

    public function getToken(): string
    {
        // Return cached token if still valid (with 5 min buffer)
        if ($this->token && $this->tokenExpiry > time() + 300) {
            return $this->token;
        }

        $ch = curl_init("{$this->baseUrl}/oauth/token");

        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
            CURLOPT_POSTFIELDS => http_build_query([
                'grant_type' => 'client_credentials',
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
                'scope' => 'engagehq:content.view engagehq:circulars.view',
            ]),
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            throw new Exception("Token request failed with status {$httpCode}");
        }

        $data = json_decode($response, true);
        $this->token = $data['access_token'];
        $this->tokenExpiry = time() + $data['expires_in'];

        return $this->token;
    }
}

// Usage
$tokenService = new TokenService(
    getenv('SHOPHERO_CLIENT_ID'),
    getenv('SHOPHERO_CLIENT_SECRET')
);

$token = $tokenService->getToken();

$content = file_get_contents(
    'https://api.engagehq.retailsuccessplatform.com/public/v1/content',
    false,
    stream_context_create([
        'http' => [
            'header' => "Authorization: Bearer {$token}",
        ],
    ])
);

Python

python
import os
import time
import requests

class TokenService:
    def __init__(self, client_id: str, client_secret: str):
        self.client_id = client_id
        self.client_secret = client_secret
        self.base_url = os.getenv('IDENTITY_URL', 'https://identity.retailsuccessplatform.com')
        self.token = None
        self.token_expiry = None

    def get_token(self) -> str:
        # Return cached token if still valid (with 5 min buffer)
        if self.token and self.token_expiry > time.time() + 300:
            return self.token

        response = requests.post(
            f'{self.base_url}/oauth/token',
            data={
                'grant_type': 'client_credentials',
                'client_id': self.client_id,
                'client_secret': self.client_secret,
                'scope': 'engagehq:content.view engagehq:circulars.view',
            },
            headers={'Content-Type': 'application/x-www-form-urlencoded'}
        )

        response.raise_for_status()
        data = response.json()

        self.token = data['access_token']
        self.token_expiry = time.time() + data['expires_in']

        return self.token

# Usage
token_service = TokenService(
    os.getenv('SHOPHERO_CLIENT_ID'),
    os.getenv('SHOPHERO_CLIENT_SECRET')
)

token = token_service.get_token()

response = requests.get(
    'https://api.engagehq.retailsuccessplatform.com/public/v1/content',
    headers={'Authorization': f'Bearer {token}'}
)

content = response.json()

Token Caching Best Practices

  1. Cache tokens in memory - Don't request a new token for every API call
  2. Refresh before expiry - Request a new token when the current one has less than 5 minutes remaining
  3. Handle 401 errors - If you receive a 401 Unauthorized, your token may have expired early; request a new one
  4. Don't share tokens - Each backend instance should manage its own token cache

Next Steps


Changelog
DateChange
2026-02-27Documentation corrections.
2026-01-15Initial publication.

ShopHero CommerceCore Platform