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"),
vaultProfile = employeeVaultProfile,
governance = employeeGovernance,
),
),
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 an OCA capture bundle 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.
Configure deferred issuance in the credential binding as well as the connector's execution policy. An asynchronous connector setting alone is not a substitute for the credential's deferral and approval gates. See callback ingress for the inbound, token-authorized contribution interface.
Failure, Retry, and Delivery Outcomes
Choose the failure policy independently from the protocol stage. For example, failure to retrieve a required employment claim should prevent issuance, whereas a downstream notification may use a separately monitored delivery path.
failureEffect | Effect when an inline invocation fails |
|---|---|
FAIL_PROTOCOL | Return the invocation error to the calling protocol integration |
RETRY_ONLY | Also return the error for inline execution; this value alone does not schedule a durable retry |
CONTINUE_WITHOUT_EFFECT | Continue with a FAILED_CONTINUED result and no successful integration effect |
DEAD_LETTER | Return a DEAD_LETTERED dispatch result |
QUARANTINE | Return a QUARANTINED dispatch result |
For durable retry, dead-letter storage, and operator replay, use the VDX-backed connector execution services. A dispatch result and a persisted dead-letter record are different things: check the run, its events, and the stored failure record before initiating recovery. Keep the invocation binding id, protocol correlation id, run id, and event cursor together so a delivery can be traced back to its issuance session. A successful HTTP submission is not proof of a successful sink commit.
Replay is an authorized recovery operation, not a new issuance request. Record the reason, verify the recovered run and destination effect, and handle duplicate delivery idempotently. Do not issue a second credential just to retry an unrelated downstream notification.
For presentation integrations, OID4VP_AFTER_VALIDATION bindings run only for a
valid presentation. Their selected fields and delivery results are separate from
the verifier's status callback. A delivery failure must not be interpreted as a
new cryptographic assessment of the presented credential. See the
OID4VP integration guide for session creation, status
retrieval, and result handling.
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.
A VAULT_RETENTION binding must provide both vaultProfile and governance.
In the pipeline example, employeeVaultProfile and employeeGovernance represent
your configured retention destination and governance policy. Missing or invalid
vault governance fails before execution, even when the binding would otherwise
continue after an operational delivery failure.
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.