Basic Connection Flow
DIDComm connections are established through an invitation-based flow where one agent creates an invitation and another agent accepts it.import { Agent } from '@credo-ts/core'
import {
DidCommModule,
DidCommHttpOutboundTransport,
} from '@credo-ts/didcomm'
import { AskarModule } from '@credo-ts/askar'
import { agentDependencies, DidCommHttpInboundTransport } from '@credo-ts/node'
import { askar } from '@openwallet-foundation/askar-nodejs'
// Agent 1 (Inviter)
const faberAgent = new Agent({
config: {
label: 'Faber College',
walletConfig: {
id: 'faber-wallet',
key: 'faber-wallet-key',
},
},
dependencies: agentDependencies,
modules: {
didcomm: new DidCommModule({
endpoints: ['http://localhost:3001'],
transports: {
inbound: [new DidCommHttpInboundTransport({ port: 3001 })],
outbound: [new DidCommHttpOutboundTransport()],
},
connections: {
autoAcceptConnections: true,
},
}),
askar: new AskarModule({ askar }),
},
})
// Agent 2 (Invitee)
const aliceAgent = new Agent({
config: {
label: 'Alice',
walletConfig: {
id: 'alice-wallet',
key: 'alice-wallet-key',
},
},
dependencies: agentDependencies,
modules: {
didcomm: new DidCommModule({
endpoints: ['http://localhost:3002'],
transports: {
inbound: [new DidCommHttpInboundTransport({ port: 3002 })],
outbound: [new DidCommHttpOutboundTransport()],
},
connections: {
autoAcceptConnections: true,
},
}),
askar: new AskarModule({ askar }),
},
})
await faberAgent.initialize()
await aliceAgent.initialize()
import { DidCommHandshakeProtocol } from '@credo-ts/didcomm'
const outOfBandRecord = await faberAgent.didcomm.oob.createInvitation({
handshakeProtocols: [DidCommHandshakeProtocol.Connections],
label: 'Faber College Invitation',
multiUseInvitation: false,
})
const invitation = outOfBandRecord.outOfBandInvitation
const invitationUrl = invitation.toUrl({ domain: 'https://example.com' })
console.log('Invitation URL:', invitationUrl)
console.log('Share this URL with Alice via QR code or link')
const { connectionRecord } = await aliceAgent.didcomm.oob.receiveInvitationFromUrl(
invitationUrl,
{
label: 'Alice',
}
)
if (!connectionRecord) {
throw new Error('No connection record created from invitation')
}
console.log('Connection initiated:', connectionRecord.id)
import { DidCommDidExchangeState } from '@credo-ts/didcomm'
// Wait on Alice's side
const aliceConnection = await aliceAgent.didcomm.connections.returnWhenIsConnected(
connectionRecord.id
)
console.log('Alice connection state:', aliceConnection.state)
// Output: 'completed'
// Get connection on Faber's side
const [faberConnection] = await faberAgent.didcomm.connections.findAllByOutOfBandId(
outOfBandRecord.id
)
const completedFaberConnection = await faberAgent.didcomm.connections.returnWhenIsConnected(
faberConnection.id
)
console.log('Faber connection state:', completedFaberConnection.state)
// Output: 'completed'
const ping = await aliceAgent.didcomm.connections.sendPing(
aliceConnection.id,
{}
)
console.log('Trust ping sent:', ping.threadId)
// Listen for ping response
import { DidCommConnectionEventTypes } from '@credo-ts/didcomm'
aliceAgent.events.on(
DidCommConnectionEventTypes.DidCommTrustPingResponseReceivedEvent,
(event) => {
if (event.payload.threadId === ping.threadId) {
console.log('✓ Connection verified with trust ping')
}
}
)
Multi-Use Invitations
Create invitations that can be used by multiple agents:Connection Events
Monitor connection state changes:Connection with Mediator
Establish connections through a mediator for mobile/edge devices:// Alice connects to a mediator first
const mediatorInvitationUrl = 'https://mediator.example.com/invitation'
const { connectionRecord: mediatorConnection } =
await aliceAgent.didcomm.oob.receiveInvitationFromUrl(mediatorInvitationUrl)
if (!mediatorConnection) {
throw new Error('Failed to create mediator connection')
}
await aliceAgent.didcomm.connections.returnWhenIsConnected(mediatorConnection.id)
const mediationRecord = await aliceAgent.didcomm.mediationRecipient.requestMediation(
mediatorConnection.id
)
console.log('Mediation granted:', mediationRecord.state)
// Set as default mediator
await aliceAgent.didcomm.mediationRecipient.setDefaultMediator(mediationRecord)
// Alice creates an invitation (will automatically use mediator)
const aliceInvitation = await aliceAgent.didcomm.oob.createInvitation()
const aliceInvitationUrl = aliceInvitation.outOfBandInvitation.toUrl({
domain: 'https://alice.example.com',
})
// The invitation includes the mediator's routing information
console.log('Mediated invitation:', aliceInvitationUrl)
Connectionless Exchange
For one-time interactions without establishing a persistent connection:Manual Connection Flow
For scenarios requiring manual intervention:Best Practices
- Use
multiUseInvitation: falsefor one-to-one connections - Set
autoAcceptConnections: truefor better user experience in trusted scenarios - Always wait for connection state to be ‘completed’ before exchanging messages
- Use mediators for mobile agents to handle offline message delivery
- Implement proper error handling for network failures during connection establishment
- Store connection records for future interactions
Next Steps
- Learn about Issuing Credentials
- Explore Verifying Presentations
- Set up Multi-tenant Agents