Agreements – Invitations & Acceptance¶
Agreement Invitations & Acceptance¶
What an Invitation Means¶
When an agreement is created, the backend records all participants (payers, providers, arbitrators) and marks them with agreement_status = pending.
An “invitation” is an off-chain notification mechanism only. It does not grant access, permissions, or state changes on-chain.
Invitations are identity-based (email + role) rather than wallet-based. They do not pre-bind, reserve, or prove any wallet address.
Invitations exist solely to:
- Inform participants that an agreement referencing their identity was created
- Share the agreementId (UUID) and blockchain metadata
- Prompt the participant to review and accept the agreement
After Receiving an Invitation¶
The participant typically receives an invitation payload containing the minimal identifiers required to locate the agreement off-chain and on-chain.
Example invitation payload¶
{
"agreementId": "d9b6da1a-432c-47d8-8b0d-9ac01aa4f1ce",
"network": "polygon",
"contractAddress": "0xabc123...789",
"role": "provider",
"email": "studio@nova.io"
}
This payload is informational. It does not include or reserve any wallet address.
How Participants Discover an Agreement¶
Participants discover an agreement through the invitation payload and related off-chain metadata shared by the integrator (for example: the agreementId plus the chain/network data needed to locate the contract).
There is no on-chain address → agreement index in the DShare contract. A participant must already know the agreement identifiers (from the invitation or an external indexer) before interacting with the contract.
In practice, participants use the invitation’s agreementId to retrieve agreement metadata and the participant list.
Retrieve Agreement Data (Required)¶
After receiving an invitation, the required discovery step is:
GET /api/v1/agreements/:agreementId
This endpoint provides the off-chain agreement snapshot needed to:
- View the agreement participants and their emails
- Identify the participant’s partnerId (participants[].id) required for on-chain acceptance
- Confirm the expected role and the current participant agreement_status
This REST call is a discovery and synchronization utility. The escrow protocol itself does not require this API call. It is required only if the participant does not already know their partnerId through another trusted channel.
How Invitations Are Delivered¶
Invitations are typically delivered via email or external messaging and include:
- agreementId (UUID)
- Network and RPC information
- DShare contract address
- The participant’s expected role (payer, provider, arbitrator)
The delivery channel is outside the scope of the protocol.
Acceptance Is an On-Chain Action¶
Accepting an agreement is not an API call. Acceptance happens only when the participant’s wallet submits:
acceptAgreement(agreementId, partnerId)
on the DShare smart contract.
In the contract, both agreementId and partnerId are bytes32-encoded UUIDs.
Minimal pseudo-call example¶
acceptAgreement(bytes32(agreementIdUuid), bytes32(partnerIdUuid))
Wallet address binding happens at acceptance¶
At agreement creation and invitation time, partner wallet addresses are not known on-chain. The contract binds the wallet address only when the participant accepts: the first successful acceptAgreement call records msg.sender as the wallet address for that partnerId. This binding is permanent and cannot be changed afterward.
The contract verifies:
- The agreement is still in Pending state
- The participant has not already accepted
- The referenced partnerId exists in the agreement
- The partner wallet address has not been set yet for that partnerId
If valid, the contract:
- Marks the participant as accepted
- Emits PartnerAccepted
- Updates internal acceptance counters
How to Determine Your partnerId¶
The partnerId is the participant UUID that identifies the partner entry in the agreement. It is mandatory for acceptAgreement.
To determine it, fetch the agreement details and match the invitation email to the returned participants list.
Example: participant list excerpt¶
{
"participants": [
{
"id": "17ce72bf-9b1a-4b70-8618-2afcce9f5f80",
"participantType": "provider",
"email": "studio@nova.io",
"agreement_status": "pending"
},
{
"id": "e0d1b4c5-0b80-4c08-8a58-90a8e4481f10",
"participantType": "payer",
"email": "payer@acme.com",
"agreement_status": "pending"
}
]
}
In this example, the invitation email studio@nova.io maps to partnerId = 17ce72bf-9b1a-4b70-8618-2afcce9f5f80.
Why Acceptance Requires Wallet Interaction¶
Acceptance is an on-chain state change. Because the contract authorizes actions using msg.sender, a participant must submit a blockchain transaction from the wallet address that will be associated to their role on-chain.
No Authentication Requirement¶
Participants do NOT need to authenticate with PAYCIFI to accept an agreement.
Any wallet can accept directly on-chain as long as:
- It controls the address that will be recorded for that participant
- It knows the agreementId and the participant partnerId
This allows external or unauthenticated wallets to fully participate after receiving an invitation.
Optional Off-Chain Synchronization¶
After a participant accepts on-chain, integrators may optionally call PATCH /agreements/participants/:participantId/status to synchronize off-chain participant status for dashboards or operational workflows. This REST update is not required for on-chain acceptance and does not grant or revoke any on-chain permissions.
Declining an invitation (or setting agreement_status = declined off-chain) does not perform any on-chain action. The DShare contract does not implement an on-chain “decline invitation” operation. On-chain "declined" outcomes exist only during service validation via validateService.
Trust Model¶
The PAYCIFI backend is not an authority for acceptance. The DShare smart contract is the single source of truth for acceptance state, role permissions, and lifecycle transitions; use events such as PartnerAccepted and AgreementApproved as the authoritative feed.
Event-based confirmation¶
Use on-chain events as the authoritative confirmation of acceptance and approval:
- PartnerAccepted confirms a successful acceptance and wallet binding for the given partnerId.
- AgreementApproved confirms the agreement has reached the approved state on-chain.
REST updates are optional synchronization helpers only.
Minimal End-to-End Flow¶
Invitation received
↓
GET /api/v1/agreements/:agreementId
↓
Resolve partnerId by matching invitation email to participants[].email
↓
acceptAgreement(agreementId, partnerId) (on-chain)
↓
PartnerAccepted event
↓
AgreementApproved event (once all required participants accepted and conditions met)
When Acceptance Completes¶
When all required participants have accepted:
- If funds are already locked, the agreement becomes Approved
- Otherwise, it waits for the funder to lock funds
This transition is emitted on-chain as AgreementApproved.
Transition to Funding & Escrow Lifecycle¶
Once an agreement is created and participants have accepted, funding and all subsequent lifecycle actions (funding, validation, arbitration, unlock, payouts) are enforced by the DShare smart contract and tracked via on-chain events.