Skip to main content

How to Access the SureInk API

The SureInk Platform provides a comprehensive REST API for programmatic access to all platform services. All API requests are routed through the Zuul API Gateway and require proper authentication.

API Request Flow

The diagram above shows the complete request flow:

  1. Client sends an authenticated request to the API Gateway
  2. API Gateway (Zuul) receives and routes the request
  3. Keycloak validates the OAuth2 access token
  4. Microservice processes the request and returns data
  5. Response flows back through the gateway to the client

API Architecture

The SureInk API follows a microservices-based REST architecture where:

  • All requests flow through the Zuul API Gateway (http://localhost:8762 in development)
  • Authentication is handled via Keycloak OAuth2/OIDC tokens
  • Service Discovery is managed by Eureka, allowing dynamic routing
  • Base URL format: {gateway-url}/sureink/{module}/api/{endpoint}

Authentication

Understanding Access Tokens

Are tokens static or dynamic?

Access tokens in the SureInk Platform are dynamic and temporary:

  • Validity: Tokens expire after 5 minutes (300 seconds) by default
  • Refresh: A refresh token (valid for 30 minutes) is provided to obtain new access tokens
  • Security: Tokens are JWT (JSON Web Tokens) signed by Keycloak
  • Scope: Each token is tied to a specific user and their permissions

Where do you get tokens?

Tokens are obtained from the Keycloak authentication server using OAuth2 flows. There are two primary methods:

  1. Password Grant (for server-to-server or development)
  2. Authorization Code Flow (for web applications with user login)

Method 1: Password Grant Flow (Direct Token Request)

This method is suitable for server-to-server integrations, scripts, or development purposes where you have the user's credentials.

Request:

curl -X POST "http://localhost:8080/auth/realms/SureInk/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=cloud-msa-app-ui" \
-d "client_secret=<your-client-secret>" \
-d "username=<your-username>" \
-d "password=<your-password>"

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"scope": "profile email"
}

TypeScript Example:

interface TokenResponse {
access_token: string;
expires_in: number;
refresh_token: string;
token_type: string;
}

async function getToken(
username: string,
password: string
): Promise<TokenResponse> {
const response = await fetch(
'http://localhost:8080/auth/realms/SureInk/protocol/openid-connect/token',
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'password',
client_id: 'cloud-msa-app-ui',
client_secret: '<your-client-secret>',
username: username,
password: password
})
}
);

if (!response.ok) {
throw new Error(`Token request failed: ${response.status}`);
}

return await response.json();
}

// Usage
const tokenData = await getToken('user@example.com', 'password123');
console.log('Access Token:', tokenData.access_token);
console.log('Expires in:', tokenData.expires_in, 'seconds');

Method 2: Refresh Token (Renew Expired Tokens)

When your access token expires, use the refresh token to obtain a new one without re-authenticating:

async function refreshToken(refreshToken: string): Promise<TokenResponse> {
const response = await fetch(
'http://localhost:8080/auth/realms/SureInk/protocol/openid-connect/token',
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'cloud-msa-app-ui',
client_secret: '<your-client-secret>',
refresh_token: refreshToken
})
}
);

return await response.json();
}

Method 3: Authorization Code Flow (For Web Applications)

For web applications where users log in through a browser, use the Authorization Code flow:

  1. Redirect to Keycloak login:

    https://msa.sureclinical.com/auth/realms/SureInk/protocol/openid-connect/auth?
    client_id=cloud-msa-app-ui&
    redirect_uri=https://yourapp.com/callback&
    response_type=code&
    scope=openid
  2. Exchange authorization code for tokens: After successful login, Keycloak redirects to your callback URL with a code parameter. Exchange this for tokens:

    async function exchangeCodeForToken(code: string): Promise<TokenResponse> {
    const response = await fetch(
    'http://localhost:8080/auth/realms/SureInk/protocol/openid-connect/token',
    {
    method: 'POST',
    headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: 'cloud-msa-app-ui',
    client_secret: '<your-client-secret>',
    code: code,
    redirect_uri: 'https://yourapp.com/callback'
    })
    }
    );

    return await response.json();
    }

Future Considerations: Cloudflare Access

The SureInk Platform may integrate Cloudflare Access in the future to provide:

  • Zero Trust network access
  • Additional layer of authentication before reaching the API Gateway
  • Enhanced DDoS protection and rate limiting
  • Simplified token management through Cloudflare's identity providers

This would add an additional authentication layer before requests reach Zuul.

1. Obtain an Access Token

All API requests require a valid OAuth2 access token from Keycloak. You can obtain a token using the password grant flow:

Request:

curl -X POST "http://localhost:8080/auth/realms/SureInk/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=cloud-msa-app-ui" \
-d "client_secret=<your-client-secret>" \
-d "username=<your-username>" \
-d "password=<your-password>"

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer"
}

2. Include the Token in API Requests

Add the access token to the Authorization header of all API requests:

Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Complete Example:

// Store token in memory or secure storage
let accessToken = '';
let refreshToken = '';

async function initializeAuth(username: string, password: string) {
const tokens = await getToken(username, password);
accessToken = tokens.access_token;
refreshToken = tokens.refresh_token;

// Set up automatic token refresh before expiry
setTimeout(() => {
refreshAccessToken();
}, (tokens.expires_in - 30) * 1000); // Refresh 30 seconds before expiry
}

async function refreshAccessToken() {
const tokens = await refreshToken(refreshToken);
accessToken = tokens.access_token;
refreshToken = tokens.refresh_token;

// Schedule next refresh
setTimeout(() => {
refreshAccessToken();
}, (tokens.expires_in - 30) * 1000);
}

Making API Requests

Base URL Structure

{gateway-url}/sureink/{module}/api/{endpoint}

Components:

  • Gateway URL: http://localhost:8762 (local) or https://msa.sureclinical.com (production)
  • Module: The microservice name (e.g., cloudmsa, rules-engine, usage-data)
  • Endpoint: The specific API path

Example: Get Customer Information

TypeScript Example:

const apiConfig = {
baseUrl: 'http://localhost:8762/sureink',
module: 'cloudmsa',
token: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...'
};

async function getCustomer(customerId: string) {
const response = await fetch(
`${apiConfig.baseUrl}/${apiConfig.module}/api/customers/${customerId}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${apiConfig.token}`,
'Content-Type': 'application/json'
}
}
);

if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}

return await response.json();
}

// Usage
const customer = await getCustomer('123');
console.log(customer);

Example: Create a New License

async function createLicense(licenseData: any) {
const response = await fetch(
`${apiConfig.baseUrl}/${apiConfig.module}/api/licenses`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiConfig.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(licenseData)
}
);

return await response.json();
}

// Usage
const newLicense = await createLicense({
customerId: '123',
productId: 'prod-456',
quantity: 10,
expirationDate: '2026-12-31'
});

Environment Configuration

Development

const config = {
apiUrl: 'http://localhost:8762/sureink',
keycloak: {
issuer: 'http://localhost:8080/auth/',
realm: 'SureInk',
clientId: 'cloud-msa-app-ui'
}
};

Production

const config = {
apiUrl: 'https://msa.sureclinical.com/sureink',
keycloak: {
issuer: 'https://msa.sureclinical.com/auth',
realm: 'SureInk',
clientId: 'cloud-msa-app-ui'
}
};

Available Modules

The following modules are accessible through the API Gateway:

ModulePathDescription
Cloud MSA/cloudmsa/apiCore business services (customers, licenses, orders)
Rules Engine/rules-engine/apiBusiness rules and validation logic
Usage Data/usage-data/apiUsage tracking and metrics
User Management/user-management/apiUser and identity management
Connector/connector/apiExternal system integrations

CORS Configuration

The API Gateway is configured with permissive CORS settings for development. In production environments, CORS should be restricted to specific origins.

Allowed Methods:

  • GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD

Allowed Headers:

  • All headers (*)

Credentials:

  • Supported (Access-Control-Allow-Credentials: true)

Error Handling

The API returns standard HTTP status codes:

Status CodeMeaning
200Success
201Created
400Bad Request
401Unauthorized (invalid or missing token)
403Forbidden (insufficient permissions)
404Not Found
500Internal Server Error

Error Response Format:

{
"timestamp": "2025-12-21T10:30:00.000+00:00",
"status": 400,
"error": "Bad Request",
"message": "Invalid customer ID format",
"path": "/sureink/cloudmsa/api/customers/invalid"
}

Rate Limiting

Currently, there are no enforced rate limits on the API. However, clients should implement appropriate retry logic with exponential backoff for failed requests.

API Methods

For detailed information about available API methods, see:

See the Methods Overview for a complete list of all available methods.