Self-service is a share-link flow: you publish a URL, an end-user opens it on their own device, submits whatever data the form asks for (or first proves their identity, through DeepID or the in-platform e-ID), and lands in your decision queue. You approve, the credential ships to their wallet.
This page walks you through the smallest possible integration — enough to issue your first credential. Detailed semantics, edge cases and full webhook payloads live in the Detailed Reference; a fully-worked, presentation-friendly example is in Walkthrough — City Card.
You issue from a credential definition your organization owns. In the portal, Credential Definitions → Create credential definition offers two starting points:
- From a template — platform-provided starter shapes (e.g. City Card). The template fills in the claims, card layout and colours; everything stays editable, and the result is your own definition under your own
vct. - Blank — declare the claims, their value types, and which are mandatory yourself.
A template is not something you can issue from directly: it has no id the API accepts. Create your definition from it first.
(Official Credential Definitions — Swiss e-ID, driver licences, etc — are issued by the federation. GET /b2b/v1/credential-definitions lists them next to your own so you can verify them. You can't issue them: an offer, an invitation or a self-service config naming one is refused.)
Enable the definition in the environment (beta or production) you'll run the self-service config in; enabling an environment requires your issuer to be set up there. A request can only be issued in an environment the definition is enabled in. Beyond that, you only need your credential definition's id in the next step.
If you'll use webhook mode, first register a webhook endpoint for that environment in the portal under B2B API → Webhooks, subscribed to the self_service.request.* events: a webhook-mode config can't be saved without one.
In the portal: Self-Service → New config. Pick:
- Credential Definition — the one from step 1.
- Verification mode —
webhookif your backend decides each request;manualif your team clicks Approve in the portal;automaticto issue without review (no admin-filled claims; with identification, every mandatory claim must come from the identification mapping). - Identification (optional) — if enabled, end-users prove identity before the form, with one of the methods your config accepts (e-ID, QES, passport-based verification, …); they pick the method on the share-link page and complete it with the identification provider (DeepID, or the in-platform e-ID). The verified attributes can be mapped onto credential definition claims so the credential is pre-filled — no typing required.
- Credential delivery — let the end-user choose (QR code right away, or an emailed redemption link), QR only, or email only.
- Credential validity — how long the issued credential's
expclaim lasts (or "never expires").
Provision a service user and request the deepcredentials.self-service scope for it (see Authentication — Service Users).
Share the slug-URL with your users.
Your backend's job in webhook mode is two HTTP calls per request. The self_service.request.submitted event carries only the request id — fetch the snapshot, then post your decision.
Fetch the snapshot (claims already pre-filled from identification, if any):
- Developmenthttps://api.dev.deepcredentials.swiss/b2b/v1/self-service/requests/{requestId}
- Integrationhttps://api.int.deepcredentials.swiss/b2b/v1/self-service/requests/{requestId}
curl -i -X GET \
'https://api.dev.deepcredentials.swiss/b2b/v1/self-service/requests/{requestId}' \
-H 'Authorization: Bearer <YOUR_JWT_HERE>' \
-H 'X-Environment: beta' \
-H 'X-Org-Id: 497f6eca-6276-4993-bfeb-53cbbbba6f08'Approve. admin_claims is optional unless the config has admin-filled claims — supply any keys you want to fill or override; send just {"decision": "approve"} for a pass-through approval.
- Developmenthttps://api.dev.deepcredentials.swiss/b2b/v1/self-service/requests/{requestId}/decision
- Integrationhttps://api.int.deepcredentials.swiss/b2b/v1/self-service/requests/{requestId}/decision
curl -i -X POST \
'https://api.dev.deepcredentials.swiss/b2b/v1/self-service/requests/{requestId}/decision' \
-H 'Authorization: Bearer <YOUR_JWT_HERE>' \
-H 'Content-Type: application/json' \
-H 'X-Environment: beta' \
-H 'X-Org-Id: 497f6eca-6276-4993-bfeb-53cbbbba6f08' \
-d '{
"decision": "approve",
"admin_claims": {
"is_employee": true
}
}'manual mode skips this entirely — your team uses Approve/Reject buttons in the portal. automatic mode skips both human and backend.
After approve, the end-user receives the credential offer (QR / deeplink on the status page, or an emailed redemption link), and the wallet picks it up. You get self_service.request.approved when the decision lands and self_service.request.credential_issued once the wallet has the credential, to close the loop.
- Identification pre-fill — claims mapped to identification attributes flow into the snapshot automatically; admins can override at decide time. See Walkthrough — City Card for the full setup and Detailed Reference → Overriding user-supplied claims for the override rules.
- Manual / automatic modes — see the modes table in the Detailed Reference.
- Rejecting a request — POST
{"decision": "reject", "reason": "..."}instead (reasonis required, up to 1000 characters). The reason is shown to the end-user verbatim. - Idempotency, retries, terminal states, error codes — Detailed Reference.
- Walkthrough — City Card — a concrete end-to-end example, presentation-friendly.
- Detailed Reference — every state, every event, every error code.
- Webhooks — signature verification, event subscriptions, secret rotation.