Skip to content
Last updated

Service Users

DeepCredentials authenticates B2B callers as DeepAdmin service users. There is no portal-side credential to mint — identity is delegated to the DeepCloud Identity Provider (Keycloak), and every B2B request carries a short-lived JWT obtained from there.

How identity is structured

  • Your partner client is a Keycloak client created for you by the DeepCloud team. It identifies your integration globally.
  • Service users are accounts created under your partner client, typically one per end-client organization you serve. Each one has its own username + password that your code uses to authenticate.
  • Organizations are the DeepAdmin entity that resources are scoped to. A service user can be a member of one or many organizations.

On every call, the portal verifies that the service user behind the bearer token is a member of the organization you supply in the X-Org-Id header.

A note on Keycloak realms

A realm in Keycloak is an isolated tenant inside a Keycloak deployment — its own users, clients, scopes, and signing keys. DeepCloud's realm is named sso, and each environment (int for testing, prod for live) runs its own Keycloak deployment with its own sso realm. A service user provisioned against int.deepcloud.swiss is not valid against deepcloud.swiss and vice versa.

Provisioning

Provisioning happens in DeepCloud, not in DeepCredentials. The canonical, always-current walkthrough is DeepCloud — API Access Setup and Authorization Overview. Read it for the URLs, the form parameters, the authorization-method options, and the credential-delivery modes — we deliberately don't duplicate them here so they can't go stale.

The high-level shape (use the left-nav on the DeepCloud page to jump to each step):

  1. API Access Setup — the DeepCloud team gives you a Partner-Service-Client-ID and Partner-Service-Client-Secret (or a JWKS configuration). When you contact them, also ask them to allow the deepcredentials.* scopes you'll need (see the Scopes table below).
  2. Service User Creation — for every end-client organization you serve, that organization's owner authorizes your partner client and you receive a service_account_username + service_account_password bound to that org.
  3. Store the credentials in your own secret manager — one client-level secret, plus one service-user credential pair per end client.

Exchanging credentials for an access token

Use the OAuth 2.0 Resource Owner Password Credentials grant against DeepCloud's Keycloak token endpoint, requesting the scopes your endpoints need:

curl -X POST "https://deepcloud.swiss/auth/realms/sso/protocol/openid-connect/token" \
  --data-urlencode "grant_type=password" \
  --data-urlencode "username=<service_account_username>" \
  --data-urlencode "password=<service_account_password>" \
  --data-urlencode "client_id=<Partner-Service-Client-ID>" \
  --data-urlencode "client_secret=<Partner-Service-Client-Secret>" \
  --data-urlencode "scope=deepcredentials.issue deepcredentials.verify deepcredentials.self-service"

The scope parameter is a space-separated list of the OAuth scopes you want on the returned token; ask for the ones your integration uses. If DeepCloud's partner-client config doesn't allow a scope, Keycloak silently drops it from the response — always inspect the scope claim on the issued token to confirm.

For the test environment, replace deepcloud.swiss with int.deepcloud.swiss.

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs…",
  "expires_in": 900,
  "refresh_expires_in": 36000,
  "refresh_token": "eyJhbGciOiJIUzI1NiIs…",
  "token_type": "Bearer",
  "scope": "deepcredentials.issue deepcredentials.verify deepcredentials.self-service"
}
Cache tokens, don't re-fetch every call

Access tokens are valid for expires_in seconds (900 = 15 min). Cache them and re-use until close to expiry. Re-fetching for every API call drives unnecessary load on DeepCloud's IdP and adds avoidable latency. A common pattern is to refresh when less than 30 seconds of validity remain. The refresh_token lets you extend without re-sending username/password.

The JWKS-based client-assertion variant (signs a JWT with your private key instead of presenting a client_secret) is covered under Token Endpoint in DeepCloud — API Access Setup and Authorization Overview — switch the Authorization Method selector on their page to "JWKS" to surface the relevant request shape and code samples.

You do not need to configure an audience on your side — Keycloak attaches a default aud claim that the portal trusts.

Calling the API

Send three headers on every /b2b/v1/* request:

HeaderValue
AuthorizationBearer <access_token> from the token response above.
X-Org-IdUUID of the organization this call is acting on. The service user must be a member.
X-Environmentbeta or production. Resources are isolated per environment.

A worked example:

Open in API reference →

curl -i -X GET \
  https://api.dev.deepcredentials.swiss/b2b/v1/health \
  -H 'Authorization: Bearer <YOUR_JWT_HERE>' \
  -H 'X-Environment: beta' \
  -H 'X-Org-Id: 497f6eca-6276-4993-bfeb-53cbbbba6f08'

Scopes

ScopeGrants access to
deepcredentials.issueCreate credential offers, list and inspect issued credentials, suspend / revoke / reactivate.
deepcredentials.verifyCreate and read OID4VP verification sessions.
deepcredentials.self-serviceRead and decide on end-user-initiated self-service requests.
EndpointRequired scope
POST /b2b/v1/credential-offersdeepcredentials.issue
GET /b2b/v1/credential-offers[/...]deepcredentials.issue
GET /b2b/v1/issued-credentials[/...]deepcredentials.issue
PATCH /b2b/v1/issued-credentials/:id/statusdeepcredentials.issue
POST /b2b/v1/verification-sessionsdeepcredentials.verify
GET /b2b/v1/verification-sessions[/...]deepcredentials.verify
GET /b2b/v1/self-service/requests/:requestIddeepcredentials.self-service
POST /b2b/v1/self-service/requests/:requestId/decisiondeepcredentials.self-service
GET /b2b/v1/health— (authenticated, no scope required)
Least privilege

Only request the scopes your integration uses. If you only verify identities, scope=deepcredentials.verify is sufficient.

Errors you may see

StatusCodeCause
400invalid_argumentMissing or malformed X-Org-Id / X-Environment header, or a missing required field in the request body.
401unauthenticatedBearer token failed validation: bad signature, expired (exp in the past), wrong issuer, or wrong audience.
403permission_deniedThe token doesn't carry the scope the endpoint requires. Inspect the scope claim on the issued token — if a scope you requested is missing, the partner client isn't configured to grant it (contact DeepCloud).
404not_foundEither the resource ID is unknown, or the X-Org-Id you supplied isn't an organization the authenticated service user is a member of. The portal returns the same code for both to avoid leaking organization existence.