iGrant.io OWS integration - overview
When to use
Read this skill before or alongside any of the igrantio-* backend/frontend
skills. It defines the shared architecture and is the single source of truth
for OWS endpoints, request payloads, the response fields you must process, and
the integrator intake below. Every other skill in this collection assumes the
contracts described here.
Integrator intake
Run this interview before writing any integration code. Ask one question at
a time, wait for the answer, and offer the recommended default with each.
Facts you can discover in the project (framework, existing env vars, an
existing backend) - look them up instead of asking; only the decisions go to
the integrator. Record the answers; every later choice hangs off them.
- Environment - demo (
https://demo-api.igrant.io), staging
(https://staging-api.igrant.io), or a custom OWS deployment (ask for its
base URL)? Recommend demo to start; switching later is a config change.
- API key - do you have an Organisation Wallet Suite API key? If not:
create an organisation at https://demo.igrant.io or contact
support@igrant.io. It lives server-side only
(env var / secret manager) - the browser never sees it.
- Tenancy - one organisation, or multiple tenants each with their own
API key? Single tenant is one env var; multi-tenant needs a
TenantStore (see igrantio-backend-proxy).
- Backend host - extend an existing Node/TypeScript backend, or
scaffold a fresh Express service? Look this up first; ask only if the
repo is empty or ambiguous.
- Webhook reachability - what public URL will receive OWS webhooks?
Local dev needs a tunnel (e.g. an ngrok-style forwarder) or the SSE
fallback of polling history endpoints.
- Frontend - which framework, and default iGrant.io look
(
igrantio-usecase-ui) or the integrator's own design system? QR
options (centre logo, green tick) are asked by igrantio-qr-code.
The interview is done when each answer is recorded and none conflicts with a
discovered fact.
The architecture in one paragraph
The browser never holds an OWS API key. A tenant backend proxies a small
allow-list of OWS endpoints, injecting the organisation's ApiKey. When a wallet
completes a flow, OWS delivers a signed webhook to the tenant backend; the
backend verifies the HMAC signature, extracts the exchange id (the correlation
key) from the webhook, and stores the event. The browser holds an SSE
connection keyed by that exchange id and updates the moment the event arrives.
See references/architecture.md for the full
data-flow, path allow-list, HMAC scheme, and the exchange-id correlation table.
The two flows
Issuance (OpenID4VCI) - you are the issuer
POST …/credential/issue with credentialDefinitionId + claims → response
carries credentialHistory.CredentialExchangeId (correlation key) and
credentialHistory.credentialOffer (the openid-credential-offer:// URI → render as QR / deep link).
- Open SSE on the
CredentialExchangeId.
- Wallet scans, accepts → webhook
openid.credential.credential_accepted /
token_issued → SSE fires → mark issued.
- Deferred variant: issue with
issuanceMode: "Deferred", then on
openid.credential.offer_received PUT …/credential/history/{CredentialExchangeId}
with the collected claims.
Verification (OpenID4VP + DCQL) - you are the verifier / relying party
POST …/verification/send with presentationDefinitionId (a stored DCQL query)
→ response carries verificationHistory.presentationExchangeId (correlation key)
and verificationHistory.vpTokenQrCode (the openid4vp:// URI → render as QR / DC API request).
- Open SSE on the
presentationExchangeId.
- Wallet presents → webhook
openid.presentation.presentation_acked.v3 /
digitalwallet.presentation.verified → SSE fires with
data.presentation.vpTokenResponse (non-empty = responded),
data.presentation.presentation[0] (the disclosed claims), and
data.presentation.verified.
Exact methods, paths, payloads, and full response schemas:
references/api-reference.md.
Environments (OWS base URL)
| Env |
Base URL |
| Demo |
https://demo-api.igrant.io |
| Staging |
https://staging-api.igrant.io |
The browser targets the tenant backend base URL (e.g. https://your-host/ows/<tenant>),
never these directly.
Glossary
- OWS - Organisation Wallet Suite (the iGrant.io API surface).
- credentialDefinitionId - a stored issuer credential configuration.
- presentationDefinitionId - a stored verifier DCQL query / presentation definition.
- CredentialExchangeId / presentationExchangeId - per-transaction id; the SSE correlation key and the webhook's
external_id.
- credentialOffer / vpTokenQrCode - the wallet-facing URI you render as a QR code or open as a same-device deep link.
- exchange id - umbrella term for the two above.
Validation / done criteria
- You can name the exact OWS path, method, and payload for issue, deferred-issue,
read-history, verification-send, and read-verification-history without guessing.
- You know which response field is the QR URI and which is the SSE correlation key
for both flows.
- You know which webhook topics to subscribe to and how each maps to an exchange id.
Documentation & workflows
When anything is unclear, consult the iGrant.io documentation before guessing:
1---2name: igrantio-ows-overview3description: Architecture, glossary, and the full iGrant.io Organisation Wallet Suite (OWS) API reference for issuing and verifying EUDI Wallet (EUDIW) and European Business Wallet (EUBW) credentials under eIDAS 2.0 (eIDAS2), covering issuance (OpenID4VCI) and verification (OpenID4VP + DCQL), including which response fields to extract and how webhooks correlate to a browser session over SSE. Read this first before building an OWS issuer or verifier backend/frontend, or whenever you need the exact OWS endpoint, payload, or response shape.4license: Apache-2.05---67# iGrant.io OWS integration - overview89## When to use10Read this skill before or alongside any of the `igrantio-*` backend/frontend11skills. It defines the shared architecture and is the **single source of truth**12for OWS endpoints, request payloads, the response fields you must process, and13the integrator intake below. Every other skill in this collection assumes the14contracts described here.1516## Integrator intake1718Run this interview before writing any integration code. Ask **one question at19a time**, wait for the answer, and offer the recommended default with each.20Facts you can discover in the project (framework, existing env vars, an21existing backend) - look them up instead of asking; only the decisions go to22the integrator. Record the answers; every later choice hangs off them.23241. **Environment** - demo (`https://demo-api.igrant.io`), staging25 (`https://staging-api.igrant.io`), or a custom OWS deployment (ask for its26 base URL)? _Recommend demo to start; switching later is a config change._272. **API key** - do you have an Organisation Wallet Suite API key? If not:28 create an organisation at <https://demo.igrant.io> or contact29 [support@igrant.io](mailto:support@igrant.io). It lives server-side only30 (env var / secret manager) - the browser never sees it.313. **Tenancy** - one organisation, or multiple tenants each with their own32 API key? _Single tenant is one env var; multi-tenant needs a33 `TenantStore` (see `igrantio-backend-proxy`)._344. **Backend host** - extend an existing Node/TypeScript backend, or35 scaffold a fresh Express service? _Look this up first; ask only if the36 repo is empty or ambiguous._375. **Webhook reachability** - what public URL will receive OWS webhooks?38 _Local dev needs a tunnel (e.g. an ngrok-style forwarder) or the SSE39 fallback of polling history endpoints._406. **Frontend** - which framework, and default iGrant.io look41 (`igrantio-usecase-ui`) or the integrator's own design system? QR42 options (centre logo, green tick) are asked by `igrantio-qr-code`.4344The interview is done when each answer is recorded and none conflicts with a45discovered fact.4647## The architecture in one paragraph48The browser never holds an OWS API key. A **tenant backend** proxies a small49allow-list of OWS endpoints, injecting the organisation's `ApiKey`. When a wallet50completes a flow, OWS delivers a **signed webhook** to the tenant backend; the51backend verifies the HMAC signature, extracts the **exchange id** (the correlation52key) from the webhook, and stores the event. The browser holds an **SSE**53connection keyed by that exchange id and updates the moment the event arrives.5455See [`references/architecture.md`](./references/architecture.md) for the full56data-flow, path allow-list, HMAC scheme, and the exchange-id correlation table.5758## The two flows5960### Issuance (OpenID4VCI) - you are the issuer611. `POST …/credential/issue` with `credentialDefinitionId` + claims → response62 carries `credentialHistory.CredentialExchangeId` (correlation key) and63 `credentialHistory.credentialOffer` (the `openid-credential-offer://` URI → render as QR / deep link).642. Open SSE on the `CredentialExchangeId`.653. Wallet scans, accepts → webhook `openid.credential.credential_accepted` /66 `token_issued` → SSE fires → mark issued.674. **Deferred** variant: issue with `issuanceMode: "Deferred"`, then on68 `openid.credential.offer_received` `PUT …/credential/history/{CredentialExchangeId}`69 with the collected claims.7071### Verification (OpenID4VP + DCQL) - you are the verifier / relying party721. `POST …/verification/send` with `presentationDefinitionId` (a stored DCQL query)73 → response carries `verificationHistory.presentationExchangeId` (correlation key)74 and `verificationHistory.vpTokenQrCode` (the `openid4vp://` URI → render as QR / DC API request).752. Open SSE on the `presentationExchangeId`.763. Wallet presents → webhook `openid.presentation.presentation_acked.v3` /77 `digitalwallet.presentation.verified` → SSE fires with78 `data.presentation.vpTokenResponse` (non-empty = responded),79 `data.presentation.presentation[0]` (the disclosed claims), and80 `data.presentation.verified`.8182Exact methods, paths, payloads, and full response schemas:83[`references/api-reference.md`](./references/api-reference.md).8485## Environments (OWS base URL)86| Env | Base URL |87| --- | --- |88| Demo | `https://demo-api.igrant.io` |89| Staging | `https://staging-api.igrant.io` |9091The browser targets the **tenant backend** base URL (e.g. `https://your-host/ows/<tenant>`),92never these directly.9394## Glossary95- **OWS** - Organisation Wallet Suite (the iGrant.io API surface).96- **credentialDefinitionId** - a stored issuer credential configuration.97- **presentationDefinitionId** - a stored verifier DCQL query / presentation definition.98- **CredentialExchangeId / presentationExchangeId** - per-transaction id; the **SSE correlation key** and the webhook's `external_id`.99- **credentialOffer / vpTokenQrCode** - the wallet-facing URI you render as a QR code or open as a same-device deep link.100- **exchange id** - umbrella term for the two above.101102## Validation / done criteria103- You can name the exact OWS path, method, and payload for issue, deferred-issue,104 read-history, verification-send, and read-verification-history without guessing.105- You know which response field is the QR URI and which is the SSE correlation key106 for both flows.107- You know which webhook topics to subscribe to and how each maps to an exchange id.108109## Documentation & workflows110111When anything is unclear, consult the iGrant.io documentation before guessing:112113- iGrant.io developer APIs (index): https://docs.igrant.io/docs/developer-apis114- Getting started: https://docs.igrant.io/docs/get-started/115- OpenID4VC API (issuer / verifier / webhook): https://docs.igrant.io/docs/category/openid4vc-api/issuer116- Workflow: issue a credential (OID4VCI): https://docs.igrant.io/docs/openID4vci-issue-credential-intime/117- Workflow: send and verify credentials (OID4VP): https://docs.igrant.io/docs/openID4vc-send-verify-credentials/