This application provides a REST API with an OpenAPI (Swagger) specification and an interactive Swagger UI. Use Swagger UI for exploration and testing, and the OpenAPI JSON for client generation or API gateways.
Open /api-docs to view the interactive documentation. Click "Authorize", choose the required scopes (api.read, api.write), and sign in.
Use this for interactive/browser/device flows where a user signs in and grants delegated scopes. Replace {TENANT_ID} with your Entra tenant ID and {API_APP_ID_URI} with the CCS Manager API Application ID URI (e.g., api://<API_APP_ID>).
Authorization Code + PKCE (browser/app):
https://your-app/callback).{API_APP_ID_URI}/api.read, api.write) and grant consent./authorize with response_type=code, code_challenge, code_challenge_method=S256, scope={API_APP_ID_URI}/api.read offline_access openid profile.grant_type=authorization_code, code_verifier, client_id, redirect_uri.Device Code (CLI-friendly):
# 1) Request device code
POST https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/devicecode
client_id={CLIENT_ID}&scope={API_APP_ID_URI}/api.read offline_access
# 2) Confirm code in browser, then poll token
POST https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token
grant_type=device_code&client_id={CLIENT_ID}&device_code={DEVICE_CODE}
API call example (curl):
curl -H "Authorization: Bearer <access_token>" https://<host>/api/v1/serviceareas
PowerShell delegated sample: ./sample-delegated-serviceareas.ps1 -ApiBaseUrl "https://api.manager.converged.services" -ResourceAppIdUri "{API_APP_ID_URI}" (uses az account get-access-token for the signed-in user).
Notes:
api.read/api.write) for user flows; do not use /.default here.scope={API_APP_ID_URI}/.default (see machine-to-machine below).For backend services, daemons, or automations without user interaction, the CCS Manager API is accessed via an Entra ID app registration using the OAuth 2.0 Client Credentials flow (grant_type=client_credentials). Calls run as application permissions: permissions are assigned to the calling application (caller app) and are clearly separated from interactive user permissions.
The calling application operates only in its own technical identity (“app identity”) and not in the context of a signed-in user—no interactive sign-in, no delegated user rights.
grant_type=client_credentials and scope={API_APP_ID_URI}/.default.Authorization: Bearer <ACCESS_TOKEN>. The API validates issuer, audience, and app roles (e.g., CcsManager.Api.Read).PowerShell sample (secret or cert): ./sample-client-credentials.ps1 -TenantId "<TENANT_ID>" -ClientId "<CLIENT_ID>" -ClientSecret "<SECRET>" or ./sample-client-credentials.ps1 -TenantId "<TENANT_ID>" -ClientId "<CLIENT_ID>" -CertificatePath .\\caller.pfx -CertificatePassword "<PFX_PASSWORD>".
1) API app “CCS Manager” (resource app)
{API_APP_ID}{API_APP_ID_URI}CcsManager.Api.Read, CcsManager.Api.WriteAssignments, CcsManager.Api.AuditRead, …2) Caller app (your integration)
CcsManager.Api.Read, ...WriteAssignments, ...AuditRead) -> Add permissions.grant_type=client_credentials and scope={API_APP_ID_URI}/.default, authenticating with the caller app’s client_id plus secret or cert (JWT client assertion). The roles claim will contain the granted app roles.Authorization: Bearer <ACCESS_TOKEN> on every request; the API checks audience, issuer, and roles.PowerShell sample (secret or cert): ./sample-client-credentials.ps1 -TenantId "{TENANT_ID}" -ClientId " or ./sample-client-credentials.ps1 -TenantId "{TENANT_ID}" -ClientId ".
POST https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={CLIENT_ID}
client_secret={CLIENT_SECRET}
grant_type=client_credentials
scope={API_APP_ID_URI}/.default
CLIENT_ID = caller app IDTENANT_ID = tenant ID (e.g., 21fe9e59-ae52-479c-ae85-68f3aa06763d)scope must end with /.default to include granted app roles.POST https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={CLIENT_ID}
grant_type=client_credentials
scope={API_APP_ID_URI}/.default
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion={JWT_SIGNED_WITH_UPLOADED_CERT_PRIVATE_KEY}
client_assertion is a JWT signed with the private key of the certificate uploaded to the caller app:
aud = https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/tokeniss and sub = CLIENT_IDExample (decoded) JWT assertion header/payload:
{
"alg": "RS256",
"typ": "JWT",
"x5t": "N2Y5MWMzYjQ2YzJhNGMwZmI5MWMwYWY2NTEyZGU0YjI"
}
{
"aud": "https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token",
"iss": "00000000-0000-0000-0000-000000000000",
"sub": "00000000-0000-0000-0000-000000000000",
"jti": "2d3c7d71-2c5f-4a16-9c84-bf6f3ce6d9dd",
"nbf": 1733222400,
"exp": 1733222700
}
The actual JWT is the base64url-encoded, signed header.payload.signature string sent as client_assertion.
GET https://api.manager.converged.services/api/v1/serviceareas
Authorization: Bearer {ACCESS_TOKEN}
Accept: application/json
The API checks audience ({API_APP_ID_URI}), token validity, issuer (tenant), and roles (e.g., CcsManager.Api.Read). Failures typically return 401 (no/invalid token) or 403 (insufficient roles).
AADSTS501051 – Application is not assigned to a role for the application
CcsManager.Api.* role assigned or no admin consent.AADSTS65001 – consent_required / invalid_grant
AADSTS650057 – Invalid resource
scope or CCS Manager not added as a resource permission.scope={API_APP_ID_URI}/.default and the CCS Manager resource with required app roles is present under API permissions.
API requests require a valid Entra ID access token with the audience set to this API. For delegated calls use scopes (api.read/api.write); for client-credentials calls use app roles via scope=.../.default and the resulting roles claim.
Health (liveness): GET /healthz - process alive (no dependency checks).
Readiness: GET /ready - API ready; checks dependencies (e.g., database) and returns 200 when ready, 503 otherwise.
# Liveness
curl -sS http://localhost:8080/healthz | jq .
# Readiness
curl -i http://localhost:8080/ready
/healthz for liveness (restart decision)./ready for readiness (traffic gating/alerts).