Skip to content

feat(vs-agent): add the receipts module to the v2 didcomm api - #685

Merged
genaris merged 5 commits into
mainfrom
feat/659-receipts-module
Sep 12, 2026
Merged

feat(vs-agent): add the receipts module to the v2 didcomm api#685
genaris merged 5 commits into
mainfrom
feat/659-receipts-module

Conversation

@tarunvadde

Copy link
Copy Markdown
Contributor

Closes #659

  • POST /v2/didcomm/receipts sends receipts on a connection and returns the sent message id.
  • UNKNOWN_ID 404 for an unknown connection, and for a deployment that does not serve the module, which is only registered with the chat plugin.
  • The module API returns void, so the controller builds the message through DidCommReceiptsService, same path the module takes internally, just keeping the id.

Two calls worth a look:

  • It adds @2060.io/credo-ts-didcomm-receipts to apps/vs-agent, which had kept the chat packages behind optImport. VsAgentNestPlugin already takes a controllers array, so this could ship with the chat plugin instead and get the OPTIONAL 404 structurally. I followed the location the issue names.
  • listProtocols is left out, it does not exist yet. Raised as Protocol Discovery and Basic Messages are missing from the v2 DIDComm API #683.

// the module is OPTIONAL, and it is only registered when the chat plugin is enabled
let receiptsService: DidCommReceiptsService
try {
receiptsService = agent.context.dependencyManager.resolve(DidCommReceiptsService)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This try/catch does not add any value here. The dependency manager's resolve method returns undefined when the service cannot be resolved, so there is no need to catch an exception in this case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the catch a deployment with no chat plugin gets a 500 instead of the 404 the module is meant to answer. But isRegistered is on the same interface and reads better, so I switched to that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review the listProtocols requirement, as it has not been implemented yet and is explicitly required by the issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left out on purpose, it does not exist yet. #666 carries the same task for seven more modules, so building it here would mean a hardcoded list that #666 rewrites. Raised it as #683.


const connection = await agent.didcomm.connections.findById(body.connectionId)
if (!connection) {
throw new AdminApiError(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the existing unknownConnection function here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was module-private in V2DidcommConnectionsController, so I could not import it. Promoted it to common/AdminApiError.ts and both controllers use it now.

@genaris genaris left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to avoid the direct dependency on credo-ts-ext modules. I know that updating receipts module can take some more time, but I think that's the right way to do it. I have no problem if we use a patch in the meantime, but try to not add this redundant code on vs-agent.

'this deployment does not serve the receipts module',
)
}
const receiptsService = agent.context.dependencyManager.resolve(DidCommReceiptsService)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this reimplementing the whole DidCommReceiptsApi.send() just because is not returning the id?

I think the correct fix for this is open a PR on https://github.com/openwallet-foundation/credo-ts-didcomm-ext and, once implemented, just call agent.didcomm.receipts.send(). If VsAgent is properly typed, we won't need the direct dependency on @2060.io/credo-ts-didcomm-receipts.

public async sendReceipts(@Body() body: SendReceiptsBodyDto): Promise<SendReceiptsResponseDto> {
const agent = await this.vsAgentService.getAgent()

if (!agent.context.dependencyManager.isRegistered(DidCommReceiptsService)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do a direct check on the enabled chat modules rather than doing this indirectly (which also add a direct dependency on receipts credo-ts-ext module)

Signed-off-by: Tarun Vadde <vaddeofficial@gmail.com>
@tarunvadde
tarunvadde force-pushed the feat/659-receipts-module branch from 4a35b90 to e50a101 Compare September 9, 2026 18:19
Signed-off-by: Tarun Vadde <vaddeofficial@gmail.com>
@tarunvadde

Copy link
Copy Markdown
Contributor Author

I'd like to avoid the direct dependency on credo-ts-ext modules. I know that updating receipts module can take some more time, but I think that's the right way to do it. I have no problem if we use a patch in the meantime, but try to not add this redundant code on vs-agent.

Thanks and both fixed. The controller checks 'receipts' in agent.modules and calls agent.modules.receipts.send(), so nothing is reimplemented any more.

I raised openwallet-foundation/credo-ts-didcomm-ext#179 to make send() return the id, and patched it locally in the meantime as you suggested. MessageState is now declared in the DTO rather than imported, since [VSA-ADM-DC-RC] already fixes those five values, so apps/vs-agent has no direct dependency on the receipts module at all.

@tarunvadde
tarunvadde requested a review from genaris September 10, 2026 09:48

@genaris genaris left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the latest changes you've introduced, now the plugin stays an optionalDependency. The only plugin import is import type, and the endpoint degrades to a 404 when the module is absent.

One architectural note, to resolve in #692 rather than here: the receipts controller lives in the app, so the route is registered (and advertised in Swagger) even on deployments that do not serve the receipts module, and it needs the agent as unknown as VsAgent<ChatAgentModules> cast. In #692 we are converging on the pattern where plugin endpoints live in the plugin package and register through VsAgentNestPlugin.controllers (as the MRTD controller does there). Since #692 already touches this controller, let's move it into plugin-chat in that PR, together with the other chat-module controllers.

@tarunvadde

Copy link
Copy Markdown
Contributor Author

Thanks. Merged main in, the conflict was the lockfile plus a duplicated patchedDependencies block that dropped the credo patches from #690. The receipts controller moved into plugin-chat in #692 as you asked.

@genaris
genaris merged commit 7c10b11 into main Sep 12, 2026
8 checks passed
@genaris
genaris deleted the feat/659-receipts-module branch September 12, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the Receipts module to the v2 DIDComm API

3 participants