Authorization
Secure API access using API Keys, OAuth 2.0, or plugin authorization
Overview
The system supports three authorization methods to authenticate API requests and access resources securely. Choose the method that best fits your integration requirements and security needs.
Authorization Methods
API Key
Simplest method for internal integrations and scripts. Static, long-lived credentials.
Recommended for: Internal tools
OAuth 2.0
Industry-standard protocol for third-party applications with user consent and token management.
Recommended for: External apps
Plugin Authorization
Automatic credentials provided to plugins. No manual configuration required.
Recommended for: Plugins
API Key Authorization
The simplest authorization method using a static API key for authentication.
How It Works
API Keys are long-lived, static credentials that identify your application. They’re stored directly in the database (encrypted) and are perfect for server-to-server integrations, internal tools, and scripts where you control the environment.
**Security Note:** API Keys should be kept secret and never exposed in client-side code or public repositories. They provide full access to your account.
Creating an API Key
- Navigate to API Keys page
- Click “Create New API Key”
- Set a descriptive name and optional expiration date
- Copy the generated key immediately (it won’t be shown again)
- Store the key securely in your application configuration
Usage Examples
TypeScript - Using Query API
const API_KEY = 'your_api_key_here';
const API_URL = 'https://your-domain.com';
async function fetchProducts() {
try {
const response = await fetch(
`${API_URL}/api/query`,
{
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: `products {
id,
name,
price,
stock
}`
})
}
);
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
const data = await response.json();
return data.products;
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
// Usage
fetchProducts().then(products => {
console.log(`Found ${products.length} products`);
products.forEach(p => {
console.log(`${p.name}: ${p.price}`);
});
});
TypeScript - Using OData API
const API_KEY = 'your_api_key_here';
const API_URL = 'https://your-domain.com';
async function fetchProductsOData() {
try {
const params = new URLSearchParams({
'$select': 'id,name,price,stock',
'$filter': 'stock gt 0',
'$top': '100',
'$orderby': 'name asc'
});
const response = await fetch(
`${API_URL}/odata/products?${params}`,
{
headers: {
'X-API-Key': API_KEY
}
}
);
if (!response.ok) {
throw new Error(`OData Error: ${response.status}`);
}
const data = await response.json();
return data.value;
} catch (error) {
console.error('OData Error:', error);
throw error;
}
}
// Usage
fetchProductsOData().then(products => {
console.log('In-stock products:', products);
});
TypeScript - Using Import API
import fs from 'fs';
const API_KEY = 'your_api_key_here';
const API_URL = 'https://your-domain.com';
async function importProducts(filePath: string) {
const formData = new FormData();
const fileContent = fs.readFileSync(filePath);
const blob = new Blob([fileContent]);
formData.append('file', blob, 'products.csv');
formData.append('entity', 'products');
formData.append('mapperId', 'your-mapper-id');
try {
const response = await fetch(
`${API_URL}/api/import`,
{
method: 'POST',
headers: {
'X-API-Key': API_KEY
},
body: formData
}
);
if (!response.ok) {
throw new Error(`Import Error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Import Error:', error);
throw error;
}
}
// Usage
importProducts('./products.csv').then(result => {
console.log(`Import completed: ${result.imported} imported`);
});
PHP - Using Query API
<?php
$apiKey = 'your_api_key_here';
$apiUrl = 'https://your-domain.com';
function fetchProducts($apiKey, $apiUrl) {
$query = [
'query' => 'products { id, name, price, stock }'
];
$ch = curl_init($apiUrl . '/api/query');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($query));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('API Error: ' . $httpCode);
}
$data = json_decode($response, true);
return $data['products'];
}
// Usage
try {
$products = fetchProducts($apiKey, $apiUrl);
echo "Found " . count($products) . " products\n";
foreach ($products as $product) {
echo "{$product['name']}: {$product['price']}\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
PHP - Using OData API
<?php
$apiKey = 'your_api_key_here';
$apiUrl = 'https://your-domain.com';
function fetchProductsOData($apiKey, $apiUrl) {
$params = [
'$select' => 'id,name,price,stock',
'$filter' => 'stock gt 0',
'$top' => 100,
'$orderby' => 'name asc'
];
$url = $apiUrl . '/odata/products?' . http_build_query($params);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . $apiKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('OData Error: ' . $httpCode);
}
$data = json_decode($response, true);
return $data['value'];
}
// Usage
try {
$products = fetchProductsOData($apiKey, $apiUrl);
echo "In-stock products: " . count($products) . "\n";
print_r($products);
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
PHP - Using Import API
<?php
$apiKey = 'your_api_key_here';
$apiUrl = 'https://your-domain.com';
function importProducts($apiKey, $apiUrl, $filePath) {
$file = new CURLFile($filePath, 'text/csv', 'products.csv');
$postData = [
'file' => $file,
'entity' => 'products',
'mapperId' => 'your-mapper-id'
];
$ch = curl_init($apiUrl . '/api/import');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . $apiKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('Import Error: ' . $httpCode);
}
return json_decode($response, true);
}
// Usage
try {
$result = importProducts($apiKey, $apiUrl, './products.csv');
echo "Import completed: " . $result['imported'] . " imported\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
Best Practices
✅ Store securely: Use environment variables or secure configuration files
✅ Set expiration: Configure automatic expiration dates for added security
✅ Rotate regularly: Create new keys and deactivate old ones periodically
✅ Use HTTPS: Always use HTTPS to prevent key interception
❌ Never commit: Don’t commit API keys to version control systems
OAuth 2.0 Authorization
Industry-standard protocol for secure delegated access with user consent.
How It Works
OAuth 2.0 is the industry-standard protocol for authorization. It enables third-party applications to obtain limited access to user accounts without exposing passwords. The authorization flow uses short-lived access tokens and long-lived refresh tokens, with secrets stored encrypted in SafeStore using split-knowledge encryption (AES-256-CBC).
**Security:**
OAuth tokens are more secure than API keys. Access tokens are JWT tokens that expire, and secrets are encrypted with both server-side pepper and user-specific encryption keys.
Setup Steps
1. Create a Developer Account
Register as a developer in the system to access OAuth features
2. Create a Developer Application
Create an application with redirect URIs, scopes, and OAuth settings
3. Get Client Credentials
Copy your Client ID and Client Secret from the application settings
4. Implement OAuth Flow
Use the Authorization Code Flow to obtain access tokens for your users
Complete OAuth 2.0 Documentation
For detailed information, see:
- OAuth 2.0 Complete Guide
- OAuth 2.0 Quick Setup Guide
- OAuth 2.0 Implementation Summary
- OAuth Testing Guide (with PHP test script)
OAuth Flow Examples
TypeScript - Step 1: Redirect User to Authorization
import crypto from 'crypto';
const CLIENT_ID = 'your_client_id';
const REDIRECT_URI = 'https://yourapp.com/oauth/callback';
const API_URL = 'https://your-domain.com';
// Generate state parameter for CSRF protection
const state = crypto.randomBytes(16).toString('hex');
sessionStorage.setItem('oauth_state', state);
// Build authorization URL
const authUrl = new URL(`${API_URL}/auth/oauth/authorize`);
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'read write');
authUrl.searchParams.set('state', state);
// Redirect to authorization page
window.location.href = authUrl.toString();
TypeScript - Step 2: Handle Callback and Exchange Code
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';
const REDIRECT_URI = 'https://yourapp.com/oauth/callback';
const API_URL = 'https://your-domain.com';
interface TokenResponse {
access_token: string;
token_type: string;
expires_in: number;
refresh_token?: string;
scope: string;
}
async function handleOAuthCallback(code: string, state: string): Promise<TokenResponse> {
// Verify state parameter
const savedState = sessionStorage.getItem('oauth_state');
if (state !== savedState) {
throw new Error('Invalid state parameter - CSRF attack detected');
}
// Exchange authorization code for tokens
try {
const response = await fetch(
`${API_URL}/auth/oauth/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET
})
}
);
if (!response.ok) {
throw new Error(`Token exchange failed: ${response.status}`);
}
const tokens: TokenResponse = await response.json();
// Store tokens securely
localStorage.setItem('access_token', tokens.access_token);
if (tokens.refresh_token) {
localStorage.setItem('refresh_token', tokens.refresh_token);
}
return tokens;
} catch (error) {
console.error('Token exchange failed:', error);
throw error;
}
}
// Usage in callback route
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
if (code && state) {
handleOAuthCallback(code, state).then(tokens => {
console.log('OAuth successful, access token obtained');
// Now you can make API calls
});
}
TypeScript - Step 3: Making Authenticated Requests
const API_URL = 'https://your-domain.com';
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';
async function makeAuthenticatedRequest(endpoint: string, data?: any) {
const accessToken = localStorage.getItem('access_token');
if (!accessToken) {
throw new Error('No access token available');
}
try {
const response = await fetch(
`${API_URL}${endpoint}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}
);
if (response.status === 401) {
// Token expired, refresh it
await refreshAccessToken();
// Retry request
return makeAuthenticatedRequest(endpoint, data);
}
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return await response.json();
} catch (error) {
throw error;
}
}
// Refresh token when access token expires
async function refreshAccessToken() {
const refreshToken = localStorage.getItem('refresh_token');
if (!refreshToken) {
throw new Error('No refresh token available');
}
const response = await fetch(
`${API_URL}/auth/oauth/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET
})
}
);
if (!response.ok) {
throw new Error(`Token refresh failed: ${response.status}`);
}
const tokens = await response.json();
localStorage.setItem('access_token', tokens.access_token);
return tokens;
}
// Usage
makeAuthenticatedRequest('/api/query', {
query: 'products { id, name, price }'
}).then(data => {
console.log('Products:', data.products);
});
PHP - Step 1: Redirect User to Authorization
<?php
session_start();
$clientId = 'your_client_id';
$redirectUri = 'https://yourapp.com/oauth/callback.php';
$apiUrl = 'https://your-domain.com';
// Generate state parameter for CSRF protection
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;
// Build authorization URL
$params = [
'client_id' => $clientId,
'redirect_uri' => $redirectUri,
'response_type' => 'code',
'scope' => 'read write',
'state' => $state
];
$authUrl = $apiUrl . '/auth/oauth/authorize?' . http_build_query($params);
// Redirect to authorization page
header('Location: ' . $authUrl);
exit;
PHP - Step 2: Handle Callback and Exchange Code
<?php
session_start();
$clientId = 'your_client_id';
$clientSecret = 'your_client_secret';
$redirectUri = 'https://yourapp.com/oauth/callback.php';
$apiUrl = 'https://your-domain.com';
function exchangeCodeForToken($code, $state) {
global $clientId, $clientSecret, $redirectUri, $apiUrl;
// Verify state parameter
if (!isset($_SESSION['oauth_state']) || $state !== $_SESSION['oauth_state']) {
throw new Exception('Invalid state parameter - CSRF attack detected');
}
unset($_SESSION['oauth_state']);
// Prepare token request
$postData = [
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $redirectUri,
'client_id' => $clientId,
'client_secret' => $clientSecret
];
// Exchange code for tokens
$ch = curl_init($apiUrl . '/auth/oauth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('Token exchange failed: ' . $httpCode);
}
$tokens = json_decode($response, true);
// Store tokens securely (in session, database, or secure cookie)
$_SESSION['access_token'] = $tokens['access_token'];
if (isset($tokens['refresh_token'])) {
$_SESSION['refresh_token'] = $tokens['refresh_token'];
}
$_SESSION['token_expires'] = time() + $tokens['expires_in'];
return $tokens;
}
// Handle callback
if (isset($_GET['code']) && isset($_GET['state'])) {
try {
$tokens = exchangeCodeForToken($_GET['code'], $_GET['state']);
echo "OAuth successful! Access token obtained.\n";
echo "Token expires in: " . $tokens['expires_in'] . " seconds\n";
// Redirect to your application
header('Location: /dashboard.php');
exit;
} catch (Exception $e) {
echo "OAuth Error: " . $e->getMessage();
}
} else if (isset($_GET['error'])) {
echo "OAuth Error: " . $_GET['error'];
}
PHP - Step 3: Making Authenticated Requests
<?php
session_start();
$clientId = 'your_client_id';
$clientSecret = 'your_client_secret';
$apiUrl = 'https://your-domain.com';
function makeAuthenticatedRequest($endpoint, $data = null) {
global $apiUrl;
if (!isset($_SESSION['access_token'])) {
throw new Exception('No access token available');
}
// Check if token is expired
if (isset($_SESSION['token_expires']) && time() >= $_SESSION['token_expires']) {
refreshAccessToken();
}
$ch = curl_init($apiUrl . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $_SESSION['access_token'],
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 401) {
// Token expired, refresh and retry
refreshAccessToken();
return makeAuthenticatedRequest($endpoint, $data);
}
if ($httpCode !== 200) {
throw new Exception('API Error: ' . $httpCode);
}
return json_decode($response, true);
}
function refreshAccessToken() {
global $apiUrl, $clientId, $clientSecret;
if (!isset($_SESSION['refresh_token'])) {
throw new Exception('No refresh token available');
}
$postData = [
'grant_type' => 'refresh_token',
'refresh_token' => $_SESSION['refresh_token'],
'client_id' => $clientId,
'client_secret' => $clientSecret
];
$ch = curl_init($apiUrl . '/auth/oauth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('Token refresh failed: ' . $httpCode);
}
$tokens = json_decode($response, true);
$_SESSION['access_token'] = $tokens['access_token'];
$_SESSION['token_expires'] = time() + $tokens['expires_in'];
}
// Usage
try {
$data = makeAuthenticatedRequest('/api/query', [
'query' => 'products { id, name, price }'
]);
echo "Found " . count($data['products']) . " products\n";
foreach ($data['products'] as $product) {
echo $product['name'] . ": " . $product['price'] . "\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Key Features
✅ User consent: Users explicitly authorize your application
✅ JWT tokens: Access tokens are stateless JWT tokens (30-day expiration)
✅ Encrypted storage: Secrets stored with AES-256-CBC split-knowledge encryption
✅ Refresh tokens: Long-lived tokens for obtaining new access tokens
✅ CSRF protection: State parameter prevents cross-site request forgery
Plugin Authorization
Automatic OAuth credentials for plugins with zero configuration.
How It Works
When developing plugins for the system, you don’t need to manually create developer applications or manage OAuth credentials. The system automatically provides your plugin with the necessary client_id and client_secret at runtime through environment variables or configuration.
**Automatic Setup:**
Plugin authorization is handled automatically by the system. Your plugin code simply reads the provided credentials from environment variables.
How Plugins Receive Credentials
When your plugin is loaded, the system provides OAuth credentials through:
OAUTH_CLIENT_ID Your plugin’s unique client identifier
OAUTH_CLIENT_SECRET Your plugin’s secret key for authentication
API_BASE_URL The base URL for making API requests
Plugin Example
TypeScript Plugin
// Plugin receives these credentials automatically
// OAUTH_CLIENT_ID will be provided by the system
// OAUTH_CLIENT_SECRET will be provided by the system
// API_BASE_URL will be provided by the system
const CLIENT_ID = process.env.OAUTH_CLIENT_ID!;
const CLIENT_SECRET = process.env.OAUTH_CLIENT_SECRET!;
const API_BASE_URL = process.env.API_BASE_URL!;
class PluginApiClient {
private accessToken: string | null = null;
private tokenExpiry: number = 0;
async authenticate() {
// Use client credentials grant
const response = await fetch(
`${API_BASE_URL}/auth/oauth/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
scope: 'read write'
})
}
);
if (!response.ok) {
throw new Error(`Authentication failed: ${response.status}`);
}
const tokens = await response.json();
this.accessToken = tokens.access_token;
this.tokenExpiry = Date.now() + (tokens.expires_in * 1000);
}
async makeRequest(endpoint: string, data?: any) {
// Refresh token if expired
if (!this.accessToken || Date.now() >= this.tokenExpiry) {
await this.authenticate();
}
const response = await fetch(
`${API_BASE_URL}${endpoint}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}
);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return await response.json();
}
}
// Usage in your plugin
const client = new PluginApiClient();
export async function pluginFunction() {
const products = await client.makeRequest('/api/query', {
query: 'products { id, name, price }'
});
console.log('Fetched products:', products);
return products;
}
PHP Plugin
<?php
// Plugin receives these credentials automatically
// OAUTH_CLIENT_ID will be provided by the system
// OAUTH_CLIENT_SECRET will be provided by the system
// API_BASE_URL will be provided by the system
$clientId = getenv('OAUTH_CLIENT_ID');
$clientSecret = getenv('OAUTH_CLIENT_SECRET');
$apiBaseUrl = getenv('API_BASE_URL');
class PluginApiClient {
private $clientId;
private $clientSecret;
private $apiBaseUrl;
private $accessToken = null;
private $tokenExpiry = 0;
public function __construct($clientId, $clientSecret, $apiBaseUrl) {
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->apiBaseUrl = $apiBaseUrl;
}
private function authenticate() {
$postData = [
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'scope' => 'read write'
];
$ch = curl_init($this->apiBaseUrl . '/auth/oauth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$this->accessToken = $data['access_token'];
$this->tokenExpiry = time() + $data['expires_in'];
}
public function makeRequest($endpoint, $data = null) {
// Refresh token if expired
if ($this->accessToken === null || time() >= $this->tokenExpiry) {
$this->authenticate();
}
$ch = curl_init($this->apiBaseUrl . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->accessToken,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
}
// Usage in your plugin
$client = new PluginApiClient($clientId, $clientSecret, $apiBaseUrl);
function pluginFunction($client) {
$products = $client->makeRequest('/api/query', [
'query' => 'products { id, name, price }'
]);
echo "Fetched products: " . count($products['products']) . "\n";
return $products;
}
// Execute plugin function
pluginFunction($client);
Benefits of Plugin Authorization
✅ Zero configuration: No manual setup required for developers
✅ Automatic injection: Credentials provided via environment variables
✅ Secure by default: Uses OAuth 2.0 with encrypted storage
✅ Consistent API: Same authentication flow as external applications
Comparison Table
Choose the right authorization method for your use case.
| Feature | API Key | OAuth 2.0 | Plugin Authorization |
|---|---|---|---|
| Setup Complexity | Simple | Moderate | Automatic |
| Security Level | Basic (static secrets) | High (encrypted, expires) | High (OAuth-based) |
| Token Expiration | Manual/configurable | 30 days (JWT) | 30 days (JWT) |
| User Consent | No | Yes (explicit) | No (system-level) |
| Best For | Internal tools, scripts | Third-party apps | System plugins |
| Refresh Capability | N/A (static) | Yes (refresh tokens) | Yes (automatic) |
| Configuration Required | Create API key | Create developer + app | None (automatic) |
API Endpoints
API Key Authentication
All API endpoints support API Key authentication via the X-API-Key header:
POST /api/query
X-API-Key: your_api_key_here
Content-Type: application/json
GET /odata/products
X-API-Key: your_api_key_here
POST /api/import
X-API-Key: your_api_key_here
Content-Type: multipart/form-data
OAuth 2.0 Endpoints
Authorization Endpoint
GET /auth/oauth/authorize
?client_id={client_id}
&redirect_uri={redirect_uri}
&response_type=code
&scope={scopes}
&state={state}
Token Endpoint
POST /auth/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code={authorization_code}
&redirect_uri={redirect_uri}
&client_id={client_id}
&client_secret={client_secret}
Token Refresh
POST /auth/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token={refresh_token}
&client_id={client_id}
&client_secret={client_secret}
Client Credentials Grant (for plugins)
POST /auth/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id={client_id}
&client_secret={client_secret}
&scope={scopes}
Authenticated API Requests
Once you have an access token, include it in the Authorization header:
POST /api/query
Authorization: Bearer {access_token}
Content-Type: application/json
{
"query": "products { id, name, price }"
}
Related Documentation
[navigate:/help/en/api/overview|API Overview|book-open|left]
[navigate:/help/en/api/query|Query API|code|left]
[navigate:/help/en/api/odata|OData API|database|left]
[navigate:/help/en/api/imports|Import API|upload|left]
[navigate:/help/en/api/webhooks|Webhooks|webhook|left]
[navigate:/help/en/api/rate-limits|Rate Limits|gauge|left]