PayScrow MarketPlace API Specification
1. Overview
The MarketPlace Transaction Start API enables brokers to create escrow-protected transactions on behalf of merchants and customers. This endpoint initiates a secure payment flow where funds are held in escrow until transaction conditions are met.
Key Features
- Escrow Protection: Funds are securely held until both parties fulfill their obligations
- Multi-Currency Support: Supports NGN, USD, and GBP
- Automatic Account Creation: Merchant and customer accounts are created automatically if they don't exist
- Flexible Charge Allocation: Configure charge split between merchant and customer (0-100%)
- Settlement Accounts: Support for multiple settlement destinations
- Payment Link Generation: Automatic generation of customer payment page URL
Use Cases
- E-commerce marketplace transactions
- Freelance platform payments
- Real estate deposits
- Service provider payments
- Any transaction requiring escrow protection
2. Authentication
All API requests must be authenticated using an API Key provided in the request headers.
Authentication Method
| Header Name |
Value |
Required |
| BrokerApiKey |
Your unique API key |
Yes |
How to Obtain API Key
- Register as a broker on the PayScrow platform
- Complete KYC verification
- Generate API key from your broker dashboard
- Store your API key securely (never expose in client-side code)
Security Warning: Never share your API key or commit it to version control. Use environment variables or secure configuration management.
3. Endpoint Details
POST /api/v3/marketplace/transactions/start
Base URL
https://api.payscrow.net
Full Endpoint URL
https://api.payscrow.net/api/v3/marketplace/transactions/start
Content Type
All requests must use Content-Type: application/json
4. Request Specification
Request Headers
| Header |
Value |
Required |
Description |
| Content-Type |
application/json |
Yes |
Request body format |
| BrokerApiKey |
Your API key |
Yes |
API authentication |
Request Body Parameters
| Parameter |
Type |
Required |
Description |
| transactionReference |
string |
Required |
Unique transaction reference (max 200 characters). Must be unique per broker. |
| merchantEmailAddress |
string |
Required |
Merchant's email address. Valid email format required. |
| merchantPhoneNo |
string |
Optional |
Merchant's phone number (max 20 characters). |
| merchantName |
string |
Required |
Merchant's name or business name (max 300 characters). |
| customerEmailAddress |
string |
Required |
Customer's email address. Valid email format required. |
| customerPhoneNo |
string |
Optional |
Customer's phone number (max 20 characters). |
| customerName |
string |
Required |
Customer's full name (max 300 characters). |
| currencyCode |
string |
Required |
3-character currency code. Supported: NGN, USD, GBP. |
| merchantChargePercentage |
decimal |
Required |
Percentage of escrow charges to be borne by merchant (0-100). Remaining percentage is charged to customer. |
| returnUrl |
string |
Optional |
URL to redirect customer after payment completion. Must be valid absolute URL. If provided, the success page will show "Return to Merchant" button. If not provided (or null), the button will show "Return to Dashboard" and redirect to PayScrow dashboard. Falls back to broker config if not provided. |
| responseUrl |
string |
Optional |
URL for payment gateway response callback. Must be valid absolute URL. |
| webhookNotificationUrl |
string |
Optional |
Webhook URL for transaction status notifications. Must be valid absolute URL. Falls back to broker config if not provided. |
| items |
array |
Required |
Array of transaction items. Must contain at least one item. See Items Object below. |
| settlementAccounts |
array |
Optional |
Array of settlement bank accounts for fund disbursement. See Settlement Accounts Object below. |
Items Object
| Parameter |
Type |
Required |
Description |
| name |
string |
Required |
Item name or description (max 300 characters). |
| description |
string |
Optional |
Detailed item description (max 4000 characters). |
| quantity |
integer |
Required |
Item quantity. Must be greater than 0. |
| price |
decimal |
Required |
Unit price. Must be greater than 0. No negative amounts allowed. |
Settlement Accounts Object
| Parameter |
Type |
Required |
Description |
| bankCode |
string |
Required* |
Bank code (exactly 3 characters). Example: "058" for GTBank. |
| accountNumber |
string |
Required* |
Bank account number (exactly 10 digits). |
| accountName |
string |
Required* |
Account holder name (max 100 characters). |
| amount |
decimal |
Required* |
Amount to settle to this account. Must be greater than 0. |
* Required if settlementAccounts array is provided. Total settlement amounts must equal total transaction amount.
5. Response Specification
Success Response (HTTP 200)
{
"success": true,
"message": null,
"data": {
"transactionId": 12345,
"transactionNumber": "TXN-2025-001234",
"paymentLink": "https://payscrow.net/pay?transId=XYZ789",
"totalPayable": 1050000.00,
"currencyCode": "NGN",
"errors": [],
"isSuccessful": true
},
"errors": null
}
Response Fields
| Field |
Type |
Description |
| success |
boolean |
Indicates if the request was successful. |
| message |
string |
Human-readable message. Null on success, error message on failure. |
| data.transactionId |
integer |
Internal database transaction ID. For internal use only. |
| data.transactionNumber |
string |
Use this for all broker operations. Human-readable transaction number used for tracking and broker API calls. |
| data.paymentLink |
string |
Customer payment page URL. Send this link to customer to complete payment. |
| data.totalPayable |
decimal |
Total amount customer needs to pay (includes items + customer's share of charges). |
| data.currencyCode |
string |
Currency code for the transaction. |
| data.isSuccessful |
boolean |
Transaction creation success status. |
| data.errors |
array |
Array of error messages. Empty on success. |
| errors |
array |
Top-level error array. Null on success, contains error details on failure. |
6. HTTP Status Codes
| Status Code |
Status |
Description |
| 200 |
OK |
Transaction created successfully. Payment link generated. |
| 400 |
Bad Request |
Validation errors. Check request parameters. Common causes: missing required fields, invalid email/URL format, field length violations, invalid numeric ranges. |
| 401 |
Unauthorized |
Authentication failed. BrokerApiKey header missing or invalid. |
| 409 |
Conflict |
Transaction reference already exists. Use a unique transaction reference. |
| 422 |
Unprocessable Entity |
Business rule violation. Common causes: invalid currency code, account creation failure, settlement validation errors, charge calculation errors. |
| 500 |
Internal Server Error |
Server error. Contact support if issue persists. |
7. Validation Rules
Field-Level Validations
- transactionReference: Not empty, max 200 characters, must be unique per broker
- currencyCode: Exactly 3 characters, must be one of: NGN, USD, GBP
- Email fields: Must be valid email format
- Phone numbers: Max 20 characters
- Name fields: Max 300 characters
- URLs: Must be valid absolute URLs (if provided)
- merchantChargePercentage: 0 ≤ value ≤ 100
- Items array: Must contain at least 1 item
- Item name: Max 300 characters
- Item description: Max 4000 characters
- Item quantity: Must be > 0
- Item price: Must be > 0 (no negative amounts)
- Settlement account number: Exactly 10 characters
- Settlement bank code: Exactly 3 characters
- Settlement account name: Max 100 characters
- Settlement amount: Must be > 0
Business Rule Validations
- Currency Code: Must be active and supported in the system
- Settlement Accounts: If provided, total settlement amount must equal total transaction amount
- Settlement Total: Cannot exceed transaction total
- Transaction Reference: Must be unique across all transactions for your broker account
8. Code Examples
Example 1: Simple Transaction
cURL
curl -X POST https://api.payscrow.net/api/v3/marketplace/transactions/start \
-H "Content-Type: application/json" \
-H "BrokerApiKey: your-api-key-here" \
-d '{
"transactionReference": "TXN-2024-001",
"merchantEmailAddress": "merchant@store.com",
"merchantPhoneNo": "08012345678",
"merchantName": "Best Electronics Store",
"customerEmailAddress": "customer@email.com",
"customerPhoneNo": "08098765432",
"customerName": "John Doe",
"currencyCode": "NGN",
"merchantChargePercentage": 50,
"returnUrl": "https://yourstore.com/payment/return",
"items": [
{
"name": "iPhone 15 Pro",
"description": "256GB Space Black",
"quantity": 1,
"price": 850000.00
}
]
}'
C# (.NET)
using System.Net.Http;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("BrokerApiKey", "your-api-key-here");
var request = new
{
transactionReference = "TXN-2024-001",
merchantEmailAddress = "merchant@store.com",
merchantPhoneNo = "08012345678",
merchantName = "Best Electronics Store",
customerEmailAddress = "customer@email.com",
customerPhoneNo = "08098765432",
customerName = "John Doe",
currencyCode = "NGN",
merchantChargePercentage = 50,
returnUrl = "https://yourstore.com/payment/return",
items = new[]
{
new
{
name = "iPhone 15 Pro",
description = "256GB Space Black",
quantity = 1,
price = 850000.00m
}
}
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
"https://api.payscrow.net/api/v3/marketplace/transactions/start",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
JavaScript (Node.js)
const axios = require('axios');
const createTransaction = async () => {
try {
const response = await axios.post(
'https://api.payscrow.net/api/v3/marketplace/transactions/start',
{
transactionReference: 'TXN-2024-001',
merchantEmailAddress: 'merchant@store.com',
merchantPhoneNo: '08012345678',
merchantName: 'Best Electronics Store',
customerEmailAddress: 'customer@email.com',
customerPhoneNo: '08098765432',
customerName: 'John Doe',
currencyCode: 'NGN',
merchantChargePercentage: 50,
returnUrl: 'https://yourstore.com/payment/return',
items: [
{
name: 'iPhone 15 Pro',
description: '256GB Space Black',
quantity: 1,
price: 850000.00
}
]
},
{
headers: {
'Content-Type': 'application/json',
'BrokerApiKey': 'your-api-key-here'
}
}
);
console.log(response.data);
} catch (error) {
console.error(error.response.data);
}
};
createTransaction();
Python
import requests
import json
url = "https://api.payscrow.net/api/v3/marketplace/transactions/start"
headers = {
"Content-Type": "application/json",
"BrokerApiKey": "your-api-key-here"
}
payload = {
"transactionReference": "TXN-2024-001",
"merchantEmailAddress": "merchant@store.com",
"merchantPhoneNo": "08012345678",
"merchantName": "Best Electronics Store",
"customerEmailAddress": "customer@email.com",
"customerPhoneNo": "08098765432",
"customerName": "John Doe",
"currencyCode": "NGN",
"merchantChargePercentage": 50,
"returnUrl": "https://yourstore.com/payment/return",
"items": [
{
"name": "iPhone 15 Pro",
"description": "256GB Space Black",
"quantity": 1,
"price": 850000.00
}
]
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
print(response.json())
Example 2: Multiple Items with Settlement Accounts
Request
{
"transactionReference": "TXN-2024-002",
"merchantEmailAddress": "merchant@tech.com",
"merchantPhoneNo": "08012345678",
"merchantName": "Tech Supplies Ltd",
"customerEmailAddress": "buyer@company.com",
"customerPhoneNo": "08098765432",
"customerName": "Jane Smith",
"currencyCode": "NGN",
"merchantChargePercentage": 100,
"webhookNotificationUrl": "https://yourstore.com/webhook",
"items": [
{
"name": "Laptop",
"description": "Dell XPS 15",
"quantity": 2,
"price": 500000.00
},
{
"name": "Mouse",
"quantity": 5,
"price": 5000.00
}
],
"settlementAccounts": [
{
"bankCode": "058",
"accountNumber": "0123456789",
"accountName": "Tech Supplies Ltd",
"amount": 1000000.00
},
{
"bankCode": "044",
"accountNumber": "9876543210",
"accountName": "Partner Company",
"amount": 25000.00
}
]
}
Response
{
"success": true,
"message": null,
"data": {
"transactionId": 67890,
"paymentLink": "https://payscrow.net/pay?transId=ABC456",
"totalPayable": 1025000.00,
"currencyCode": "NGN",
"errors": [],
"isSuccessful": true
},
"errors": null
}
9. Error Handling
Error Response Format
All error responses follow a consistent format:
{
"success": false,
"message": "Error summary",
"data": {
"transactionId": 0,
"paymentLink": "",
"totalPayable": 0,
"currencyCode": "",
"errors": [
"Detailed error message 1",
"Detailed error message 2"
],
"isSuccessful": false
},
"errors": [
"Error detail 1",
"Error detail 2"
]
}
Common Error Scenarios
1. Missing API Key (HTTP 401)
{
"success": false,
"message": "BrokerApiKey header is required",
"errors": [
"Missing or invalid BrokerApiKey header"
]
}
2. Validation Errors (HTTP 400)
{
"success": false,
"message": "Validation failed",
"errors": [
"'Transaction Reference' must not be empty.",
"currency code has to be 3 characters only!",
"must have at least one item to pay for.",
"Price must be greater than 0. Negative amounts are not allowed."
]
}
3. Duplicate Transaction (HTTP 409)
{
"success": false,
"message": null,
"data": {
"transactionId": 0,
"paymentLink": "",
"totalPayable": 0,
"currencyCode": "",
"errors": [
"Transaction Reference: \"TXN-2024-001\" already exist."
],
"isSuccessful": false
},
"errors": null
}
4. Invalid Currency Code (HTTP 422)
{
"success": false,
"message": null,
"data": {
"transactionId": 0,
"paymentLink": "",
"totalPayable": 0,
"currencyCode": "",
"errors": [
"Invalid currency code"
],
"isSuccessful": false
},
"errors": null
}
5. Settlement Validation Error (HTTP 400)
{
"success": false,
"message": "Validation failed",
"errors": [
"Settlement amount cannot be greater than the amount to be escrowed!"
]
}
6. Server Error (HTTP 500)
{
"success": false,
"message": "An unexpected error occurred",
"errors": [
"Transaction failed due to server error. Please try again."
]
}
10. Business Logic
Account Auto-Creation
When a transaction is created:
- System checks if merchant account exists (by email)
- If not found, creates merchant account automatically
- Same process applies for customer account
- Account name and phone are updated if account already exists
Transaction Number Generation
Each transaction gets a unique number in format: MKT-{number:D8}
Example: MKT-00012345
Charge Calculation
Escrow charges are calculated based on transaction amount and currency:
- Total transaction amount = Sum of (quantity × price) for all items
- Total escrow charge = Calculated by system based on currency and amount
- Merchant charge = Total charge × (merchantChargePercentage ÷ 100)
- Customer charge = Total charge × ((100 - merchantChargePercentage) ÷ 100)
- Total payable = Transaction amount + Customer charge
Example: If transaction total is ₦1,000,000, escrow charge is ₦15,000, and merchantChargePercentage is 50:
• Merchant pays: ₦7,500 (50% of ₦15,000)
• Customer pays: ₦1,007,500 (₦1,000,000 + ₦7,500)
Payment Link
The payment link directs the customer to a secure payment page where they can:
- View transaction details
- See items being purchased
- Review total amount to pay
- Complete payment using available payment methods
Payment Success Redirect Behavior
After successful payment, customers are shown a success page with a return button. The button's behavior is determined by the returnUrl parameter:
With returnUrl Provided
- Button displays: "Return to Merchant"
- Clicking redirects customer to the provided returnUrl
- Use this to bring customers back to your website/store after payment
- Example: https://yourstore.com/payment/success
Without returnUrl (or null)
- Button displays: "Return to Dashboard"
- Clicking redirects customer to PayScrow dashboard (/Dashboard)
- Useful for internal transactions or when merchant doesn't need custom redirect
Best Practice: Always provide a returnUrl for external merchant transactions to ensure customers return to your website. The URL should be an absolute URL (e.g., https://yourstore.com/order-confirmation) and should be a page that confirms the order and provides next steps to the customer.
Settlement Accounts
When settlement accounts are provided:
- Funds will be distributed to specified accounts upon transaction completion
- Total settlement amount must equal total transaction amount
- Useful for split payments or marketplace scenarios
- If not provided, full amount goes to merchant's default account
Supported Currencies
| Currency Code |
Currency Name |
Symbol |
Charge Cap |
| NGN |
Nigerian Naira |
₦ |
₦1,000.00 |
| USD |
US Dollar |
$ |
$100.00 |
| GBP |
British Pound |
£ |
£100.00 |
11. Troubleshooting
Issue: "Invalid currency code" error
Cause: Currency code is not supported or inactive
Solution:
- Ensure currency code is exactly 3 characters
- Use only supported currencies: NGN, USD, GBP
- Currency codes are case-sensitive - use UPPERCASE
Issue: "Transaction Reference already exist" error
Cause: Duplicate transaction reference
Solution:
- Each transaction reference must be unique for your broker account
- Use a different reference for each transaction
- Consider using timestamp or UUID in your reference format
- Example: TXN-{timestamp}-{randomString}
Issue: Settlement validation errors
Cause: Settlement amounts don't match transaction total
Solution:
- Sum of all settlement amounts must equal total transaction amount
- Calculate: Total = Sum of (item.quantity × item.price)
- Ensure no rounding errors in settlement amounts
- Each settlement amount must be greater than 0
Issue: "Failed to create merchant/customer account" error
Cause: Email already registered with conflicting details
Solution:
- Email might be registered with different phone number
- Contact PayScrow support to resolve account conflicts
- Ensure consistent merchant/customer details across transactions
Issue: Unauthorized (401) errors
Cause: Missing or invalid API key
Solution:
- Verify BrokerApiKey header is present in request
- Check API key is correct (no extra spaces)
- Ensure API key is active and not expired
- Regenerate API key if necessary
Issue: Validation errors on URLs
Cause: Invalid URL format
Solution:
- URLs must be absolute (include protocol: https://)
- Valid example: https://yoursite.com/callback
- Invalid example: /callback or yoursite.com/callback
- URLs are optional - omit if not needed
Best Practices:
- Always use HTTPS for all URLs
- Implement idempotency using unique transaction references
- Store API responses for reconciliation
- Implement webhook handlers for real-time updates
- Monitor API response times and status codes
- Keep API keys secure and rotate regularly
In addition to creating transactions, brokers have access to other operations for managing transactions on behalf of their clients.
Get Transaction Status
This endpoint returns the current status of a transaction using its transaction number.
GET /api/v3/marketplace/transactions/{transactionNumber}/status
Authentication
Uses the same BrokerApiKey header authentication as the transaction creation endpoint.
Full Endpoint URL
https://api.payscrow.net/api/v3/marketplace/transactions/{transactionNumber}/status
URL Parameters
| Parameter |
Type |
Required |
Description |
| transactionNumber |
string |
Yes |
The transaction number returned when the transaction was created using the /start endpoint (e.g., "MKT-00012345"). |
Success Response (200 OK)
{
"success": true,
"message": null,
"data": {
"transactionNumber": "MKT-134345",
"status": "In Progress",
"inEscrow": true,
"inDispute": false,
"statusId": 2
},
"errors": null
}
Response Fields
| Field |
Type |
Description |
| transactionNumber |
string |
The transaction number (null if not found) |
| status |
string |
Human-readable status: "Pending", "In Progress", "Completed", "Finalized", or "Terminated" |
| inEscrow |
boolean |
Whether funds are currently held in escrow |
| inDispute |
boolean |
Whether the transaction is currently in dispute |
| statusId |
integer |
Numeric status ID: 1=Pending, 2=In Progress, 3=Completed, 4=Finalized, 5=Terminated |
Transaction Status Values
| Status ID |
Status Name |
Description |
| 1 |
Pending |
Transaction created, awaiting payment |
| 2 |
In Progress |
Payment received, funds in escrow |
| 3 |
Completed |
Escrow code applied, ready for release |
| 4 |
Finalized |
Funds released, transaction concluded |
| 5 |
Terminated |
Transaction cancelled or refunded |
HTTP Status Codes
| Code |
Meaning |
Description |
| 200 |
OK |
Transaction found and status returned successfully |
| 401 |
Unauthorized |
Invalid or missing BrokerApiKey |
| 403 |
Forbidden |
Transaction exists but you are not the broker for this transaction |
| 404 |
Not Found |
Transaction with the specified transaction number does not exist |
Error Response Examples
404 Not Found - Transaction not found:
{
"success": false,
"errors": ["Transaction not found"],
"errorType": "NotFound"
}
403 Forbidden - Not authorized as broker:
{
"success": false,
"errors": ["You are not authorized as the broker for this transaction"],
"errorType": "Forbidden"
}
Integration Notes
When to Use This Endpoint:
- To poll for transaction status updates (recommended: every 30 seconds)
- To verify payment status before fulfilling orders
- To check if a transaction is in dispute before taking action
- Use the transactionNumber from the /start response
Calculate Transaction Charges
This endpoint allows brokers to calculate transaction charges before creating a transaction. Use this to preview the charge breakdown for your customers.
GET /api/v3/marketplace/charges/calculate
Authentication
Uses the same BrokerApiKey header authentication as the transaction creation endpoint.
Full Endpoint URL
https://api.payscrow.net/api/v3/marketplace/charges/calculate?currencyCode=NGN&amount=100000&merchantChargePercentage=50
Query Parameters
| Parameter |
Type |
Required |
Description |
| currencyCode |
string |
Yes |
3-character currency code. Supported: NGN, USD, GBP. |
| amount |
decimal |
Yes |
Transaction amount to calculate charges for. Must be greater than 0. |
| merchantChargePercentage |
decimal |
Optional |
Percentage of charge to be paid by merchant (0-100). Default is 0 (customer pays all charges). |
Success Response (200 OK)
{
"success": true,
"message": null,
"data": {
"amount": 100000.00,
"totalCharge": 1600.00,
"merchantCharge": 800.00,
"customerCharge": 800.00,
"grandTotalPayable": 100800.00,
"totalSettlementAmount": 99200.00,
"currencyCode": "NGN",
"currencySymbol": "₦",
"isSuccessful": true,
"errors": []
},
"errors": null
}
Response Fields
| Field |
Type |
Description |
| amount |
decimal |
Original transaction amount provided in the request |
| totalCharge |
decimal |
Total escrow charge before splitting between merchant and customer |
| merchantCharge |
decimal |
Portion of charge to be paid by the merchant |
| customerCharge |
decimal |
Portion of charge to be paid by the customer |
| grandTotalPayable |
decimal |
Total amount customer will pay (amount + customerCharge) |
| totalSettlementAmount |
decimal |
Amount to be settled to merchant after deducting merchant's share of charges (amount - merchantCharge) |
| currencyCode |
string |
Currency code (e.g., "NGN") |
| currencySymbol |
string |
Currency symbol (e.g., "₦") |
HTTP Status Codes
| Code |
Meaning |
Description |
| 200 |
OK |
Charges calculated successfully |
| 400 |
Bad Request |
Validation error: Invalid currency code, amount <= 0, or merchantChargePercentage outside 0-100 range |
| 401 |
Unauthorized |
Invalid or missing BrokerApiKey |
| 404 |
Not Found |
Currency not found or not active |
Error Response Examples
400 Bad Request - Validation error:
{
"success": false,
"message": "Currency code is required",
"errors": ["Currency code is required"]
}
404 Not Found - Currency not found:
{
"success": false,
"message": "Currency 'XYZ' not found or is not active",
"errors": ["Currency 'XYZ' not found or is not active"]
}
Code Examples
cURL
curl -X GET "https://api.payscrow.net/api/v3/marketplace/charges/calculate?currencyCode=NGN&amount=100000&merchantChargePercentage=50" \
-H "BrokerApiKey: your-api-key-here"
JavaScript
const response = await fetch(
'https://api.payscrow.net/api/v3/marketplace/charges/calculate?currencyCode=NGN&amount=100000&merchantChargePercentage=50',
{
method: 'GET',
headers: {
'BrokerApiKey': 'your-api-key-here'
}
}
);
const result = await response.json();
console.log('Total charge:', result.data.totalCharge);
console.log('Customer pays:', result.data.grandTotalPayable);
Integration Notes
When to Use This Endpoint:
- Before creating a transaction, to show customers the exact amount they will pay
- To display charge breakdowns in your checkout flow
- To calculate and display fees transparently to merchants and customers
- Charges are calculated based on broker-specific or global charge configurations
Raise Marketplace Transaction Dispute (Broker)
This endpoint allows brokers to initiate a dispute on a marketplace transaction on behalf of an involved party (merchant or customer).
POST /api/v3/marketplace/transactions/{transactionNumber}/broker/raise-dispute
Authentication
Uses the same BrokerApiKey header authentication as the transaction creation endpoint.
Full Endpoint URL
https://api.payscrow.net/api/v3/marketplace/transactions/{transactionNumber}/broker/raise-dispute
URL Parameters
| Parameter |
Type |
Required |
Description |
| transactionNumber |
string |
Yes |
The transaction number returned when the transaction was created using the /start endpoint. This is used for all broker operations. |
Request Body
| Field |
Type |
Required |
Description |
| requestedBy |
string |
Yes |
Who is requesting the dispute: "merchant" or "customer". This determines who initiated the dispute and against whom. |
| complaint |
string |
Yes |
Detailed description of the dispute/complaint. Must be between 10-1000 characters. |
Request Example
{
"requestedBy": "customer",
"complaint": "The merchant did not deliver the goods as agreed. I have contacted them multiple times without resolution."
}
Success Response (200 OK)
{
"success": true,
"errors": [],
"errorType": null
}
HTTP Status Codes
| Code |
Meaning |
Description |
| 200 |
OK |
Dispute successfully initiated. The Dispute Resolution module will handle the dispute process. |
| 400 |
Bad Request |
Validation error: Missing required fields, invalid requestedBy value, or complaint length violation (10-1000 chars). |
| 401 |
Unauthorized |
Invalid or missing BrokerApiKey. |
| 403 |
Forbidden |
The broker is not authorized for this transaction. |
| 404 |
Not Found |
Transaction not found with the provided ID. |
| 422 |
Unprocessable Entity |
Business rule violation: Transaction payment pending, already in dispute, concluded, or not yet in escrow. |
| 500 |
Internal Server Error |
An unexpected error occurred during processing. |
Error Response Examples
404 Not Found - Transaction not found:
{
"success": false,
"errors": ["Transaction not found"],
"errorType": "NotFound"
}
403 Forbidden - Not authorized as broker:
{
"success": false,
"errors": ["You are not authorized as the broker for this transaction"],
"errorType": "Forbidden"
}
422 Unprocessable Entity - Business rule violation:
{
"success": false,
"errors": ["This transaction is already in dispute"],
"errorType": "UnprocessableEntity"
}
400 Bad Request - Validation error:
{
"success": false,
"errors": [
"RequestedBy must be either 'merchant' or 'customer'",
"Complaint must be at least 10 characters"
],
"errorType": "Validation"
}
Business Rules
- Only the broker associated with the transaction can raise a dispute via this endpoint
- The requestedBy field must be either "merchant" or "customer" (case-insensitive)
- The requestedBy value determines:
- If "merchant": Dispute is raised BY the merchant AGAINST the customer
- If "customer": Dispute is raised BY the customer AGAINST the merchant
- Disputes cannot be raised on transactions that are:
- Payment still pending
- Not yet entered escrow (still in pending status)
- Already concluded/finalized
- Already in dispute
- Once initiated, the dispute enters the Dispute Resolution module workflow
- Use the transactionNumber returned from the /start endpoint for all broker operations (not the internal database ID)
Integration Notes
When to Use This Endpoint:
- When a merchant or customer contacts you (the broker) with a complaint about a transaction
- Before the transaction has been released or completed
- As an alternative to the merchant/customer raising the dispute directly through the platform
Apply Escrow Code (Broker)
This endpoint allows brokers to apply an escrow code to release funds from escrow. The escrow code is received via the Transaction Paid webhook and should be stored securely until the broker is ready to release the funds.
POST /api/v3/escrow/escrowtransactions/applycode
Authentication
Uses the same BrokerApiKey header authentication as the transaction creation endpoint.
Full Endpoint URL
https://api.payscrow.net/api/v3/escrow/escrowtransactions/applycode
Request Body
| Field |
Type |
Required |
Description |
| transactionId |
string (GUID) |
Yes |
The transaction GUID. Use the transactionId field from the webhook payload or from the /start response. |
| code |
string |
Yes |
The escrow code received in the webhook payload's escrowCode field. Maximum 10 characters. |
Request Example
{
"transactionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "ABC123"
}
Success Response (200 OK)
{
"success": true,
"message": "Escrow Code applied successfully!",
"errors": []
}
HTTP Status Codes
| Code |
Meaning |
Description |
| 200 |
OK |
Escrow code applied successfully. Transaction status changes to "PendingSettlement" and funds will be released. |
| 400 |
Bad Request |
Validation error: Missing or invalid fields, or a business rule violation (see error examples below). |
| 401 |
Unauthorized |
Invalid or missing BrokerApiKey. |
| 500 |
Internal Server Error |
An unexpected error occurred during processing. |
Error Response Examples
400 Bad Request - Transaction not found or not authorized:
{
"success": false,
"message": "",
"errors": ["Transaction not found or you are not authorized as broker!"]
}
400 Bad Request - Already released:
{
"success": false,
"message": "",
"errors": ["Escrow code already applied for this transaction"]
}
400 Bad Request - Transaction in dispute:
{
"success": false,
"message": "",
"errors": ["You cannot apply an escrow code to a transaction that is in dispute!"]
}
400 Bad Request - Invalid escrow code:
{
"success": false,
"message": "",
"errors": ["Invalid code. Please ensure you have entered the correct code!"]
}
Business Rules
- The broker must be associated with the transaction (verified via escrow account role)
- The transaction must not have been previously released
- The transaction must not be currently in dispute
- The escrow code is validated case-insensitively against the stored code
- On success, the escrow status changes to PendingSettlement and the transaction is marked as released
- The MarketPlace transaction automatically updates to Completed and InEscrow becomes false
Code Examples
cURL
curl -X POST "https://api.payscrow.net/api/v3/escrow/escrowtransactions/applycode" \
-H "BrokerApiKey: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "ABC123"
}'
JavaScript
const response = await fetch(
'https://api.payscrow.net/api/v3/escrow/escrowtransactions/applycode',
{
method: 'POST',
headers: {
'BrokerApiKey': 'your-api-key-here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
transactionId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
code: 'ABC123'
})
}
);
const result = await response.json();
if (result.success) {
console.log('Escrow released:', result.message);
} else {
console.error('Failed:', result.errors);
}
C# (HttpClient)
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("BrokerApiKey", "your-api-key-here");
var payload = new
{
transactionId = "3fa85f64-5717-4562-b3fc-2c963f66afa6",
code = "ABC123"
};
var response = await client.PostAsJsonAsync(
"https://api.payscrow.net/api/v3/escrow/escrowtransactions/applycode",
payload
);
var result = await response.Content.ReadFromJsonAsync<ApplyCodeResult>();
if (result.Success)
Console.WriteLine($"Escrow released: {result.Message}");
else
Console.WriteLine($"Errors: {string.Join(", ", result.Errors)}");
Integration Notes
When to Use This Endpoint:
- After receiving the Transaction Paid webhook which contains the escrowCode
- When the broker has verified that the merchant has fulfilled their obligations (e.g., goods delivered, service completed)
- Use the transactionId (GUID) from the webhook payload, not the transactionNumber
Typical Flow:
- Create transaction via /start endpoint
- Customer pays — you receive a Transaction Paid webhook containing the escrowCode
- Store the escrowCode and transactionId securely
- When ready to release funds, call this endpoint with the stored values
- Funds move to PendingSettlement and are disbursed to the merchant
Get Supported Banks
This endpoint returns a list of all Nigerian banks supported by PayScrow for settlement purposes.
GET /api/v3/payments/banks/broker/supported-banks
Authentication
Uses the same BrokerApiKey header authentication as the transaction creation endpoint.
Full Endpoint URL
https://api.payscrow.net/api/v3/payments/banks/broker/supported-banks
Request
This endpoint does not require any parameters. Simply make a GET request with your broker API key in the header.
Success Response (200 OK)
{
"success": true,
"message": null,
"data": {
"banks": [
{
"id": 1,
"name": "Access Bank",
"code": "044",
"country": "NG"
},
{
"id": 2,
"name": "Guaranty Trust Bank",
"code": "058",
"country": "NG"
},
{
"id": 3,
"name": "United Bank for Africa",
"code": "033",
"country": "NG"
},
{
"id": 4,
"name": "Zenith Bank",
"code": "057",
"country": "NG"
}
]
},
"errors": null
}
Response Fields
| Field |
Type |
Description |
| banks |
array |
List of all supported Nigerian banks |
| banks[].id |
integer |
Internal bank ID |
| banks[].name |
string |
Full name of the bank |
| banks[].code |
string |
Bank code (CBN code) - Use this when specifying settlement accounts |
| banks[].country |
string |
Country code (always "NG" for Nigeria) |
HTTP Status Codes
| Code |
Meaning |
Description |
| 200 |
OK |
Successfully retrieved the list of supported banks |
| 401 |
Unauthorized |
Invalid or missing BrokerApiKey |
Integration Notes
When to Use This Endpoint:
- Before collecting settlement account information from merchants
- To validate bank codes when creating transactions with settlement accounts
- To display a dropdown/selection list of supported banks in your UI
- The bank code field should be used in the settlementAccounts[].bankCode when creating transactions
Webhooks
PayScrow sends webhook notifications to your specified endpoint to keep you informed about transaction events in real-time.
Webhook Setup
To receive webhook notifications, include the webhookNotificationUrl parameter when creating a transaction via the /start endpoint. PayScrow will send HTTP POST requests to this URL when specific events occur.
Important:
- Your webhook endpoint must accept POST requests
- The endpoint should return a 2xx status code to indicate successful receipt
- PayScrow will retry failed webhook deliveries with exponential backoff
- Ensure your endpoint can handle the webhook payload format described below
Transaction Paid Webhook
This webhook is triggered when a customer successfully completes payment for a transaction.
Event Trigger
Sent when: Customer payment is verified and the transaction enters escrow status.
HTTP Method
POST
Headers
| Header |
Value |
| Content-Type |
application/json |
Webhook Payload
{
"transactionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"transactionNumber": "TXN-2025-001234",
"escrowCode": "ABC123",
"externalReference": "YOUR-REF-12345",
"paymentStatus": "Paid",
"escrowStatus": "InEscrow",
"amountPaid": "1050000.00",
"currency": "NGN",
"status": "InProgress",
"paymentDate": "2025-12-07T14:30:00Z",
"timestamp": "2025-12-07T14:30:05Z"
}
Payload Fields
| Field |
Type |
Description |
| transactionId |
string (GUID) |
Unique transaction identifier (GUID format) |
| transactionNumber |
string |
Human-readable transaction number for tracking |
| escrowCode |
string |
6-character code for releasing funds from escrow. Store this securely! |
| externalReference |
string |
Your original transaction reference from the /start request |
| paymentStatus |
string |
Payment status - will be "Paid" for this webhook |
| escrowStatus |
string |
Escrow status - will be "InEscrow" indicating funds are held |
| amountPaid |
string |
Total amount paid including all charges (formatted as decimal string) |
| currency |
string |
Currency code (e.g., "NGN") |
| status |
string |
Transaction status - will be "InProgress" after payment |
| paymentDate |
string (ISO 8601) |
Date and time when payment was completed |
| timestamp |
string (ISO 8601) |
Date and time when webhook was generated |
Webhook Response
Your endpoint should respond with a 2xx HTTP status code to acknowledge receipt of the webhook. Any other status code will be considered a failure and PayScrow will retry delivery.
HTTP/1.1 200 OK
Content-Type: application/json
{
"received": true
}
Retry Policy
| Attempt |
Delay |
| 1st retry |
30 seconds |
| 2nd retry |
5 minutes |
| 3rd retry |
1 hour |
Security Considerations
Important Security Notes:
- Validate the source: Ensure webhook requests are coming from PayScrow's servers (check IP whitelisting if available)
- Idempotency: Your endpoint should handle duplicate webhooks gracefully. Use transactionId or transactionNumber to detect duplicates
- Secure storage: Store the escrowCode securely - it's required to release funds from escrow
- Verify transactions: Cross-reference webhook data with your records using externalReference
- HTTPS required: Your webhook URL must use HTTPS in production
Example Implementation (Node.js/Express)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/payscrow/transaction-paid', async (req, res) => {
try {
const payload = req.body;
// Log webhook receipt
console.log('Received PayScrow webhook:', payload);
// Check for duplicate (idempotency)
const existingRecord = await db.findTransaction(payload.transactionNumber);
if (existingRecord && existingRecord.webhookProcessed) {
console.log('Duplicate webhook, already processed');
return res.status(200).json({ received: true, duplicate: true });
}
// Verify transaction matches your records
const transaction = await db.findByReference(payload.externalReference);
if (!transaction) {
console.error('Transaction not found:', payload.externalReference);
return res.status(404).json({ error: 'Transaction not found' });
}
// Update transaction status
await db.updateTransaction(transaction.id, {
status: 'paid',
escrowCode: payload.escrowCode,
paymentDate: payload.paymentDate,
webhookProcessed: true
});
// Trigger your business logic (send confirmation email, update inventory, etc.)
await handleTransactionPaid(transaction, payload);
// Acknowledge receipt
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
Testing Webhooks
Testing Tips:
- Use tools like webhook.site or ngrok to test webhook delivery during development
- Test your endpoint's handling of duplicate webhooks
- Verify your endpoint responds within a reasonable timeout (recommended: under 5 seconds)
- Test failure scenarios and ensure your endpoint returns appropriate status codes
© 2025 PayScrow. All rights reserved.
Document Version: 1.1 | Last Updated: December 2025