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.
Credentials
Section titled “Credentials”| 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.
Get a token
Section titled “Get a token”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) withgrant_type=client_credentials. - Optionally send
scopewith 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.
Examples
Section titled “Examples”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.readPython (requests), reusing the token until shortly before it expires:
import osimport timeimport 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.
Use a token
Section titled “Use a token”Send it in the Authorization header:
GET /v1/organization HTTP/1.1Host: api-dev.simblia.comAuthorization: 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.
Scopes
Section titled “Scopes”| 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).
Token endpoint errors
Section titled “Token endpoint errors”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 |
Rotate a secret
Section titled “Rotate a secret”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.
When access stops
Section titled “When access stops”- 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.
Authorization server metadata
Section titled “Authorization server metadata”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):
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.