Tutorial: build an issuer integration

Take the first lifecycle tutorial and make it something you would deploy.

Assumes: you have completed that tutorial.

Separate configuration from runtime

Configuration — organisations, issuers, credential configurations — belongs in a setup script or deploy step, run rarely and reviewed. Issuance belongs on your request path.

Mixing them means every request carries code that only matters once, and configuration changes ride in on whatever deploy happens to be next.

deploy pipeline   -> Management API -> issuers, credential configurations
application       -> Product API    -> issuance

Script your configuration. Then creating the Production equivalent is the same script with a did_live. key.

Make every issuance idempotent

Derive the key from your own business identifier, once, before the first attempt:

Idempotency-Key: enrolment-2026-a41f9

Not a fresh UUID per attempt. See use idempotency.

Model the wait honestly

An issuance reaches awaiting_wallet and stays there until the holder accepts. Your data model needs a state for that, and your UI needs to say something true about it.

issued_offer      -> awaiting_wallet
holder_accepted   -> the credential exists
expired           -> the offer lapsed; reissue if still appropriate

An integration that reports "credential issued" at the point of the API call is telling users something that is not yet true.

Validate claims before you call

Issuance rejects claims not declared in the credential configuration. Catching that in your own validation gives a better error message and one less round trip.

Also decide what to do with missing optional claims: omitting them and sending an empty string are different, and the second produces a credential asserting an empty value.

Handle failure properly

Status Action
400 A bug or bad data. Log and surface. Do not retry
409 Idempotency key reused with a different payload. Investigate — likely a key collision in your code
429 Back off and retry
5xx Retry with backoff, same idempotency key

Log x-ms-request-id on every failure. It is the difference between a diagnosable report and "it broke".

Least privilege

Your issuance service needs credentials.issue and probably credentials.read. It does not need issuers.manage, credentials.revoke or anything about verification.

Use a separate key for the setup pipeline with the management scopes. A leaked issuance key should not be able to reconfigure your issuer.

Before production

  • Configuration scripted and run against Production with a did_live. key
  • Key read from a secret store, never committed
  • A startup assertion that the key's environment is the one you expect
  • x-ms-request-id logged on every response
  • Retries carrying idempotency keys
  • awaiting_wallet represented honestly in your model and your UI

Edit this page on GitHub