Skip to content

Authentication

The API uses standard OAuth 2.0 client credentials (RFC 6749, section 4.4). Your server exchanges a client ID and secret for a short-lived access token, then sends that token with each request.

What Sandbox prefix Production prefix Where it goes
Client ID sim_dev_c_ sim_prod_c_ Token endpoint (HTTP Basic user name)
Client secret sim_dev_s_ sim_prod_s_ Token endpoint (HTTP Basic password)
Access token sim_dev_at_ sim_prod_at_ Authorization: Bearer on API requests

The prefixes tell you which environment a credential belongs to and let secret scanners recognize a leaked secret. Sandbox and production credentials never work in the other environment.

POST https://api.simblia.com/v1/oauth/token (sandbox: https://api-dev.simblia.com/v1/oauth/token)

  • Authenticate with HTTP Basic: client ID as user name, client secret as password. This is the only supported client authentication (client_secret_basic); credentials in the form body are not accepted.
  • Send a form body (application/x-www-form-urlencoded) with grant_type=client_credentials.
  • Optionally send scope with space-separated scopes. It must be a subset of your client’s scopes; without it you get all of them.
{
"access_token": "sim_dev_at_…",
"token_type": "Bearer",
"expires_in": 900,
"scope": "organization.read"
}

The response has Cache-Control: no-store. Tokens last at most 15 minutes. There are no refresh tokens: when a token expires (or shortly before), ask for a new one with the same credentials.

Terminal window
curl -s https://api-dev.simblia.com/v1/oauth/token \
-u "$SIMBLIA_CLIENT_ID:$SIMBLIA_CLIENT_SECRET" \
-d grant_type=client_credentials \
-d scope=organization.read

Python (requests), reusing the token until shortly before it expires:

import os
import time
import requests
TOKEN_URL = "https://api-dev.simblia.com/v1/oauth/token"
_token = {"value": None, "expires_at": 0.0}
def access_token() -> str:
if _token["value"] and time.monotonic() < _token["expires_at"] - 60:
return _token["value"]
r = requests.post(
TOKEN_URL,
auth=(os.environ["SIMBLIA_CLIENT_ID"], os.environ["SIMBLIA_CLIENT_SECRET"]),
data={"grant_type": "client_credentials"},
timeout=10,
)
r.raise_for_status()
body = r.json()
_token["value"] = body["access_token"]
_token["expires_at"] = time.monotonic() + body["expires_in"]
return _token["value"]

C# (HttpClient):

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
static async Task<string> GetAccessTokenAsync(HttpClient http, string clientId, string clientSecret)
{
var basic = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api-dev.simblia.com/v1/oauth/token")
{
Content = new FormUrlEncodedContent(new Dictionary<string, string> { ["grant_type"] = "client_credentials" }),
};
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", basic);
using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var token = await response.Content.ReadFromJsonAsync<TokenResponse>();
return token!.access_token;
}
record TokenResponse(string access_token, string token_type, int expires_in, string scope);

Copy the client ID and secret exactly as issued, without adding or removing characters.

Send it in the Authorization header:

GET /v1/organization HTTP/1.1
Host: api-dev.simblia.com
Authorization: Bearer sim_dev_at_…

Access tokens are opaque. Do not parse them, decode them or rely on their length or format beyond the prefix; the format can change at any time. To know when a token expires, use expires_in from the token response.

Scope Gives access to
organization.read Your organization’s name, type, institution type and country

A request whose token lacks the scope an operation needs gets 403 forbidden (insufficient_scope).

The token endpoint answers errors in the standard OAuth 2.0 format (RFC 6749, section 5.2), not in the Simblia error envelope:

{ "error": "invalid_client", "error_description": "Client authentication failed." }
HTTP error Typical cause
400 invalid_request, unsupported_grant_type, invalid_scope Missing or wrong form fields, or a scope your client does not have
401 invalid_client Wrong client ID or secret, a credential from the other environment, or a suspended or revoked client
429 rate_limited (Simblia error envelope) Too many requests; see rate limits

An organization administrator can rotate a client’s secret in the Simblia app. The new secret is shown once. The previous secret keeps working for 24 hours, so you can deploy the new one without downtime. After 24 hours only the new secret works.

  • Revoking a client ends its secrets and its access tokens at once.
  • If Simblia suspends or revokes a client, or the organization is no longer approved, the next request is denied with 401 unauthenticated, even if the token has not expired yet.

OAuth libraries can discover the token endpoint and the supported options from the RFC 8414 metadata document at the root of the API host (outside /v1):

Terminal window
curl -s https://api-dev.simblia.com/.well-known/oauth-authorization-server
{
"issuer": "https://api-dev.simblia.com",
"token_endpoint": "https://api-dev.simblia.com/v1/oauth/token",
"grant_types_supported": ["client_credentials"],
"token_endpoint_auth_methods_supported": ["client_secret_basic"],
"scopes_supported": ["organization.read"]
}

The document can contain more fields; ignore the ones you do not use.