# Holder messaging via DIDComm

Holder messaging enables secure, encrypted communication between organizations and digital wallet holders using the DIDComm protocol. This feature allows verifiers to send authenticated requests directly to credential holders' wallets, enabling real-time verification workflows without requiring traditional authentication methods.

### Example use case

Consider a customer service scenario where enhanced identity verification is needed. A customer calls their bank to request a credit limit increase or password change. Rather than relying solely on traditional phone-based verification, the call center can leverage the customer's existing digital credentials for stronger authentication.

The process works as follows: the customer receives a credential during onboarding. The call center agent can initiate a credential verification request. The customer receives a secure message in their digital wallet via push notification, reviews the request, and responds using biometric authentication to unlock their wallet and confirm the data sharing.

### Technical implementation

#### Prerequisites

* Issuer has captured the holder's DID
* Issuer is using a valid message schema

{% hint style="info" %}
Current Truvera wallet implementation supports only YES/NO holder messaging schema. Customers using the wallet SDK can implement custom schemas based on their needs.
{% endhint %}

#### **Encrypted message dispatch**

Uses the Truvera API to send an encrypted DIDComm message to the holders DID.

On steps how to create the sender DID see the [Create DID](/truvera-api/dids.md) or step by step instructions in the [Truvera Workspace.](/truvera-workspace/create-an-organization-profile-did.md)\\

{% hint style="warning" %}
Static key (did:key method) cannot be used for the senders DID. Please use other available methods (e.g. did:cheqd).\
Recipient DID from the holder wallet will be a did:key.
{% endhint %}

The message script:

```javascript
const axios = require("axios").default;
const API_KEY = ;//Your Truvera API key// 
const API_URL = "https://api-testnet.truvera.io";//for production https://api.truvera.io
const WALLET_DID = ;//Holders wallet DID//

const apiClient = axios.create({
  baseURL: API_URL,
  headers: {
    "Content-Type": "application/json",
    "User-Agent": "insomnia/9.3.2",
    Authorization: `Bearer ${API_KEY}`,
  },
});

async function sendYesNoMessage() {
  const payload = {
    type: "text",
    payload: {
      senderName: , //Name of the message sender DID//
      senderDid: , //Message sender DID//
      senderLogo: , //Logo of the message sender DID//
      title: "Are you currently speaking with our customer support team?",
      question:
        "This confirms you initiated the call and helps prevent fraud. Your personal information will not be shared.",
      yesText: "Yes",
      noText: "No",
      expirationDate: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(),
      messageId: 'Internal-message-1234'//add a message ID to track the message
    },
    type: "https://schema.truvera.io/yes-no-payload-V1.json",
    senderDid: ,//the message sender DID//
    algorithm: "ECDH-1PU+A256KW",
    recipientDids: [WALLET_DID],
  };

  const { data: encryptedMessage } = await apiClient.post(
    "/messaging/encrypt",
    payload
  );
  console.log("Message encrypted successfully:", encryptedMessage);

  const sendPayload = {
    to: WALLET_DID,
    message: encryptedMessage.jwe,
  };

  const { data: sentMessage } = await apiClient.post(
    "/messaging/send",
    sendPayload
  );
  console.log("Message sent successfully:", sentMessage);
}


sendYesNoMessage();
```

#### Response payload from the wallet

```
{
    "messageId": "f07b97220782dd2c66df0180f09ad4cdd8d8671b2a86218129e4c721c03dfa5e",
    "to": "did:cheqd:testnet:senderDID",
    "messageType": "https://schema.truvera.io/yes-no-response-V1.json",
    "sender": null,
    "receivedAt": "2025-09-04T12:25:29.331Z",
    "content": {
        "id": "3d287880-898a-11f0-8698-4d8b1173feb3",
        "type": "https://schema.truvera.io/yes-no-response-V1.json",
        "created_time": 1756988727,
        "from": "did:key:holderDID",
        "body": {
            "messageId": "internal-message-1234",
            "response": "no"
        },
        "to": [
            "did:cheqd:testnet:senderDID"
        ]
    }
}
```

#### **Response notification and retrieval**

Configure a webhook via the [API](/truvera-api/webhooks.md) or the Truvera [Workspace](/truvera-workspace/creating-api-keys-and-webhook-endpoints.md#h_fae99467a4) to listen to didcomm\_message\_received event.

You can also get all the received messages using the API.

## List DIDComm messages

> Retrieve a list of DIDComm messages for the authenticated user. \
> Messages are returned in reverse chronological order (newest first).<br>

```json
{"openapi":"3.0.2","info":{"title":"Truvera API","version":"1"},"tags":[{"name":"messaging","description":"Operations about DIDComm messaging"}],"servers":[{"url":"https://api-testnet.truvera.io","description":"Sandbox server (uses test data)"},{"url":"https://api.truvera.io","description":"Production server (uses live data)"}],"security":[{"bearerAuth":[]},{"accessToken":[]},{"nextAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"accessToken":{"type":"apiKey","in":"header","name":"DOCK-API-TOKEN"},"nextAuth":{"type":"apiKey","in":"header","name":"CERTS-TOKEN"}},"schemas":{"DID":{"description":"DID as fully qualified, typically. `did:cheqd:`","type":"string","minimum":32},"DIDCommMessage":{"description":"A DIDComm message with metadata","type":"object","properties":{"messageId":{"type":"string","description":"Unique identifier for the message"},"to":{"type":"string","description":"The DID that received this message"},"messageType":{"type":"string","description":"The type of the message (from the message content)"},"sender":{"type":"string","nullable":true,"description":"The DID of the message sender (if available)"},"receivedAt":{"type":"string","format":"date-time","description":"When the message was received"},"content":{"type":"object","description":"The message content (decrypted if encrypted, plaintext if not). Not included in message lists.","additionalProperties":true}},"required":["messageId","to","receivedAt"]},"Error":{"description":"An API Error","type":"object","properties":{"status":{"type":"integer"},"type":{"type":"string"},"message":{"type":"string"}}}}},"paths":{"/messaging/messages":{"get":{"summary":"List DIDComm messages","tags":["messaging"],"description":"Retrieve a list of DIDComm messages for the authenticated user. \nMessages are returned in reverse chronological order (newest first).\n","parameters":[{"name":"offset","in":"query","description":"How many items to offset by for pagination","required":false,"schema":{"type":"integer","format":"int32","default":0,"minimum":0}},{"name":"limit","in":"query","description":"How many items to return at one time (max 64)","required":false,"schema":{"type":"integer","format":"int32","default":64,"minimum":1,"maximum":64}},{"name":"to","in":"query","description":"Filter messages by specific DID","required":false,"schema":{"$ref":"#/components/schemas/DID"}},{"name":"messageType","in":"query","description":"Filter messages by type (e.g., 'https://didcomm.org/basicmessage/2.0/basic-message')","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"A paged array of DIDComm messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/DIDCommMessage"}},"total":{"type":"integer","description":"Total number of messages available"},"offset":{"type":"integer","description":"Current offset for pagination"},"limit":{"type":"integer","description":"Current limit for pagination"}}}}}},"400":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

To get the message content make an API call to retrieve the full response.

## Get a specific DIDComm message

> Retrieve a specific DIDComm message by its ID. \
> Only messages owned by the authenticated user can be retrieved.<br>

```json
{"openapi":"3.0.2","info":{"title":"Truvera API","version":"1"},"tags":[{"name":"messaging","description":"Operations about DIDComm messaging"}],"servers":[{"url":"https://api-testnet.truvera.io","description":"Sandbox server (uses test data)"},{"url":"https://api.truvera.io","description":"Production server (uses live data)"}],"security":[{"bearerAuth":[]},{"accessToken":[]},{"nextAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"accessToken":{"type":"apiKey","in":"header","name":"DOCK-API-TOKEN"},"nextAuth":{"type":"apiKey","in":"header","name":"CERTS-TOKEN"}},"schemas":{"DIDCommMessage":{"description":"A DIDComm message with metadata","type":"object","properties":{"messageId":{"type":"string","description":"Unique identifier for the message"},"to":{"type":"string","description":"The DID that received this message"},"messageType":{"type":"string","description":"The type of the message (from the message content)"},"sender":{"type":"string","nullable":true,"description":"The DID of the message sender (if available)"},"receivedAt":{"type":"string","format":"date-time","description":"When the message was received"},"content":{"type":"object","description":"The message content (decrypted if encrypted, plaintext if not). Not included in message lists.","additionalProperties":true}},"required":["messageId","to","receivedAt"]},"Error":{"description":"An API Error","type":"object","properties":{"status":{"type":"integer"},"type":{"type":"string"},"message":{"type":"string"}}}}},"paths":{"/messaging/messages/{messageId}":{"get":{"summary":"Get a specific DIDComm message","tags":["messaging"],"description":"Retrieve a specific DIDComm message by its ID. \nOnly messages owned by the authenticated user can be retrieved.\n","parameters":[{"name":"messageId","in":"path","required":true,"description":"The unique identifier of the message","schema":{"type":"string","pattern":"^[a-f0-9]{64}$"}}],"responses":{"200":{"description":"The requested DIDComm message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DIDCommMessage"}}}},"400":{"description":"Invalid message ID format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Message not found or not owned by user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.truvera.io/truvera-api/messaging.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
