Authentication
The Clopos Open API v2 uses short-lived JWT access tokens. Exchange your credentials at /v2/auth to receive a token, then include it in the x-token header on every subsequent request.
Prerequisites
Before you can authenticate you will need:
- Client ID — your application’s unique identifier
- Client Secret — your application’s secret key
- Brand — the brand identifier you want to access
- Integrator ID — identifies the integrator making the request. New in v2 and required on every auth call.
Request your credentials and an integrator_id from Clopos by filling out this form.
All v2 requests use the base URL https://integrations.clopos.com/open-api/v2.
Authentication flow
Step 1: Obtain an access token
Send a POST request to the v2 auth endpoint:
curl -X POST https://integrations.clopos.com/open-api/v2/auth \
-H "Content-Type: application/json" \
-d '{
"client_id": "{{client_id}}",
"client_secret": "{{client_secret}}",
"brand": "{{brand}}",
"integrator_id": "{{integrator_id}}"
}'
Request body
| Field | Type | Required | Description |
|---|
client_id | string | Yes | Issued by Clopos. |
client_secret | string | Yes | Paired secret for the client. |
brand | string | Yes | Brand identifier. |
integrator_id | string | Yes | Integrator identifier issued by Clopos. |
venue_id is not part of the v2 auth body. The active venue is resolved from the JWT and can optionally be overridden per-request with the x-venue header.
Step 2: Inspect the response
A successful authentication returns a signed JWT:
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"expires_at": 1767852332,
"message": "Authentication successful"
}
| Field | Type | Description |
|---|
token | string | The JWT. Include it verbatim in the x-token header. |
token_type | string | Always Bearer. |
expires_in | integer | Token lifetime in seconds (typically 3600). |
expires_at | integer | Unix timestamp after which the token is rejected. |
The JWT encodes your brand, venue_id, integrator_id, and upstream auth state, so you do not need to send them as separate headers.
Step 3: Call an authenticated endpoint
Include the JWT in the x-token header. That is the only header required on v2 endpoints.
curl -X GET "https://integrations.clopos.com/open-api/v2/products" \
-H "x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Required header
x-token — the JWT returned by /v2/auth.
Optional header
x-venue — override the venue encoded in the JWT for this request. Useful when a single integrator operates across multiple venues.
Token management
Tokens expire after expires_in seconds (typically 1 hour). Refresh them before they expire to avoid request failures.
Best practices
- Store tokens securely — never expose them in client-side code or commit them to source control.
- Refresh proactively — re-authenticate before
expires_at rather than waiting for a 401.
- Handle errors gracefully — on any
401, re-authenticate and retry once.
- Use HTTPS only — never send credentials over unencrypted connections.
Error handling
Errors from /v2/auth
| Status | Body | Cause |
|---|
400 Bad Request | { "success": false, "error": "Missing client_id, client_secret, brand, or integrator_id" } | One or more required fields were omitted. |
400 Bad Request | { "success": false, "error": "Invalid integrator_id" } | The integrator_id is unknown or inactive. |
200 OK | { "success": false, "error": "Integrator is in test mode. But brand is not in test mode", "brand": "...", "integrator": "..." } | The integrator is flagged as test-only, but the brand is in production stage. Switch to a production integrator or a test brand. Note this is returned as a 200 with success: false, not a 4xx. |
401 Unauthorized | { "success": false, "message": "..." } | Upstream rejected the client_id / client_secret / brand combination. |
Errors from authenticated endpoints
| Status | Body | Cause |
|---|
401 Unauthorized | { "success": false, "error": "Headers are missing" } | The x-token header was not sent. |
401 Unauthorized | { "success": false, "error": "Invalid token" } | The JWT is malformed or its signature does not verify. |
401 Unauthorized | { "success": false, "expires_at": "2026-01-01T12:00:00.000Z", "error": "Token expired" } | The JWT expired. Re-authenticate to get a new one. Note that expires_at in this error is an ISO 8601 string, unlike the Unix timestamp returned by /v2/auth. |
401 Unauthorized | { "success": false, "error": "Invalid integrator_id" } | The integrator_id encoded in the token is no longer active. |
401 Unauthorized | { "success": false, "error": "Integrator is in test mode. But brand is not in test mode", "brand": "...", "integrator": "..." } | Same test/production mismatch as above, enforced on every authenticated call. |
Code examples
const BASE_URL = 'https://integrations.clopos.com/open-api/v2';
async function authenticate() {
const response = await fetch(`${BASE_URL}/auth`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: '{{client_id}}',
client_secret: '{{client_secret}}',
brand: '{{brand}}',
integrator_id: '{{integrator_id}}'
})
});
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'Authentication failed');
}
return { token: data.token, expiresAt: data.expires_at };
}
async function makeAuthenticatedRequest(path, token, options = {}) {
return fetch(`${BASE_URL}${path}`, {
...options,
headers: {
'x-token': token,
'Content-Type': 'application/json',
...options.headers
}
});
}
Next steps
Once you have your access token you can start making API requests: