Upgrading Your Controller to AIP 2.0¶
This guide helps you upgrade your controller application to use AIP 2.0 endpoints and features when working with ACA-Py.
Overview¶
AIP 2.0 introduces significant changes to how credential and presentation exchanges are handled. AIP 1.0 exclusively supported Indy credentials. AIP 2.0 supports multiple credential formats, providing better interoperability and flexibility.
Key differences from AIP 1.0:
- New REST endpoints under
/issue-credential-2.0and/present-proof-2.0namespaces - Format-specific filters in request bodies
- New exchange record types with updated field names
- Updated webhook topics and payload structures
- Enhanced state management
Credential format options:
anoncredsfilter (Recommended): Requires upgraded wallet type (askar-anoncreds). This is the recommended option offering improved performance, better features, and future-proof compatibility.indyfilter: Backward compatible with AIP 1.0, works with existing Indy wallets (no wallet upgrade required)
💡 Recommendation: While the
indyfilter provides backward compatibility, we strongly recommend upgrading toaskar-anoncredswallet and using theanoncredsfilter. The AnonCreds format offers better performance, improved features, and is the future direction for credential formats. See the AnonCreds Filter section for upgrade instructions.
Prerequisites¶
- ACA-Py instance with AIP 2.0 support (typically ACA-Py 0.7.0 or later)
- Controller application using ACA-Py's Admin API
- Understanding of Aries protocols and REST APIs
- For
anoncredsfilter: ACA-Py started with--wallet-type askar-anoncreds(in multitenant mode, this setting can be overwritten per tenant) - For existing wallets with the
askartype, there's an endpoint to upgrade the wallet toaskar-anoncreds
API Endpoints¶
When upgrading your controller, update all API calls to use the new v2.0 endpoints. The v2.0 endpoints replace the v1.0 endpoints and provide enhanced functionality.
Credential Exchange Endpoints¶
GET /issue-credential-2.0/records- List exchanges (query params:connection_id,role,state,thread_id,limit,offset,order_by,descending)GET /issue-credential-2.0/records/{cred_ex_id}- Get single exchangePOST /issue-credential-2.0/create- Create offer (requiresfilterin body)POST /issue-credential-2.0/send-offer- Send offer (requiresconnection_idandfilter)POST /issue-credential-2.0/records/{cred_ex_id}/send-request- Send requestPOST /issue-credential-2.0/records/{cred_ex_id}/issue- Issue credentialPOST /issue-credential-2.0/records/{cred_ex_id}/store- Store credentialDELETE /issue-credential-2.0/records/{cred_ex_id}- Remove exchange
Presentation Exchange Endpoints¶
GET /present-proof-2.0/records- List exchanges (query params:connection_id,role,state,thread_id,limit,offset)GET /present-proof-2.0/records/{pres_ex_id}- Get single exchangeGET /present-proof-2.0/records/{pres_ex_id}/credentials- List available credentialsPOST /present-proof-2.0/send-proposal- Send proposalPOST /present-proof-2.0/create-request- Create request (requirespresentation_requestin body)POST /present-proof-2.0/send-request- Send requestPOST /present-proof-2.0/records/{pres_ex_id}/send-presentation- Send presentationPOST /present-proof-2.0/records/{pres_ex_id}/verify-presentation- Verify presentationDELETE /present-proof-2.0/records/{pres_ex_id}- Remove exchange
Exchange Records¶
AIP 2.0 introduces new exchange record types that replace the v1.0 records. Understanding these record structures is essential for properly handling exchanges in your controller.
Credential Exchange Records (V20CredExRecord)¶
Key fields:
cred_ex_id: Unique identifier for the exchange (replacescredential_exchange_id)connection_id: Connection identifierthread_id: Thread identifier for message threadingstate: Current state of the exchangerole: Role in the exchange (issuerorholder)initiator: Who initiated the exchange (selforexternal)credential_definition_id: Credential definition identifierschema_id: Schema identifiercredential_id: Stored credential identifier (after issuance)auto_offer: Whether to automatically send offersauto_issue: Whether to automatically issue credentialsauto_remove: Whether to automatically remove the record on completioncreated_at: Timestamp when the record was createdupdated_at: Timestamp when the record was last updated
Exchange States:
proposal-sent: Proposal has been sentproposal-received: Proposal has been receivedoffer-sent: Offer has been sentoffer-received: Offer has been receivedrequest-sent: Request has been sentrequest-received: Request has been receivedcredential-issued: Credential has been issuedcredential-received: Credential has been receiveddone: Exchange completed successfullyabandoned: Exchange was abandoned
Presentation Exchange Records (V20PresExRecord)¶
Key fields:
pres_ex_id: Unique identifier for the exchange (replacespresentation_exchange_id)connection_id: Connection identifierthread_id: Thread identifier for message threadingstate: Current state of the exchangerole: Role in the exchange (proverorverifier)initiator: Who initiated the exchange (selforexternal)auto_present: Whether to automatically send presentationsauto_verify: Whether to automatically verify presentationsverified: Boolean indicating verification result (for verifier role, after verification)verified_msgs: Verification result messagescreated_at: Timestamp when the record was createdupdated_at: Timestamp when the record was last updated
Exchange States:
proposal-sent: Proposal has been sentproposal-received: Proposal has been receivedrequest-sent: Request has been sentrequest-received: Request has been receivedpresentation-sent: Presentation has been sentpresentation-received: Presentation has been receiveddone: Exchange completed successfullyabandoned: Exchange was abandoned
Filters¶
AIP 2.0 uses format-specific filters in request bodies when creating credential offers and presentation requests. Unlike AIP 1.0, AIP 2.0 supports multiple credential formats, allowing your controller to specify filters for different credential types.
Credential Exchange Filters¶
The filter field is required in requests to endpoints like:
POST /issue-credential-2.0/createPOST /issue-credential-2.0/send-offerPOST /issue-credential-2.0/send-request
You must include at least one format filter (indy or anoncreds). You can include both formats to allow the holder to choose which format they prefer.
AnonCreds Filter (Requires Wallet Upgrade)¶
💡 Recommended: The
anoncredsfilter uses the newer AnonCreds format, which offers improved performance, better features, and is the recommended approach for new deployments. If you haven't already upgraded your wallet, we encourage you to do so.
The anoncreds filter uses the newer AnonCreds format and requires upgrading your wallet to askar-anoncreds:
Important: To use the anoncreds filter, start ACA-Py with --wallet-type askar-anoncreds:
Multitenant mode: In multitenant mode, the wallet type setting can be overwritten per tenant. Each tenant can have its own wallet type configuration.
Upgrading existing wallets: For existing wallets with the askar type, you can upgrade to askar-anoncreds using the wallet upgrade endpoint:
⚠️ Warning: This upgrade is irreversible. You cannot downgrade from
askar-anoncredsback toaskar. It is highly recommended to back up your wallet and test the upgrade in a development environment before upgrading a production wallet.
Benefits of upgrading to askar-anoncreds:
- Improved performance and efficiency
- Better support for modern credential features
- Future-proof solution aligned with AnonCreds specifications
- Enhanced interoperability with other AnonCreds-compatible systems
AnonCreds filter fields:
cred_def_id(string, optional): Credential definition identifierschema_id(string, optional): Schema identifierschema_issuer_id(string, optional): Schema issuer IDschema_name(string, optional): Schema nameschema_version(string, optional): Schema versionissuer_id(string, optional): Credential issuer ID
Indy Filter (Backward Compatible)¶
The indy filter provides backward compatibility with AIP 1.0 and works with existing Indy wallets. It has a similar structure to the anoncreds filter:
Indy filter fields:
cred_def_id(string, optional): Credential definition identifierschema_id(string, optional): Schema identifierschema_issuer_did(string, optional): Schema issuer DIDschema_name(string, optional): Schema nameschema_version(string, optional): Schema version (e.g., "1.0")issuer_did(string, optional): Credential issuer DID
Presentation Request Filters¶
When creating presentation requests, you specify a presentation_request object that supports multiple proof formats. The presentation_request field is used in endpoints like:
POST /present-proof-2.0/create-requestPOST /present-proof-2.0/send-request
You must include at least one format (indy or anoncreds).
For detailed examples and advanced features of building presentation requests, see the AnonCreds Specification - Create Presentation Request section.
AnonCreds Proof Request¶
For AnonCreds proof requests:
{
"presentation_request": {
"anoncreds": {
"name": "Proof of Identity",
"version": "1.0",
"requested_attributes": {
"attr1_referent": {
"name": "name",
"restrictions": [
{
"cred_def_id": "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag"
}
]
}
},
"requested_predicates": {
"pred1_referent": {
"name": "age",
"p_type": ">=",
"p_value": 18,
"restrictions": [
{
"cred_def_id": "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag"
}
]
}
},
"non_revoked": {
"from": 1234567890,
"to": 1234567890
}
}
}
}
Indy Proof Request¶
The indy proof request has a similar structure to the anoncreds proof request. Use "indy" instead of "anoncreds" in the presentation_request object for backward compatibility with AIP 1.0.
Webhooks¶
When upgrading to AIP 2.0, your controller's webhook handler must be updated to process the new v2.0 webhook topics and payload structures. Configure webhooks using --webhook-url when starting ACA-Py:
Webhooks are sent as POST requests to the configured URL with the topic appended as a path component.
Credential Exchange Webhooks¶
Topic: issue_credential_v2_0
Webhook URL: https://your-controller.example.com/webhooks/issue_credential_v2_0
Example payload:
{
"connection_id": "conn-123",
"cred_ex_id": "cred-ex-456",
"thread_id": "thread-789",
"state": "offer-sent",
"role": "issuer",
"initiator": "self",
"credential_definition_id": "cred-def-id",
"schema_id": "schema-id",
"auto_offer": false,
"auto_issue": false,
"auto_remove": false,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:01Z"
}
Key webhook fields:
cred_ex_id: The credential exchange identifier (use this to retrieve full details via API)state: Current state of the exchangerole: Your role in the exchange (issuerorholder)connection_id: Connection identifierthread_id: Thread identifier for message correlation
Presentation Exchange Webhooks¶
Topic: present_proof_v2_0
Webhook URL: https://your-controller.example.com/webhooks/present_proof_v2_0
Example payload:
{
"connection_id": "conn-123",
"pres_ex_id": "pres-ex-456",
"thread_id": "thread-789",
"state": "request-received",
"role": "prover",
"initiator": "external",
"auto_present": false,
"auto_verify": false,
"verified": null,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:01Z"
}
Key webhook fields:
pres_ex_id: The presentation exchange identifier (use this to retrieve full details via API)state: Current state of the exchangerole: Your role in the exchange (proverorverifier)connection_id: Connection identifierthread_id: Thread identifier for message correlationverified: Boolean indicating verification result (for verifier role, after verification)
Webhook Processing Best Practices¶
Idempotency: Webhooks may be delivered multiple times. Implement idempotent handlers using the exchange ID and state.
State Transitions: Monitor state transitions to trigger appropriate actions:
offer-received→ Retrieve offer details and decide whether to requestrequest-received→ Issue credentialcredential-received→ Store credentialrequest-received(presentation) → Prepare and send presentationpresentation-received→ Verify presentation
Error Handling: Check for error_msg field in webhook payloads to handle errors.
Webhook Topics: Ensure your webhook handler distinguishes between v1.0 and v2.0 topics:
- v1.0:
issue_credential,present_proof - v2.0:
issue_credential_v2_0,present_proof_v2_0
Migration Steps¶
This section provides a step-by-step guide for migrating your controller code from AIP 1.0 to AIP 2.0.
1. Update API Calls¶
Replace all v1.0 endpoint calls in your controller code with v2.0 equivalents:
Before (v1.0):
GET /issue-credential/recordsPOST /issue-credential/send-offer
After (v2.0):
GET /issue-credential-2.0/recordsPOST /issue-credential-2.0/send-offer
2. Update Webhook Handlers¶
Update your controller's webhook handlers to process v2.0 topics:
Before (v1.0):
- Webhook topic:
issue_credential - Field name:
credential_exchange_id
After (v2.0):
- Webhook topic:
issue_credential_v2_0 - Field name:
cred_ex_id(note: field name changed)
Key changes:
- Update webhook route to handle
/webhooks/issue_credential_v2_0path - Use
cred_ex_idinstead ofcredential_exchange_idto access the exchange ID - Handle state transitions:
offer-received,request-received,credential-received
Supporting both versions during migration:
- Check the topic name to determine which field name to use
- Use
cred_ex_idfor v2.0 topics,credential_exchange_idfor v1.0 topics
3. Update Filter Logic in Request Bodies¶
Since AIP 1.0 exclusively supported Indy credentials, you'll need to choose between using the indy filter (backward compatible) or anoncreds filter (requires wallet upgrade):
Before (v1.0 - Credential Offer):
- Use
credential_definition_idfield directly - No filter object required
After (v2.0 - Using indy filter, backward compatible):
- Wrap credential definition ID in
filter.indy.cred_def_id - Use
@type: "issue-credential/2.0/credential-preview"in credential preview
After (v2.0 - Using anoncreds filter, requires askar-anoncreds wallet):
- Wrap credential definition ID in
filter.anoncreds.cred_def_id - Requires
--wallet-type askar-anoncredswhen starting ACA-Py
Before (v1.0 - Presentation Request):
- Use
proof_requestobject directly
After (v2.0 - Presentation Request):
- Use
presentation_requestobject with format-specific request (indyoranoncreds) - Wrap proof request structure in
presentation_request.indyorpresentation_request.anoncreds
4. Update Exchange Record Field Access¶
Update your controller code that accesses exchange record fields:
Before (v1.0):
credential_exchange_id- Credential exchange identifiercredential_definition_id- Credential definition identifierpresentation_exchange_id- Presentation exchange identifier
After (v2.0):
cred_ex_id- Credential exchange identifier (shorter name)cred_def_id- Credential definition identifier (shorter name)pres_ex_id- Presentation exchange identifier (shorter name)
Key field name changes:
credential_exchange_id→cred_ex_idpresentation_exchange_id→pres_ex_idcredential_definition_id→cred_def_id(within filter)
5. Update State Handling Logic¶
Update your controller's state handling logic to account for v2.0 state names:
Before (v1.0):
- States use snake_case:
offer_sent,request_received
After (v2.0):
- States use kebab-case:
offer-sent,request-received,credential-received
State name changes:
offer_sent→offer-sentoffer_received→offer-receivedrequest_sent→request-sentrequest_received→request-receivedcredential_received→credential-receivedpresentation_sent→presentation-sentpresentation_received→presentation-received
Credential Management¶
As an issuer, you may need to revoke credentials that have been issued. AIP 2.0 supports credential revocation for both indy and anoncreds credential formats.
Revoking Credentials¶
To revoke a credential, use the revocation endpoint that matches the filter used when the credential was issued:
If credential was issued using anoncreds filter (askar-anoncreds wallet):
If credential was issued using indy filter (askar wallet):
The endpoint you use depends on the filter used during issuance, not the credential format itself.
Revocation Request Parameters¶
The simplest way to revoke a credential is using the cred_ex_id (credential exchange ID) from when the credential was issued:
{
"cred_ex_id": "cred-ex-456",
"publish": true,
"notify": true,
"connection_id": "conn-123",
"comment": "Credential revoked due to policy violation"
}
Request parameters:
cred_ex_id(string, optional): Credential exchange ID from the issuance. If provided, revocation details are retrieved automatically.rev_reg_id(string, optional): Revocation registry ID. Required ifcred_ex_idis not provided.cred_rev_id(string, optional): Credential revocation ID. Required ifcred_ex_idis not provided.publish(boolean, optional): Whether to publish the revocation to the ledger immediately. Defaults tofalse. Iffalse, revocation is marked as pending.notify(boolean, optional): Whether to send a revocation notification to the credential holder. Defaults tofalse.connection_id(string, optional): Connection ID of the credential holder. Required ifnotifyistrue.notify_version(string, optional): Version of revocation notification protocol to use. Defaults to"v1_0".comment(string, optional): Optional comment explaining the reason for revocation.
Revocation Workflow¶
- Revoke the credential using the revocation endpoint with
cred_ex_id - Publish to ledger by setting
publish: trueto immediately update the revocation registry on the VDR, or setpublish: falseto mark as pending - Notify the holder by setting
notify: trueand providing theconnection_idto send a revocation notification
Example: Revoking with immediate publication and notification
{
"cred_ex_id": "cred-ex-456",
"publish": true,
"notify": true,
"connection_id": "conn-123",
"comment": "Credential expired"
}
Example: Revoking and marking as pending (publish later)
Publishing Pending Revocations¶
If you revoked credentials with publish: false, you can publish all pending revocations later. Use the endpoint that matches the filter used when issuing:
If credentials were issued using anoncreds filter (askar-anoncreds wallet):
If credentials were issued using indy filter (askar wallet):
Revocation Registries¶
When issuing credentials with revocation support, revocation registries are automatically created. These registries track the revocation status of credentials and are stored on a Verifiable Data Registry (VDR):
- Revocation Registry Definition: Defines the revocation registry and its parameters
- Revocation Status Lists: Published periodically to update the revocation status on the ledger
- Tails Files: Support files used for generating non-revocation proofs
Verifier Considerations¶
When requesting presentations, verifiers can include non_revoked fields in presentation requests to verify that credentials haven't been revoked. See the AnonCreds Specification - Create Presentation Request for details on requesting non-revocation proofs.
For more details on revocation, see the AnonCreds Specification section on revocation.