Shogun protects every API call with a Bearer JWT. For server-to-server integrations you exchange a client_id and client_secret for a token that lasts one hour. A separate Dashboard API exists for browser and mobile sessions and uses email/password login instead. This page covers both flows, explains how to attach a token to requests, and shows you how to verify the HMAC-SHA256 signatures Shogun adds to outbound webhooks.
Two authentication flows
Shogun has two separate APIs with different credential types:
| Integration API | Dashboard API |
|---|
| Who uses it | Your backend server | Your web or mobile app |
| Credentials | client_id + client_secret | Email + password |
| Token endpoint | POST /api/v1/security/api/generate_token | POST /api/v1/auth/web/login |
| Token type | Bearer JWT | Bearer JWT (+ HTTP-only cookie) |
| Token TTL | 1 hour | Configurable |
The rest of this page focuses on the Integration API, which is what you use for backend server-to-server calls.
Your API credentials
Find your credentials in Dashboard → Security → API Client.
| Field | Description |
|---|
client_id | Public identifier for your business — safe to log |
client_secret | Secret key — treat like a password, never expose publicly |
If your client_secret is ever compromised, rotate it immediately from the dashboard. The old secret is invalidated instantly. Rotation requires password confirmation.
Generating a token
Send your credentials to the token endpoint to receive a Bearer JWT:
curl -X POST https://api.shogun.io/api/v1/security/api/generate_token \
-H "Content-Type: application/json" \
-d '{
"client_id": "your-client-id",
"client_secret": "your-client-secret"
}'
A successful response:
{
"status": true,
"response_code": "00",
"message": "Token generated successfully",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
}
Cache the token value server-side. Tokens expire after 3600 seconds (1 hour). Generate a new one before expiry rather than waiting for a 401 or response_code: "42" error on a live request.
Using the token
Pass the token as a Bearer value in the Authorization header on every authenticated request:
curl https://api.shogun.io/api/v1/account/api/fetch_customer_accounts \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Validating a token
Before making a sensitive request, you can confirm a cached token is still valid:
curl -X GET https://api.shogun.io/api/v1/security/api/validate_token \
-H "Authorization: Bearer <your-token>"
A valid token returns token metadata including its issued_at and expires_at timestamps:
{
"status": true,
"message": "Token is valid",
"data": {
"username": "your-username",
"client_id": "your-client-id",
"authorities": ["TRANSACTION_READ", "TRANSACTION_WRITE"],
"issued_at": "2024-01-15T10:30:00",
"expires_at": "2024-01-16T10:30:00"
}
}
Dashboard API auth
If you are building a web or mobile dashboard on top of Shogun, use the Dashboard API login endpoint instead. It accepts an email/password pair and returns a JWT you pass as a Bearer token on all /web/ endpoints:
curl -X POST https://api.shogun.io/api/v1/auth/web/login \
-H "Content-Type: application/json" \
-d '{ "email": "you@business.com", "password": "yourpassword" }'
Token lifecycle
client_id + client_secret
↓
POST /api/v1/security/api/generate_token
↓
Bearer token (1h TTL)
↓
Use on all API calls
↓
Expires → generate a new one
Repeated failed authentication attempts trigger a temporary account lockout. Implement exponential backoff on 401 responses to avoid being locked out during token rotation.
Webhook signature verification
Shogun signs every outbound webhook payload with HMAC-SHA256 using your webhook secret. The signature is sent in the X-Shogun-Signature header in the format sha256=<hex-digest>. You must verify this signature before trusting or processing the event.
Always verify the signature before acting on a webhook payload. Skipping verification exposes your system to spoofed events.
import hmac
import hashlib
def verify_webhook(payload_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature_header)
Pass the raw request body as bytes, the X-Shogun-Signature header value, and your webhook secret. The function returns True only when the signatures match using a timing-safe comparison.const crypto = require('crypto');
function verifyWebhook(payloadBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payloadBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
Pass payloadBody as a Buffer or string containing the raw request body, signatureHeader as the value of X-Shogun-Signature, and your webhook secret string. Returns true when the signatures match.
Use your framework’s raw body parser to read the request body before any JSON parsing. Many frameworks parse JSON by default and discard the raw bytes, which will break signature verification.