GoSmarter API supports two authentication methods. Choose based on how your code runs.
Which method do I need?
| Method | Header | Best for |
|---|
| API key | GoSmarter-Api-Key: zpka_... | Scripts, CI/CD, server-to-server, automation |
| Bearer token | Authorization: Bearer eyJ... | Interactive apps, user-delegated access |
Both are accepted on all endpoints. The gateway enforces the same permissions for either path.
API keys
The quickest option for programmatic access — no token exchange required.
GET /api/company/{companyId}/millcert/certificates
GoSmarter-Api-Key: zpka_your_key_here
See the API Keys guide for creating, rotating, and revoking keys.
Bearer tokens (OAuth 2.0)
Use when your application authenticates on behalf of a user via Microsoft Entra ID (CIAM).
Getting credentials
Contact your GoSmarter administrator to obtain:
- Client ID — your application's unique identifier
- Client Secret — keep this secure, never commit to source control
- Tenant ID —
36078520-1cde-40ad-940e-6f26f1a90414
- API Scope —
api://0399c3db-81cb-4a1e-8ff6-9fcfca8dec21/access_as_user
Never expose your client secret in client-side code or public repositories. Store it in environment variables or a secret management system.
Requesting a token
curl --request POST \
--url 'https://36078520-1cde-40ad-940e-6f26f1a90414.ciamlogin.com/36078520-1cde-40ad-940e-6f26f1a90414/oauth2/v2.0/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=YOUR_CLIENT_ID' \
--data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
--data-urlencode 'scope=api://0399c3db-81cb-4a1e-8ff6-9fcfca8dec21/access_as_user'
Response:
{
"token_type": "Bearer",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1yNS1BVWl..."
}
Using the token
curl "https://api.gosmarter.ai/api/company/YOUR_COMPANY_ID/millcert/certificates" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Code examples
JavaScript / Node.js
async function getAccessToken() {
const params = new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: process.env.API_SCOPE,
});
const response = await fetch(process.env.TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params,
});
const { access_token } = await response.json();
return access_token;
}
async function callAPI(companyId) {
const token = await getAccessToken();
const response = await fetch(
`https://api.gosmarter.ai/api/company/${companyId}/millcert/certificates`,
{ headers: { Authorization: `Bearer ${token}` } }
);
return response.json();
}
Python
import requests, os
def get_access_token():
resp = requests.post(
os.environ["TOKEN_URL"],
data={
"grant_type": "client_credentials",
"client_id": os.environ["CLIENT_ID"],
"client_secret": os.environ["CLIENT_SECRET"],
"scope": os.environ["API_SCOPE"],
},
)
resp.raise_for_status()
return resp.json()["access_token"]
def call_api(company_id):
token = get_access_token()
resp = requests.get(
f"https://api.gosmarter.ai/api/company/{company_id}/millcert/certificates",
headers={"Authorization": f"Bearer {token}"},
)
resp.raise_for_status()
return resp.json()
C#
using System.Net.Http.Headers;
public class GoSmarterClient
{
private readonly HttpClient _http;
private readonly string _tokenUrl;
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _scope;
public async Task<string> GetAccessTokenAsync()
{
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = _clientId,
["client_secret"] = _clientSecret,
["scope"] = _scope,
});
var resp = await _http.PostAsync(_tokenUrl, content);
resp.EnsureSuccessStatusCode();
var result = await resp.Content.ReadFromJsonAsync<TokenResponse>();
return result!.AccessToken;
}
public async Task<string> CallAPIAsync(string companyId)
{
var token = await GetAccessTokenAsync();
_http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var resp = await _http.GetAsync(
$"https://api.gosmarter.ai/api/company/{companyId}/millcert/certificates");
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsStringAsync();
}
}
Token caching
Tokens expire after 1 hour. Cache the token and request a new one proactively — don't fetch a new token per request.
let cachedToken = null;
let expiresAt = 0;
async function getToken() {
if (cachedToken && Date.now() < expiresAt - 60_000) return cachedToken;
const resp = await fetch(process.env.TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: process.env.API_SCOPE,
}),
});
const { access_token, expires_in } = await resp.json();
cachedToken = access_token;
expiresAt = Date.now() + expires_in * 1000;
return cachedToken;
}
Troubleshooting
401 Unauthorized
- API key: verify the key is valid — visit Settings → API Keys in this portal
- Bearer token: the token may have expired; request a new one
- Check the header name is exact:
GoSmarter-Api-Key or Authorization: Bearer <token>
401 Unauthorized — "API key metadata is missing userId"
- The key was created before user metadata was available. Delete and recreate the key from Settings → API Keys.
403 Forbidden — "Token missing required scope: access_as_user"
- The Bearer token was issued without the required scope. Re-authenticate including
scope=api://0399c3db-81cb-4a1e-8ff6-9fcfca8dec21/access_as_user.
Last modified on