Step 2: Create a Contract
After obtaining the access token, use it to create a payment contract.
Endpoint
POST {{BaseUrl}}/apps/api/contracts
Headers
-
Authorization: Bearer
{access_token}
Replace{access_token}with the token obtained from login (Step 1). -
Content-Type: application/json
Request Body
{
"title": "Sell a property",
"contractServiceType": "product",
"BuyerParty": {
"userRefId" : "USR_abc001",
"phoneNumber": "966583944460",
"firstName": "Kareem",
"lastName": "Hasan",
"identity": "1071855555"
},
"SellerParty": {
"userRefId" : "USR_abc002",
"phoneNumber": "966583944461",
"firstName": "Ashraf",
"lastName": "Ali",
"identity": "1071866666"
},
"amount": 1000,
"description": "External Iphone mobile",
"notes": "notes for mobile",
"reference": "12321",
"buyerFeePercentage": 5,
"sellerFeePercentage": 3,
"metaData": {
"metadata1": "1",
"metadata2": "2",
"metadata3": "",
"metadata4": ""
},
"callbackUrl": "https://yourdomain.com/usercart",
"Milestones": [
{
"Name": "milestone 1 name",
"Description": "milestone 1 description",
"Amount": 600,
"DueDate": "2026-12-20T10:00:00.000Z"
},
{
"Name": "milestone 2 name",
"Description": "milestone 2 description",
"Amount": 400,
"DueDate": "2026-12-31T09:35:30.369Z"
}
]
}
Field Descriptions
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| title | string | Contract title | Required Example: "Sell an iPhone" |
| contractServiceType | string | Type of service for the contract | Required Expected Values: "Product", "Service" |
| BuyerParty | object (Party) | Buyer information | Required |
| SellerParty | object (Party) | Seller information | Required |
| amount | number | Contract amount | Required Example: 1000 |
| description | string | Contract description | Optional Example: "External iPhone mobile" |
| notes | string | Additional notes | Optional Example: "notes for mobile" |
| reference | string | External reference identifier | Optional Example: "12321" |
| metaData | object (MetaData) | Custom key-value metadata | Optional |
| callbackUrl | string | Callback URL for contract events | Optional Example: "https://yourdomain.com/usercart" |
| Milestones | list of objects (Milestone) | Separate contract into multiple milestones | Optional |
| buyerFeePercentage | number | Your fee charged to the buyer at checkout | Optional Range: 0–100 Requires contract-level fee Learn more |
| sellerFeePercentage | number | Your fee deducted from the seller at release | Optional Range: 0–100 Requires contract-level fee Learn more |
| noFeesApplied | boolean | Waives your platform fee on this contract. Your agreement with WePay applies as usual | Optional Default: false Requires contract-level fee Learn more |
Contract Fees
By default, the fee agreed with WePay for your account applies to every contract you create; it is charged to the buyer.
If contract-level fee is enabled for your account, you can set the fee on each contract using buyerFeePercentage, sellerFeePercentage or noFeesApplied.
In contract-level mode, every contract must include a fee field; otherwise the request returns 400 BuyerOrSellerFeeRequired.
Note: Sending any of these fields while contract-level fee is not enabled returns 403 with error code 1887.
See Contract Fees for the full guide, worked examples, and validation rules.
Party (BuyerParty / SellerParty)
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| userRefId* | string | User identifier in wepay system | Optional |
| phoneNumber | string | Party phone number (including country code) | Required if userRefId is not provided Example: "966583944460" |
| firstName** | string | First name | Required if userRefId is not provided Example: "Kareem" |
| lastName** | string | Last name | Required if userRefId is not provided Example: "Hasan" |
| identity** | string | Identity number | Optional, even if userRefId is not provided Example: "1071855555" |
*userRefId is optional in the request. It is a unique identifier across all parties in WePay system.
**firstName, lastName, and identity are sent to pre-fill the KYC form Personal info.
To get userRefId, you can call the User Onboarding API to retrieve a user and obtain his userRefId, if exists.
You can also choose to not provide userRefId in the request, or just provide the party's phone number and name and WePay will try to find an existing user with the same phone number. If the user is a new one, it will be created automatically in the system and a new userRefId will be generated for him, and will be returned in the response. You can then store this userRefId in your system for future reference.
If you want to create a user in Wepay and get the userRefId before creating a contract, you can use the User Creation API to create a new user and get his userRefId, then use this userRefId in the contract creation request.
Note: On contract creation, the seller receives an SMS with a KYC link to verify his identity.
MetaData
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| metadata1 | string | Custom metadata field | OptionalExample: "1" |
| metadata2 | string | Custom metadata field | OptionalExample: "2" |
| metadata3 | string | Custom metadata field | OptionalExample: "" |
| metadata4 | string | Custom metadata field | OptionalExample: "" |
Milestone
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| Name | string | Milestone name | Required Example: "milestone 1 name" |
| Description | string | Milestone Description | Optional Example: "milestone 1 description" |
| Amount | Number | Milestone amount | Required Example: 600 Note: Must be lower than contract amount and the total of all milestones amounts must be equal to the contract amount |
| DueDate | string (date-time) | Milestone due date (UTC) | Required Example: 2026-12-20T10:00:000Z |
Response:
Result<ExternalCreateContractResponse>
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| status | int | Indicates whether the request was successful | Required Example: 200 |
| message | string | Result message | Optional Example: "Contract created successfully" |
| validationErrors | array of string | List of validation or business errors | Optional |
| data | object (ExternalCreateContractResponse) | Contract creation result data | Optional |
ExternalCreateContractResponse
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| contractId | string | Unique external contract identifier | Required Example: "CNT-2601-00100003" |
| status | string (ContractStatus) | Current contract status | Required Example: "pending" |
| contractServiceType | string (ContractServiceType) | Type of contract service | Required Example: "product" |
| checkoutUrl | string | Checkout URL for completing payment | Required Example: "/checkout?token=encodedToken" The token is valid for 10 minutes. If expired, you need to request a new one by calling /apps/api/contracts/checkout |
| buyerParty | object (ExternalContractParty) | Buyer party details | Required |
| sellerParty | object (ExternalContractParty) | Seller party details | Required |
| reference | string | External reference identifier | Optional Nullable |
| metaData | object (ContractMetaData) | Custom metadata | Optional Nullable |
| milestones | array of Milestone | Contract milestones | Required |
| pricingLineItems | array of ExternalPriceLineItem | Pricing breakdown items | Required |
| createdDate | string (date-time) | Contract creation timestamp (UTC) | Required Example: 2024-01-15T10:30:00Z |
ExternalContractParty
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| userRefId | string | User identifier in wepay | Required |
| firstName | string | First name | Required Example: "Kareem" |
| lastName | string | Last name | Required Example: "Hasan" |
| phoneNumber | string | Phone number including country code | Required Example: "966583944460" |
ContractMetaData
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| metadata1 | string | Custom metadata field | Optional |
| metadata2 | string | Custom metadata field | Optional |
| metadata3 | string | Custom metadata field | Optional |
| metadata4 | string | Custom metadata field | Optional |
Milestone
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| Id | number | Milestone identifier | Required Example: 13 |
| name | string | Milestone name | Required Example: "Delivery" |
| description | string | Milestone description | Optional |
| amount | number | Milestone amount | Required Example: 500 |
| dueDate | string (date-time) | Milestone due date (UTC) | Required Example: 2024-02-01T00:00:00Z |
| pricingLineItems | array of ExternalPriceLineItem | Pricing breakdown items | Required |
Milestone Validation Rules
| Rule | Requirement |
|---|---|
| Total amounts | Must equal contract amount |
| Milestone amount | Must be > 0 |
| DueDate format | ISO 8601 UTC (with Z suffix) |
| Name | Required field |
| Description | Optional field |
Important: Milestone Amount Calculation
Request Amount = Base contract amount
Response Amount = Base + all fees + taxes
Example:
- Request: 600 SAR (base)
- Response: 669.00 SAR (contract amount plus the fees and VAT paid by the buyer)
See pricingLineItems array for full breakdown.
ExternalPriceLineItem
| Field Name | Type | Description | Required / Notes / Example |
|---|---|---|---|
| lineType | string (PricingLineType) | Pricing line type | Required Example: "EscrowFee" |
| LineTypeDescription | string | Pricing line type description | Required |
| role | string (PricingRole) | Party responsible for the amount | Required Example: "Buyer" |
| RoleDescription | string | Role description | Required |
| amount | number | Amount value | Required Example: 50 |
| description | string | Pricing line description | Optional |
| order | integer | Display order | Required Example: 1 |
Example Request (cURL)
curl -X POST "{{BaseUrl}}/apps/api/contracts" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "TEST Sell a property",
"contractServiceType": "Product",
"BuyerParty": {
"phoneNumber": "966583944460",
"firstName": "Kareem",
"lastName": "Hasan"
},
"SellerParty": {
"phoneNumber": "966583944461",
"firstName": "Ashraf",
"lastName": "Ali"
},
"amount": 1000,
"description": "TEST External Iphone mobile",
"notes": "notes for mobile",
"callbackUrl": "http://localhost:3000/en/payment-success",
"Milestones": [
{
"Name": "milestone 1 name",
"Description": "milestone 1 description",
"Amount": 600,
"DueDate": "2026-12-20T10:00:00.000Z"
},
{
"Name": "milestone 2 name",
"Description": "milestone 2 description",
"Amount": 400,
"DueDate": "2026-12-31T09:35:30.369Z"
}
]
}'
Note: API uses camelCase for JSON responses:
- Request:
"Milestones"(PascalCase - C# convention) - Response:
"milestones"(camelCase - JSON convention)
DueDate Format: Always use UTC timezone
- Example:
"2026-12-20T10:00:00.000Z" - System converts to Saudi time (UTC+3) for display
Note: Milestones are read-only after contract creation.
Example Response
{
"data": {
"contractId": "CNT-2601-00100003",
"status": "pending",
"contractServiceType": "product",
"checkoutUrl": "{{BaseUrl}}/checkout?token=RSGFF38PXVO....",
"buyerParty": {
"userRefId": "USR_20260405152053_5487",
"firstName": "Kareem",
"lastName": "Hasan",
"phoneNumber": "966583944460"
},
"sellerParty": {
"userRefId": "USR_20260405151029_7034",
"firstName": "Ashraf",
"lastName": "Ali",
"phoneNumber": "966583944461"
},
"reference": "12321",
"metaData": {
"metadata1": "1",
"metadata2": "2",
"metadata3": "",
"metadata4": ""
},
"milestones": [
{
"id": 12,
"name": "milestone 1 name",
"description": "milestone 1 description",
"dueDate": "2026-12-20T10:00:00.000Z",
"amount": 669.00,
"pricingLineItems": [
{
"lineType": "contractAmount",
"role": "buyer",
"amount": 600,
"description": null,
"order": 1
},
{
"lineType": "escrowFee",
"role": "thirdParty",
"amount": 15.0,
"description": null,
"order": 2
},
{
"lineType": "escrowFeeTax",
"role": "thirdParty",
"amount": 2.25,
"description": null,
"order": 3
},
{
"lineType": "externalPlatformFee",
"role": "buyer",
"amount": 60.00,
"description": null,
"order": 4
},
{
"lineType": "externalPlatformFeeTax",
"role": "buyer",
"amount": 9.00,
"description": null,
"order": 5
},
{
"lineType": "netEscrowAmountToSeller",
"role": "seller",
"amount": 600,
"description": null,
"order": 6
}
]
},
{
"id": 13,
"name": "milestone 2 name",
"description": "milestone 2 description",
"dueDate": "2026-12-31T09:35:30.369Z",
"amount": 457.5,
"pricingLineItems": [
{
"lineType": "contractAmount",
"role": "buyer",
"amount": 400,
"description": null,
"order": 1
},
{
"lineType": "escrowFee",
"role": "thirdParty",
"amount": 10.00,
"description": null,
"order": 2
},
{
"lineType": "escrowFeeTax",
"role": "thirdParty",
"amount": 1.50,
"description": null,
"order": 3
},
{
"lineType": "externalPlatformFee",
"role": "buyer",
"amount": 40.00,
"description": null,
"order": 4
},
{
"lineType": "externalPlatformFeeTax",
"role": "buyer",
"amount": 6.00,
"description": null,
"order": 5
},
{
"lineType": "netEscrowAmountToSeller",
"role": "seller",
"amount": 400,
"description": null,
"order": 6
}
]
}
],
"pricingLineItems": [
{
"lineType": "contractAmount",
"role": "buyer",
"amount": 1000,
"description": null,
"order": 1
},
{
"lineType": "escrowFee",
"role": "thirdParty",
"amount": 25.0,
"description": null,
"order": 2
},
{
"lineType": "escrowFeeTax",
"role": "thirdParty",
"amount": 3.75,
"description": null,
"order": 3
},
{
"lineType": "externalPlatformFee",
"role": "buyer",
"amount": 100.0,
"description": null,
"order": 4
},
{
"lineType": "externalPlatformFeeTax",
"role": "buyer",
"amount": 15.0,
"description": null,
"order": 5
},
{
"lineType": "netEscrowAmountToSeller",
"role": "seller",
"amount": 1000,
"description": null,
"order": 6
}
],
"createdDate": "2026-01-25T10:28:10.4874015Z"
},
"message": "Contract created successfully.",
"status": 200,
"validationErrors": []
}
Example Response with Buyer and Seller Fees
When contract-level fee is enabled and you charge both parties, the pricing contains two externalPlatformFee lines — one for each party — each followed by its own tax line.
Request:
{
"title": "Sell a property",
"contractServiceType": "product",
"amount": 1000,
"buyerFeePercentage": 5,
"sellerFeePercentage": 3,
"BuyerParty": { "phoneNumber": "966583944460", "firstName": "Kareem", "lastName": "Hasan" },
"SellerParty": { "phoneNumber": "966583944461", "firstName": "Ashraf", "lastName": "Ali" }
}
Resulting pricingLineItems:
"pricingLineItems": [
{
"lineType": "contractAmount",
"role": "buyer",
"amount": 1000,
"order": 1
},
{
"lineType": "externalPlatformFee",
"role": "buyer",
"amount": 50.00,
"order": 4
},
{
"lineType": "externalPlatformFeeTax",
"role": "buyer",
"amount": 7.50,
"order": 5
},
{
"lineType": "externalPlatformFee",
"role": "seller",
"amount": 30.00,
"order": 6
},
{
"lineType": "externalPlatformFeeTax",
"role": "seller",
"amount": 4.50,
"order": 7
},
{
"lineType": "netEscrowAmountToSeller",
"role": "seller",
"amount": 965.50,
"order": 8
}
]
The buyer pays 1,057.50 at checkout. The seller receives 965.50 when the contract is released, because the seller fee is deducted at release rather than at payment.
Party name and identity sent at creation pre-fill the KYC form.
See Contract Fees for the complete guide.