Agreements – Deal Creation¶
When should I use this?¶
- After the user has authenticated with PAYCIFI, completed wallet linking, and finished profile completion, when a payer is ready to formalize scope, participants, fees, and deadlines for a programmable escrow.
- Before running any funding challenges or participant acceptance workflows, because those steps depend on the agreement ID generated here. Wallets that are not authenticated through PAYCIFI cannot call
POST /agreements, but they can still participate later on-chain once they are invited and accept via the supported flows.
Purpose¶
Create a programmable deal that binds the payer, service providers, and optional arbitrator around defined tasks, amounts, and deadlines. The backend stores the agreement, provisions invitations, and mirrors the payload on-chain; from that point, acceptance, validation, funding, arbitration, and payouts are enforced directly by the DShare smart contract while the backend supplies orchestration utilities and status reads.
Preconditions¶
- Authentication: Caller MUST include a valid
Authorization: Bearer <accessToken>header (obtained after login). Only PAYCIFI-issued tokens are accepted; unauthenticated external wallets—even if they control valid Ethereum or Circle addresses—cannot invoke this endpoint. - Account state: The creator’s profile MUST be complete (
POST /users/registerdone) and wallet setup finished so payer funding can occur later. - Participants & creator role: Payload MUST include at least one
payerand oneprovider. Exactly one participant MUST haveowner: true, andagreementInfo.ownerUserIdMUST match that participant’suserIdand the authenticated user. The owner can be either the payer or a provider—the backend only checks theownerflag +ownerUserIdpair when computing the on-chain creator ID. - Arbitrator (optional): Arbitration remains free-form: send
name,email,walletAddress, and optionallyphone/additionalInfo. If the email already exists on a non-arbitrator user, the backend aborts creation withagreement-creation-service-error; otherwise it either reuses or auto-registers the arbitrator and sends an invitation.
Client Responsibilities¶
- MUST provide normalized lowercase emails for all participants and arbitrator data.
- MUST ensure
agreementInfo.ownerUserIdequals both the authenticated user ID and the participant flagged withowner: true. - MUST supply
acceptanceDeadlineandcompletionDeadlineas UNIX timestamps expressed in seconds. - MUST choose a supported currency code defined by PAYCIFI (
agreementInfo.currency). - SHOULD ensure every
agreementItemsentry references an existing participant ID and includes the required pricing fields.
API Flow¶
- Client → POST
/agreementswith{ participants, agreementInfo, agreementItems, arbitrationSettings }. - Validation: Backend normalizes emails, checks required arrays, validates currency and deadlines, and calculates platform fees via
CalculFees. - Persistence:
AgreementsService.createAgreementinserts the agreement with statuspending, stores participants (payer/provider/arbitrator) withagreement_status = pending, and creates items. Invitations are queued for anyone withoutuserId. - Blockchain provisioning: Controller calls
BlockchainService.createAgreement, mirroring totals, fee strategy, partners, and deadlines on-chain. Returning data (contract address, token address, RPC URL, block number) is written back to the agreement record. - Notifications: Email invitations go to every pending participant plus optional arbitrator, and a confirmation email goes to the creator.
- Response: API returns
success: true, database snapshot (agreementInfo,participants,agreementItems), computed totals,agreementId, andblockchainAgreementmetadata. Every participant remains inpendingstate until they accept and the payer funds the agreement.
Creator role & ownership¶
- The controller identifies the creator by locating the participant flagged with
owner: true. No participant-type restriction exists: either the payer or a provider can be the owner. agreementInfo.ownerUserIdMUST match that participant’suserId. Mismatches are not auto-corrected and will cause downstream blockchain calls to fail, so the client MUST enforce this alignment before calling the API.
Arbitrator handling¶
- Arbitrators are optional but MUST already exist as arbitrators in PAYCIFI. Select them using the arbitrator discovery endpoints (
GET /arbitrators?isCurated=...orGET /arbitrators/availability). - During creation, the backend validates that the provided email maps to an active arbitrator. If the email exists but is not flagged as arbitrator,
agreement-creation-service-erroris returned and the transaction is rolled back. - Integrators SHOULD never assume arbitrary emails can be promoted to arbitrator status via this endpoint; pre-select and validate the arbitrator before submitting the agreement payload.
Arbitrator Discovery¶
Use the dedicated arbitrator endpoint to fetch curated (PAYCIFI-approved) or non-curated arbitrators before calling POST /agreements.
- Endpoint:
GET /arbitrators?isCurated=true|false - Auth: Requires the standard
Authorization: Bearer <accessToken>header. - Query param:
isCuratedis mandatory. Set it totrueto list curated arbitrators that PAYCIFI already validated, orfalseto list all non-curated arbitrators in your workspace. - Response: Returns sanitized arbitrator objects
{ id, name, email, isAvailable }that you can use to pre-fillarbitrationSettings. - Availability check: To confirm whether a known arbitrator email is currently available (without listing), call
GET /arbitrators/availability?email=<email>; it returns{ success, available, arbitrator }.
Example request¶
GET /arbitrators?isCurated=true
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example response¶
[
{
"id": "23c44f2e-865e-4c2f-8f85-2d3c5d2a9e11",
"name": "DShare Arbitration Desk",
"email": "panel@paycifi.com",
"isAvailable": true
},
{
"id": "6a89e5fd-9342-4dbe-8b39-2064c3914ea4",
"name": "Global Escrow Partners",
"email": "team@global-escrow.io",
"isAvailable": false
}
]
If no arbitrator matches the requested curation level or availability filters, the endpoint returns an empty array. When your integration relies on a pre-agreed arbitrator outside of this list, you must still provide their name, email, and walletAddress in arbitrationSettings; however, prefer curated IDs whenever possible to avoid invitation rework.
Deadline handling¶
- Deadlines are required and must be provided by the client; the backend performs strict validation.
- Inputs must be UNIX timestamps expressed in seconds (milliseconds are normalized by dividing by 1000).
- Validation rules:
acceptanceDeadlinemust be greater than the current block timestamp.completionDeadlinemust be strictly greater thanacceptanceDeadline.completionDeadlinecannot exceed the current timestamp by more than ~100 years.
On-chain execution¶
Funding, validation, arbitration, and payouts are executed directly on the DShare smart contract. PAYCIFI does not proxy or abstract these on-chain transactions for externally owned wallets; integrators perform blockchain interactions with their own tooling once the agreement has been created and mirrored on-chain.
Example Request¶
POST /agreements
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"participants": [
{
"id": "payer-temp",
"userId": "3f6f6b4f-9c7d-4b9b-9f5a-43b0d4c2f101",
"participantType": "payer",
"name": "Acme Corp",
"email": "payer@acme.com",
"owner": true,
"additionalInfo": "Main funding entity"
},
{
"id": "provider-temp",
"userId": "92a4b2b6-45f5-4501-8e71-0d371cbb9011",
"participantType": "provider",
"name": "Studio Nova",
"email": "studio@nova.io",
"additionalInfo": "Creative agency"
}
],
"agreementInfo": {
"title": "Website Localization Sprint",
"description": "Localize the main marketing site in FR/DE",
"currency": "USDC",
"totalAmount": 1800,
"feePayerStrategy": "payer",
"acceptanceDeadline": 1735603200,
"completionDeadline": 1738281600,
"ownerUserId": "3f6f6b4f-9c7d-4b9b-9f5a-43b0d4c2f101",
"network": "base"
},
"agreementItems": [
{
"id": "item-1",
"participantId": "provider-temp",
"description": "Landing page localization",
"quantity": 1,
"unitPrice": 900,
"fees": 45
},
{
"id": "item-2",
"participantId": "provider-temp",
"description": "Product tour localization",
"quantity": 1,
"unitPrice": 900,
"fees": 45
}
],
"arbitrationSettings": {
"name": "DShare Panel",
"email": "arbitration@paycifi.com",
"phone": "+33180000000",
"additionalInfo": "Escrow arbitration panel",
"walletAddress": "0x0000000000000000000000000000000000000000"
}
}
Example Response¶
{
"success": true,
"agreementId": "a55f1c3c-7de1-4ed3-8c90-9cf52a4b1d1a",
"agreementInfo": {
"id": "a55f1c3c-7de1-4ed3-8c90-9cf52a4b1d1a",
"title": "Website Localization Sprint",
"status": "pending",
"currency": "USDC",
"acceptanceDeadline": "2024-12-31T00:00:00.000Z",
"completionDeadline": "2025-01-31T00:00:00.000Z",
"payerTotalAmountWithFees": 1889.1
},
"participants": [
{
"id": "e0d1b4c5-0b80-4c08-8a58-90a8e4481f10",
"participantType": "payer",
"owner": true,
"agreementStatus": "pending"
},
{
"id": "17ce72bf-9b1a-4b70-8618-2afcce9f5f80",
"participantType": "provider",
"agreementStatus": "pending"
},
{
"id": "db5a9fef-29ce-4cde-bc41-1881c5f5b24c",
"participantType": "arbitrator",
"agreementStatus": "pending"
}
],
"agreementItems": [
{
"id": "4e6f4d8f-5e6c-45bf-b4da-4208b0214a05",
"participantId": "17ce72bf-9b1a-4b70-8618-2afcce9f5f80",
"description": "Landing page localization",
"providerItemStatus": "pending",
"payerItemStatus": "pending"
}
],
"blockchainAgreement": {
"agreementId": "0x613535f163332d6465312d34...",
"contractAddress": "0xabc123...",
"tokenAddress": "0xdef456...",
"rpcUrl": "https://base-mainnet.g.alchemy.com/v2/...",
"acceptanceDeadline": 1735603200,
"completionDeadline": 1738281600
}
}
Response Handling¶
- Success: Persist
agreementId,participants, andblockchainAgreement. Use participant IDs to drive acceptance (PATCH/agreements/participants/:participantId/status) and funding flows (/agreements/circle/fund/init). - Validation errors (400): Inspect
errorCode/missingDatato fix payload issues (e.g.,missing-data,missing-acceptance-deadline,bad-completion-deadline). Client SHOULD block the user from proceeding until the payload is corrected. - Role or currency errors (400):
agreementInfo.currency - unknown currencyindicates the code is not supported.deadline-too-farindicates completion is beyond the allowed horizon. - Server errors (500):
fees-calculation-error,agreement-creation-error,agreement-creation-service-error, oragreement-creation-controller-errorrequire retry after verifying payload and backend health. IffromErroris returned (non-production), log it for troubleshooting. - Funding errors (delegated path):
forbidden-wallet-type,delegate-wallet-not-found,missing-required-fields, orinvalid-bytes32-argscome directly from the Circle funding endpoints when prerequisites are missing. Surface these to the payer and block funding until their session uses a delegate wallet and provides the required IDs.
Error Codes¶
missing-datafees-calculation-errormissing-currencymissing-acceptance-deadlinemissing-completion-deadlinebad-acceptance-deadlinebad-completion-deadlinedeadline-too-faragreement-creation-erroragreement-creation-service-erroragreement-creation-controller-error
Related Endpoints¶
POST /agreements– create the agreement and deploy it on-chain.PATCH /agreements/participants/:participantId/status– collect accept/decline responses from each participant.POST /agreements/:agreementId/recalculate-status– recompute the overall state after participant updates (optional helper).