Connector Invocations
OID4VCI issuance integrations are modeled as connector invocations. A connector invocation is a durable binding from an issuance phase to a connector target. It can fetch fields, push selected data out, write evidence to a vault, emit notifications, or start a route-backed integration run.
Pick Your Integration Shape
| Your situation | Approach |
|---|---|
| Your backend already has the data when it creates the offer | Seed attributes or connector fields through the offer/session REST body |
| You want live data when the wallet requests the credential | Bind an ENRICHMENT_SOURCE connector invocation at OID4VCI_CREDENTIAL_REQUEST |
| You need to resolve identity before claim enrichment | Bind an enrichment invocation at OID4VCI_AUTHORIZATION or OID4VCI_TOKEN |
| You need final enrichment or policy state before signing | Bind an invocation at OID4VCI_PRE_ISSUE |
| You need retained evidence or re-issuance support | Bind a VAULT_RETENTION invocation at OID4VCI_POST_ISSUANCE |
| Your upstream is slow or asynchronous | Use an async/deferred execution policy and callback or deferred completion |
| You need audit or notifications | Bind NOTIFICATION or route orchestration invocations at the relevant phase |
Manual REST contributions still exist. Their contributorId field is provenance for pushed records; it is not the automatic integration binding model.
OID4VCI Phases
Connector invocation bindings can target these stages:
| Stage | Protocol moment |
|---|---|
OID4VCI_START | Offer/session start before grant-specific work |
OID4VCI_AUTHORIZATION | Authorization-code flow and AS/userinfo context |
OID4VCI_PRE_AUTHORIZED | Pre-authorized-code offer setup |
OID4VCI_TOKEN | Token exchange and access-token context |
OID4VCI_CREDENTIAL_REQUEST | Wallet calls /credential |
OID4VCI_PRE_ISSUE | After request/proof validation, before the credential is built/signed |
OID4VCI_DEFERRED | Wallet polls /credential_deferred |
OID4VCI_POST_ISSUANCE | After a credential response is produced |
OID4VCI_NOTIFICATION_RECEIPT | Wallet sends issuance notification receipt |
You do not need every phase. Bind only the phases that matter for the credential flow.
Registering an Invocation Binding
VDX-hosted deployments persist connector bindings through the connector API. This is the durable/registrable layer for connector routes, grants, throttling, run lineage, and invocation bindings; the EDK consumes the resolved connector model at issuance time.
POST /api/connector/v1/invocation/bindings
Authorization: Bearer <operator access token>
Content-Type: application/json
{
"binding": {
"invocationBindingId": "issuer-config-1:credential-request:hr-profile",
"ownerSurface": "OID4VCI",
"ownerReference": "issuer-config-1",
"stage": "OID4VCI_CREDENTIAL_REQUEST",
"role": "ENRICHMENT_SOURCE",
"target": {
"routeId": "hr-profile-route"
},
"exchangeMode": "PLATFORM_INITIATED_PULL",
"subsetMapping": {
"inputFields": [
{ "canonicalFieldPath": "employee.id" }
],
"producedFields": [
{ "canonicalFieldPath": "employee.givenName" },
{ "canonicalFieldPath": "employee.familyName" },
{ "canonicalFieldPath": "employment.department" }
]
},
"logicalContext": {
"tenantId": "tenant-1"
},
"executionPolicy": {
"timing": "INLINE_REQUIRED",
"failureEffect": "FAIL_PROTOCOL"
}
}
}
The binding id is stable and appears in audit/run lineage. The target can point at a route, connector instance, or operation binding depending on the deployment.
Composing a Pipeline
The issuer resolves a PipelineConfiguration for an issuer and requested credential configurations. The important field is invocationBindings:
val employeeCredentialPipeline = PipelineConfiguration(
pipelineId = "employee-credential-v2",
invocationBindings = listOf(
ConnectorInvocationBinding(
invocationBindingId = ConnectorInvocationBindingId("issuer-config-1:token:risk"),
ownerSurface = ConnectorOwnerSurface.OID4VCI,
ownerReference = "issuer-config-1",
stage = ConnectorInvocationStage.OID4VCI_TOKEN,
role = ConnectorInvocationRole.ENRICHMENT_SOURCE,
target = ConnectorInvocationTarget(routeId = ConnectorRouteId("risk-score-route")),
exchangeMode = ConnectorExchangeMode.PLATFORM_INITIATED_PULL,
subsetMapping = ConnectorSubsetMapping(
inputFields = listOf(ConnectorFieldSelector(protocolClaimPath = "subject")),
producedFields = listOf(ConnectorFieldSelector(canonicalFieldPath = "risk.score")),
),
logicalContext = ConnectorLogicalContext(tenantId = "tenant-1"),
),
ConnectorInvocationBinding(
invocationBindingId = ConnectorInvocationBindingId("issuer-config-1:credential-request:hr-profile"),
ownerSurface = ConnectorOwnerSurface.OID4VCI,
ownerReference = "issuer-config-1",
stage = ConnectorInvocationStage.OID4VCI_CREDENTIAL_REQUEST,
role = ConnectorInvocationRole.ENRICHMENT_SOURCE,
target = ConnectorInvocationTarget(routeId = ConnectorRouteId("hr-profile-route")),
exchangeMode = ConnectorExchangeMode.PLATFORM_INITIATED_PULL,
subsetMapping = ConnectorSubsetMapping(
inputFields = listOf(ConnectorFieldSelector(canonicalFieldPath = "employee.id")),
producedFields = listOf(
ConnectorFieldSelector(canonicalFieldPath = "employee.givenName"),
ConnectorFieldSelector(canonicalFieldPath = "employee.familyName"),
ConnectorFieldSelector(canonicalFieldPath = "employment.department"),
),
),
logicalContext = ConnectorLogicalContext(tenantId = "tenant-1"),
),
ConnectorInvocationBinding(
invocationBindingId = ConnectorInvocationBindingId("issuer-config-1:post-issuance:proof-vault"),
ownerSurface = ConnectorOwnerSurface.OID4VCI,
ownerReference = "issuer-config-1",
stage = ConnectorInvocationStage.OID4VCI_POST_ISSUANCE,
role = ConnectorInvocationRole.VAULT_RETENTION,
target = ConnectorInvocationTarget(routeId = ConnectorRouteId("proof-vault-route")),
exchangeMode = ConnectorExchangeMode.PLATFORM_INITIATED_PUSH,
logicalContext = ConnectorLogicalContext(tenantId = "tenant-1"),
),
),
claimsBindings = listOf(
CredentialClaimsBinding(
id = "EmployeeCredential",
semanticAttributeSetRef = employeeSetRef,
deferralPolicy = DeferralPolicy(enabled = false),
),
),
expectedInitialConnectorFields = setOf("employee.id"),
)
The pieces:
invocationBindingsis the ordered connector-native contract for OID4VCI runtime integration.subsetMapping.inputFieldsdeclares which connector fields, protocol claims, or canonical fields a connector needs.subsetMapping.producedFieldsdeclares the canonical fields later phases can consume.executionPolicycontrols inline, optional, async/deferred, and durable execution.claimsBindingsmaps eachcredential_configuration_idto a semantic attribute set and deferral/approval policy.expectedInitialConnectorFieldsis the caller contract for session/offer creation.
Pattern 1: Push Known Data
If the backend already has every field when it starts issuance, push attributes or connector fields through the offer or session REST body. This is a manual contribution path. It is useful for short-lived issuance where the data is known and stable.
Use this sparingly for PII-heavy payloads. If the wallet might claim the credential much later, seed connector fields and fetch the live values through connector invocations instead.
Pattern 2: Fetch Live Data at Credential Request
For most production integrations, seed a connector field such as employee.id at offer/session start and bind an ENRICHMENT_SOURCE connector invocation at OID4VCI_CREDENTIAL_REQUEST.
When the wallet calls /credential, the EDK passes accumulated attributes and connector fields to the connector route. Returned producedFields are written back into the session bag with connector provenance, then the credential claim assembler reads them.
Pattern 3: Async or Deferred Upstream
When an upstream cannot answer within the wallet request window, set the invocation execution policy to an async or durable timing:
ConnectorInvocationExecutionPolicy(
timing = ConnectorExecutionTiming.DEFERRED_ASYNC,
failureEffect = ConnectorFailureEffect.FAIL_PROTOCOL,
syncWaitWindowMillis = 5000,
)
The EDK may wait briefly for completion. If the integration is still pending, the wallet receives a deferred credential response and polls. Callback ingress can contribute the later fields into the same session before deferred issuance completes.
Post-Issuance Retention
By default, the session retains the bag only until session expiry and cleanup. For re-issuance or audit evidence, bind OID4VCI_POST_ISSUANCE with a VAULT_RETENTION role and a materialization policy. That keeps retention explicit and tied to connector governance and run lineage.
Operational Notes
- Use
ownerSurface = OID4VCIfor issuer pipeline bindings. - Use
ownerReferenceconsistently, usually the issuer configuration id or pipeline owner id. - Keep connector route execution and VDX registry state in VDX-owned modules.
- Keep the EDK model neutral: connector/public and pipeline public contracts must not import VDX packages.
- Manual contribution groups use
contributorIdonly as record provenance.