Skip to content

Authentication

The Gateway API uses OAuth2 for authentication, supporting multiple grant types to accommodate different integration scenarios. All API requests must include a valid access token in the Authorization header.

The Gateway supports three OAuth2 grant types:

1. Password Grant (User-based Authentication)

Section titled “1. Password Grant (User-based Authentication)”

Use Case: User-specific operations, web applications with user login

Middleware: auth:api

Access To:

  • /api/1.0/* - Legacy v1.0 endpoints
  • /api/2.0/user/* - User management endpoints

Token Request:

POST /oauth/token
Content-Type: application/json
{
"grant_type": "password",
"client_id": "<YOUR_CLIENT_ID>",
"client_secret": "<YOUR_CLIENT_SECRET>",
"username": "<USER_EMAIL>",
"password": "<USER_PASSWORD>",
"scope": "*"
}

Response:

{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "def50200..."
}

2. Client Credentials Grant (Application Authentication)

Section titled “2. Client Credentials Grant (Application Authentication)”

Use Case: Server-to-server integrations, background jobs, app-level operations

Middleware: client_credentials + auth.app

Access To:

  • /api/2.0/app/* - Application-level endpoints

Token Request:

POST /oauth/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "<YOUR_CLIENT_ID>",
"client_secret": "<YOUR_CLIENT_SECRET>",
"scope": "*"
}

Response:

{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1QiLC..."
}

[!NOTE] Client Credentials tokens do NOT include a refresh token. Generate a new token when needed.


3. Account-based Authentication (Web Components)

Section titled “3. Account-based Authentication (Web Components)”

Use Case: Embedded widgets, account-scoped operations

Middleware: auth.account:webcomponent:*

Access To:

  • /api/2.0/webcomponent/* - Account-scoped endpoints

Token Generation: Account tokens are generated via intent tokens or direct account authorization flows.


Include the access token in the Authorization header:

GET /api/2.0/app/integrations
Host: <GATEWAY_URL>
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...
Accept: application/json

cURL Example:

Terminal window
curl -X GET https://<GATEWAY_URL>/api/2.0/app/integrations \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Accept: application/json"

Grant TypeExpires InRefresh Available
Password Grant1 year (31536000s)✅ Yes
Client Credentials1 year (31536000s)❌ No
Account TokenVariableDepends on implementation

Use the refresh token to obtain a new access token without re-authenticating:

POST /oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "<YOUR_REFRESH_TOKEN>",
"client_id": "<YOUR_CLIENT_ID>",
"client_secret": "<YOUR_CLIENT_SECRET>",
"scope": "*"
}

Response:

{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "<NEW_ACCESS_TOKEN>",
"refresh_token": "<NEW_REFRESH_TOKEN>"
}

[!IMPORTANT] The old refresh token becomes invalid after use. Always store the new refresh token.


Tokens can be issued with specific scopes to limit access:

  • * - Full access (default)
  • app:account-read - Read account data
  • app:account-create - Create accounts
  • app:integration-read - Read integrations
  • app:integration-action - Execute integration actions
  • app:integration-claim - Claim integrations
  • app:integration-update - Update integrations
  • app:integration-delete - Delete integrations
  • webcomponent:integration-read - Read integrations (account scope)
  • webcomponent:integration-event - Execute integration events (account scope)

Request specific scopes:

{
"grant_type": "client_credentials",
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"scope": "app:account-read app:integration-read"
}

Some endpoints require specific permissions beyond scopes:

Common Permissions:

  • read-user - View user data
  • create-user - Create users
  • update-user - Modify users
  • delete-user - Delete users
  • read-integration - View integrations
  • create-integration - Create integrations
  • update-integration - Modify integrations
  • delete-integration - Delete integrations
  • read-schedule - View schedules
  • create-schedule - Create schedules
  • update-schedule - Modify schedules
  • delete-schedule - Delete schedules

Middleware Example:

['middleware' => ['auth:api', 'permission:read-integration']]

Cause: Invalid, missing, or expired token

Response:

{
"message": "Unauthenticated."
}

Solutions:

  • Verify token is included in Authorization header
  • Check token hasn’t expired
  • Refresh token (password grant) or generate new token (client credentials)

Cause: Authenticated but lacking required permissions or scopes

Response:

{
"message": "This action is unauthorized."
}

Solutions:

  • Verify token has required scopes
  • Check user has necessary permissions
  • Ensure resource ownership validation passes

Cause: Invalid credentials or malformed request

Response:

{
"error": "invalid_client",
"error_description": "Client authentication failed",
"message": "Client authentication failed"
}

Solutions:

  • Verify client_id and client_secret are correct
  • Check request payload format
  • Ensure Content-Type: application/json header is set

[!CAUTION] Never commit tokens to version control or expose them in client-side code!

Environment Variables:

.env
GATEWAY_ACCESS_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...
GATEWAY_CLIENT_ID=your-client-id
GATEWAY_CLIENT_SECRET=your-client-secret

Encrypt at Rest:

// Laravel example
$encrypted = encrypt($accessToken);
$decrypted = decrypt($encrypted);

All API requests must use HTTPS to protect tokens in transit.

Never: http://<GATEWAY_URL>/api/...

Always: https://<GATEWAY_URL>/api/...

  • Refresh tokens before expiration
  • Rotate client secrets periodically
  • Revoke compromised tokens immediately

Request only the scopes your application needs:

Avoid: "scope": "*"

Better: "scope": "app:integration-read app:account-read"

Automatically refresh tokens before expiration:

class GatewayClient {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.token = null;
this.tokenExpiry = null;
}
async getToken() {
if (this.token && this.tokenExpiry > Date.now() + 60000) {
return this.token;
}
const response = await axios.post('/oauth/token', {
grant_type: 'client_credentials',
client_id: this.clientId,
client_secret: this.clientSecret,
scope: '*'
});
this.token = response.data.access_token;
this.tokenExpiry = Date.now() + (response.data.expires_in * 1000);
return this.token;
}
}

use Illuminate\Support\Facades\Http;
// Get access token
$response = Http::post('https://<GATEWAY_URL>/oauth/token', [
'grant_type' => 'client_credentials',
'client_id' => config('gateway.client_id'),
'client_secret' => config('gateway.client_secret'),
'scope' => '*'
]);
$token = $response->json()['access_token'];
// Make authenticated request
$integrations = Http::withToken($token)
->get('https://<GATEWAY_URL>/api/2.0/app/integrations')
->json();
const axios = require('axios');
async function authenticate() {
const response = await axios.post('https://<GATEWAY_URL>/oauth/token', {
grant_type: 'client_credentials',
client_id: process.env.GATEWAY_CLIENT_ID,
client_secret: process.env.GATEWAY_CLIENT_SECRET,
scope: '*'
});
return response.data.access_token;
}
async function getIntegrations() {
const token = await authenticate();
const response = await axios.get(
'https://<GATEWAY_URL>/api/2.0/app/integrations',
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.data;
}
import requests
import os
def get_access_token():
response = requests.post(
'https://<GATEWAY_URL>/oauth/token',
json={
'grant_type': 'client_credentials',
'client_id': os.getenv('GATEWAY_CLIENT_ID'),
'client_secret': os.getenv('GATEWAY_CLIENT_SECRET'),
'scope': '*'
}
)
return response.json()['access_token']
def get_integrations():
token = get_access_token()
response = requests.get(
'https://<GATEWAY_URL>/api/2.0/app/integrations',
headers={'Authorization': f'Bearer {token}'}
)
return response.json()

Test your authentication setup using the interactive Swagger documentation:

Open API Docs →

  1. Click the “Authorize” button
  2. Enter your client_id and client_secret
  3. Select desired scopes
  4. Click “Authorize”
  5. Test endpoints with authenticated requests