This document provides a quick overview of the most common steps: creating wallets, issuing credentials, and verifying credentials. More detailed descriptions of the different steps can be found in the Example Flows document.
Note: It is always helpful to inspect the Swagger UIs to understand the available endpoints, their expected inputs, and the corresponding outputs. If requests fail, check the Swagger UI to ensure you've called the correct endpoint with the correct data. The Swagger UIs are accessible, under a vanilla setup, at:
- Multitenant-Admin (Managing tenants) -> http://cloudapi.127.0.0.1.nip.io/tenant-admin/docs
- Governance (Acting as governance) -> http://cloudapi.127.0.0.1.nip.io/governance/docs
- Tenant (Acting as a tenant) -> http://cloudapi.127.0.0.1.nip.io/tenant/docs
- Public (Interface to read the trust registry) -> http://cloudapi.127.0.0.1.nip.io/public/docs
If you find any model descriptions unclear in the document below, try checking the Swagger UI documentation before opening an issue.
It is also recommended to understand and perhaps set up a webhook listener (refer to our Webhooks doc). This will significantly aid in observing the activities occurring in the ACA-Py instances in the background.
The admin "wallet" is already configured as it is not a subwallet on a multi-tenant agent. To create subwallets for tenants, you have to use the tenant admin role. The permissions and routing to the correct ACA-Py instance are handled by acapy-cloud under the hood. You need to provide two things:
-
Authorization in the header:
{"x-api-key": "tenant-admin.APIKEY"}
, wheretenant-admin
is a fixed term representing the role, andAPIKEY
is the auth token you must know and provide. Note: This auth string is separated by a dot, so keep that in there. -
The wallet payload (body) of the wallet you want to create, e.g.,
{ "wallet_label": "Demo Issuer", "wallet_name": "Faber", "roles": ["issuer"], "group_id": "API demo", "image_url": "https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png" }
Send this to the /tenant-admin/v1/admin/tenants
endpoint. You can omit the
roles field altogether or pass "issuer" and/or "verifier". All payloads are
documented in Swagger, so if in doubt, consult the
Multitenant-Admin docs.
Creating a tenant with roles will update the trust registry by writing an entry
for an actor
, including wallet details and its associated roles.
If you wish to later update entities roles, you will have to do that again via
the tenants admin API, which will handle interacting with the trust registry
(see also the Swagger for update tenant
).
To create schemas and effectively write them to the ledger as well as registering them on the trust registry, use the governance role:
-
Provide the following authentication header:
{ "x-api-key": "governance.ADMIN_API_KEY" }
Replace the
ADMIN_API_KEY
with the actual API key. Keep the dot and recall thatgovernance
is a keyword known to acapy-cloud as a role. It will resolve the correct endpoint and available actions based on the role and provided token. -
Provide the information about the schema, e.g.:
{ "name": "yourAwesomeSchemaName", "version": "1.3.7", "attributes": ["skill", "age"] }
Note that you will need to have a public DID to do so (if your agent lacks one, you can use the governance role to create one: see Bootstrapping the Trust Ecosystem). Run the request with the header from 1. and the payload from 2. against the Governance URL and endpoint
/v1/definitions/schemas
(POST method). Upon success, the created schema will be returned.
-
Register a schema (see above)
-
Register an issuer (create a wallet passing "issuer" as a role - see above)
-
Issuer creates credential definition
-
Create a connection between the issuer and prospective holder
A connection can be created using the
/v1/connections/did-exchange/create-request
endpoint of the Tenant URL. Here, you will also need to authenticate via the header, e.g., using{ "x-api-key": "tenant.WALLET_TOKEN" }
where the
WALLET_TOKEN
is the bearer token you get from the create wallet response for a tenant wallet.Use the issuer's public DID as the
their_public_did
parameter in the request.This will automatically establish a connection between the issuer and the holder, and can be verified by checking the connection records (
GET /v1/connections
) for either party.Alternatively, an OOB invitation can be used to create a connection. Please see this example docs for more details.
-
Issue a credential from issuer to prospect holder
- Create and send a credential authenticating with the issuer. The credential has the form:
{ "connection_id": "string", "schema_id": "string", "attributes": { "additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string" } }
This should correspond to a schema you created previously, and the
connection_id
is the ID of the connection you created in the previous step. If you're unsure what that ID is, you can always run a GET request against theconnections
endpoint to find it. -
Accept and store the credential in the holder wallet
-
Using the holder (authenticating with the holder auth header), issue a GET request against the
/v1/issuer/credentials
endpoint, providing the connection ID of the connection established above. Note: The connection IDs are unique for each entity, so the connection between the issuer and the holder is one connection with two separate connection IDs - one for the issuer and one for the holder. This will provide you with a credential record that should be in the state of being offered. Providing the connection ID again, you can now use the holder to store the credential by posting to/v1/issuer/credentials/{credential_exchange_id}/store
-
(Optional) Get yor credentials from your wallet (
wallet/credentials
) check whether the credential is actually stored. You can also check this via waypoint.
-
Ensure you followed Issuing a Credential steps to have a wallet with a credential (prover)
-
Register an entity as a verifier (verifier)
- In other words, create or update a wallet, passing the role "verifier"
-
Create a connection between 1. prover and 2. verifier the same way as in Issuing a Credential
-
Create a proof request (
/v1/verifier/create-request
) using the verifier and send it to the prover. Consult the Swaggerverifier
endpoints. POST to/v1/verifier/send-request
with a payload of the following form, replacing the values accordingly (and ensuring they can be covered by the previously created schema and issued credential):{ "connection_id": "string", "indy_proof_request": { "requested_attributes": { "additionalProp1": { "name": "string", "names": ["string"], "non_revoked": {}, "restrictions": [] } }, "requested_predicates": { "additionalProp1": { "name": "string", "p_type": "<", "p_value": 0, "non_revoked": {}, "restrictions": [] }, "name": "string", "non_revoked": {}, "version": "string" } } }
-
Send the proof request.
- From the prover, get the proof records using
/v1/verifier/proofs
and create a proof request you want to send, just as above (same payload format and endpoint).
- From the prover, get the proof records using
-
Accept the proof request.
- From the verifier, you can now accept (or reject; see
/v1/verifier/reject-request
on Swagger for payload) by POSTing to/v1/verifier/send-request
, adjusting the payload to:
{ "proof_id": "string", "indy_presentation_spec": { "requested_attributes": { "additionalProp1": { "cred_id": "string", "revealed": true } }, "requested_predicates": { "additionalProp1": { "cred_id": "string", "timestamp": 0 } }, "self_attested_attributes": { "additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string" } } }
- From the verifier, you can now accept (or reject; see
-
(Optional) Wait for the prover and verifier's webhook, using waypoint, and see that the presentation is acknowledged. Alternatively, GET the proof records and check the
state
field.
Note: There are multiple flows to this "dance". For further details, you may want to refer to the official Aries-RFC.
If you'd like to delve deeper into more of acapy-cloud's capabilities, please see the Example Flows document.