.NET SDK
XENBIT.ResQueServe.SDK.Client is a typed REST client for the Dashboard and the Realm API. It authenticates as an API client, renews its short-lived access tokens itself, and maps API errors to exceptions. It targets .NET 10 and integrates with Microsoft.Extensions.DependencyInjection.
dotnet add package XENBIT.ResQueServe.SDK.Client
The models and interfaces come from XENBIT.ResQueServe.Core and XENBIT.ResQueServe.Abstractions, see the API reference.
Authentication
Create an API client in your account first, see Authentication. The SDK chooses the method from what you configure:
| Configured | Behavior |
|---|---|
ClientId + ClientSecret |
Recommended. The SDK requests a token with the client_credentials grant, caches it, and renews it shortly before it expires (TokenRenewalMargin, default 60 seconds). If a request answers 401, it renews the token and repeats the request once. |
JwtToken |
A long-living token, used as it is and never renewed. Legacy: it stays valid until it expires or is revoked. You can replace it at runtime with UpdateToken(...) on a client. |
All clients share one token provider and one connection pool, so a token is requested only once. If the token endpoint fails (for example with 429), the SDK does not ask again until the Retry-After time has passed (10 seconds if the server names none) and throws the same OAuthException in the meantime.
Configuration
| Option | Description |
|---|---|
DashboardBaseUrl |
Base URL of the Dashboard. Required for the Dashboard clients |
RealmBaseUrl |
Base URL of the Realm. Optional for the SIM Dispatcher environments: it is derived from DashboardBaseUrl |
AuthorizationServerUrl |
Base URL of the authorization server (/oauth/token). Optional, the Dashboard of the environment |
ClientId, ClientSecret |
The API client. Keep the secret out of source control |
JwtToken |
Long-living token, used when no ClientId is set |
TokenRenewalMargin |
How long before expiry a token is renewed. Default 60 seconds |
TimeoutSeconds |
Request timeout. Default 30 |
RequestCulture |
Sent as Accept-Language. Default is the current UI culture |
All URLs must be absolute https URLs. http is accepted only for loopback hosts (localhost, 127.0.0.1), so the client secret and the tokens never travel in clear text.
Note
For the SIM Dispatcher, BaseUrlDefaults contains the hosts: SIMDispatcherDashboard (www.sim-dispatcher.com), SIMDispatcherRealm (platform.sim-dispatcher.com), SIMDispatcherStageDashboard (stage.sim-dispatcher.com) and SIMDispatcherStageRealm (stage-platform.sim-dispatcher.com).
Registration
From configuration (recommended):
{
"ResQueServe": {
"DashboardBaseUrl": "https://www.sim-dispatcher.com",
"ClientId": "YOUR_CLIENT_ID",
"ClientSecret": "YOUR_CLIENT_SECRET",
"TimeoutSeconds": 60
}
}
using XENBIT.ResQueServe.SDK.Extensions;
builder.Services.AddResQueServeClients(builder.Configuration, sectionName: "ResQueServe");
In code:
using XENBIT.ResQueServe.SDK.Defaults;
using XENBIT.ResQueServe.SDK.Extensions;
builder.Services.AddResQueServeClients(options =>
{
options.DashboardBaseUrl = BaseUrlDefaults.SIMDispatcherDashboard;
options.ClientId = Environment.GetEnvironmentVariable("RESQUESERVE_CLIENT_ID")!;
options.ClientSecret = Environment.GetEnvironmentVariable("RESQUESERVE_CLIENT_SECRET")!;
});
AddResQueServeClients registers the Dashboard and the Realm clients. AddResQueServeDashboardClient and AddResQueServeRealmClient register only one of the two.
Clients
The clients are registered as scoped services; inject them where you need them.
| Client | Purpose |
|---|---|
DashboardSystemClient |
Health and version information |
DashboardPoiClient |
Points of interest |
DashboardUnitClient |
Units (vehicles and responders) |
DashboardKeywordClient |
Keywords (dispatch logic) |
DashboardRadioGroupClient |
Radio groups |
DashboardLookupClient |
Address and geodata lookup |
RealmSystemClient |
System endpoints of the Realm |
public class FleetManager(DashboardPoiClient poiClient, DashboardUnitClient unitClient)
{
public async Task LoadStationsAsync(long dispatchCenterId)
{
var stations = await poiClient.GetAllAsync(dispatchCenterId, search: "FindBy('isStation', 'true')");
// ...
}
}
Search an address
The dispatch center needs an address package for the lookup.
public async Task<long?> FindAddressIdAsync(DashboardLookupClient client, long dispatchCenterId, string query)
{
var results = await client.SearchAddressesAsync(dispatchCenterId, query, isInArea: true);
return results?.FirstOrDefault()?.Id;
}
Create a unit
using XENBIT.ResQueServe.Abstractions.DataStructures;
using XENBIT.ResQueServe.Core.Models;
public async Task CreateAmbulanceAsync(DashboardUnitClient client, long dispatchCenterId, long stationPoiId)
{
var unit = new Unit
{
Callsign = "Rescue 1-83-1",
CallsignShort = "1-83-1",
Category = UnitCategory.Ambulance,
Poi = new Poi { Id = stationPoiId },
DutyOptions = UnitDutyOptions.Always
};
var created = await client.CreateAsync(dispatchCenterId, unit);
}
Link to an external source
ShelfOrigin links an entity to an external data source:
unit.Origin = new ShelfOrigin<string>(
Namespace: "Internal_HR_System",
Id: "HR_ID_9921",
Version: new ShelfVersion<string>("v1.0"));
Error handling
API errors are mapped to exceptions derived from ResQueServeException:
try
{
await unitClient.CreateAsync(dispatchCenterId, unit);
}
catch (ValidationException ex) when (ex.Problems.Contains(ProblemInformation.CallsignAlreadyExists))
{
// a specific problem, identified by the ProblemInformation enum
}
catch (DataNotFoundException ex)
{
Console.WriteLine($"Not found: {ex.LookupProperty} = {ex.ProvidedValue}");
}
catch (OAuthException ex) when (ex.RequiresNewCredentials)
{
// invalid_client / unauthorized_client: the API client needs new credentials, a retry will not help
}
catch (ResQueServeException ex)
{
// any other platform error; log the trace identifier for support
logger.LogError(ex, "API error");
}
OAuthException is thrown when the token endpoint rejects a request. Error holds the RFC 6749 error code (for example invalid_client or too_many_requests), StatusCode the HTTP status. Errors without a mapped type are an UnknownException with StatusCode, ResponseContent and ProblemDetails.