Skip to main content

Issue a Credential Directly to a Wallet

This page walks through pushing a credential into a Business Wallet unattended, no QR code, no redirect, no accept step. It is a two-call sequence:

  1. Create a credential offer with the Start Issue Credential API.
  2. Push that offer into the target wallet with directIssue=true.

Preconditions

  • A credential template is configured for the credential you want to issue.
  • OAuth2 or an API key is enabled for your backend.
  • The issuer is registered in the target wallet as a trusted contact with directIssueAllowed = true. Without this, the credential is accepted and then discarded.
  • You know the walletExternalKey of the target wallet.

Step 1: Create the credential offer

Call the Start Issue Credential API with the template key and the credential attributes. Unlike the QR flow, you do not request a qr_code.

POST /api/v2/credential/issue
Content-Type: application/json
{
"correlation_id": "89cba89",
"template_id": "ebw_oid",
"claims": {
"id": "90004760",
"name": "CoolSoft B.V.",
"issuing_authority": "Credenco",
"issuing_country": "NL"
}
}

The response contains the credential offer URI:

{
"correlation_id": "89cba89",
"request_uri": "openid-credential-offer://?credential_offer_uri=https://wallet.credenco.com/api/oidc/credential-offer/abc123",
"status_uri": "https://wallet.credenco.com/api/v2/issue/89cba89"
}

You only need the credential_offer_uri query parameter from request_uri for the next step. Parse it out of the request_uri and URL-decode it.

Step 2: Push the offer into the wallet

Post the offer to the wallet's OIDC offer endpoint with directIssue=true, passing the credential_offer_uri you extracted:

POST /api/oidc/offer/{walletExternalKey}?directIssue=true&credential_offer_uri=https%3A%2F%2Fwallet.credenco.com%2Fapi%2Foidc%2Fcredential-offer%2Fabc123
ParameterInDescription
walletExternalKeypathExternal key of the target wallet.
directIssuequerySet to true for unattended issue. Omit it to drop the offer in the inbox instead.
credential_offer_uriqueryThe URL-encoded credential_offer_uri extracted from the request_uri in step 1.

The endpoint responds with 201 Created. Behind it, the wallet:

  1. Completes the OpenID4VCI exchange on the holder's behalf, with no user interaction.
  2. Verifies the issuer DID against the wallet's trusted contacts.
  3. Keeps the credential if the issuer is trusted, or discards it if not.
Why two calls?

The first call mints the credential offer from your issuer. The second call delivers that offer to a specific wallet. Splitting them lets the same offer mechanism serve both the attended (QR / redirect) flows and this unattended one, the only difference is directIssue=true.

Step 3: Check the result

Use the Get Issue Status API with your correlation_id to confirm issuance and retrieve the revocation_uuid (needed later to revoke the credential, if a status list is configured):

{
"status": "issue_request_created",
"correlation_id": "89cba89",
"revocation_uuid": "0e2c1f62-3392-462e-a25e-483fa0e2a3a3"
}
Trust is enforced silently

If the issuer is not a trusted contact of the target wallet, the OIDC offer endpoint still returns 201 and issuance still reports success, but the wallet discards the credential, so it never appears to the holder. Always register the trusted contact first.

Worked example: a company credential

This sequence places the ebw_oid company credential into a new Business Wallet:

  1. The backend collects the official company info (CoC number, legal name).
  2. It calls POST /api/v2/credential/issue with template_id: "ebw_oid" and the company claims (id, name, issuing_authority: "Credenco", issuing_country: "NL").
  3. It extracts the credential_offer_uri from the response and posts it to POST /api/oidc/offer/{walletExternalKey}?directIssue=true.
  4. Because Credenco is a trusted contact of the wallet, the credential is retained and is present the first time the user opens their wallet.