Skip to content
Last updated

Self-Service Quickstart

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.

The four steps

1. Pick a credential definition

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.

2. Create a self-service config

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 — webhook if your backend decides each request; manual if your team clicks Approve in the portal; automatic to 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 exp claim 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.

3. Decide each submission (webhook mode)

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):

Open in API reference →

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.

Open in API reference →

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.

4. The credential lands in the wallet

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.

Common variations

  • 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 (reason is required, up to 1000 characters). The reason is shown to the end-user verbatim.
  • Idempotency, retries, terminal states, error codes — Detailed Reference.