Retail Return & Exchange API - cURL Collection
Complete cURL commands for testing the Retail Return & Exchange module endpoints.
Base URL: http://localhost:4000
Authentication: Include your Firebase ID token in the Authorization header or flowpos-id-token cookie.
π Table of Contentsβ
π Return Transactionsβ
1. Initiate Return (Receipt-Based)β
curl --location '{{BASE_URL}}/returns' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"locationId": "{{locationId}}",
"returnType": "receipt_based",
"receiptNumber": "000123",
"customerId": "{{customerId}}",
"cashRegisterSessionId": "{{cashRegisterSessionId}}",
"salespersonId": "{{employeeId}}",
"createdBy": "{{userId}}"
}'
2. Initiate Return (No Receipt)β
curl --location '{{BASE_URL}}/returns' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"locationId": "{{locationId}}",
"returnType": "no_receipt",
"customerId": "{{customerId}}",
"createdBy": "{{userId}}"
}'
3. Add Return Lineβ
curl --location '{{BASE_URL}}/returns/{{returnId}}/lines?businessId={{businessId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"originalSaleLineId": "{{originalLineId}}",
"quantity": 2,
"returnReasonId": "{{returnReasonId}}",
"condition": "resellable",
"serialNumber": "SN123456",
"notes": "Customer changed mind"
}'
Condition Options: resellable, damaged, opened
4. Calculate Refundβ
curl --location '{{BASE_URL}}/returns/{{returnId}}/refund-calculation?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
5. Check Requires Approvalβ
curl --location '{{BASE_URL}}/returns/{{returnId}}/requires-approval?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
6. Commit Return (Finalize)β
curl --location '{{BASE_URL}}/returns/{{returnId}}/commit?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"refundMethods": [
{
"paymentMethodId": "{{paymentMethodId}}",
"amount": 150.00,
"gatewayTransactionId": "txn_abc123"
}
],
"approvedBy": "{{managerEmployeeId}}",
"approvalReason": "Return amount exceeds threshold",
"notes": "Refund processed to original card"
}'
7. Get Return Detailsβ
curl --location '{{BASE_URL}}/returns/{{returnId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
8. Cancel Draft Returnβ
curl --location --request DELETE '{{BASE_URL}}/returns/{{returnId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
9. Lookup Sale by Receipt Numberβ
curl --location '{{BASE_URL}}/returns/sales/000123/lookup?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
π Exchange Transactionsβ
1. Initiate Exchangeβ
curl --location '{{BASE_URL}}/exchanges' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"locationId": "{{locationId}}",
"receiptNumber": "000123",
"customerId": "{{customerId}}",
"cashRegisterSessionId": "{{cashRegisterSessionId}}",
"salespersonId": "{{employeeId}}",
"createdBy": "{{userId}}"
}'
2. Add Exchange Return Line (Item Being Returned)β
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/return-lines?businessId={{businessId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"originalSaleLineId": "{{originalLineId}}",
"quantity": 1,
"returnReasonId": "{{returnReasonId}}",
"condition": "resellable",
"serialNumber": "SN123456",
"notes": "Wrong size"
}'
3. Add Exchange Sale Line (New Item Being Purchased)β
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/sale-lines?businessId={{businessId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"productId": "{{newProductId}}",
"quantity": 1,
"unitPrice": 89.99,
"notes": "Size L instead of M",
"inventoryDetails": [
{
"quantity": 1,
"batchNumber": "BATCH001",
"serialNumber": "SN789012"
}
]
}'
4. Calculate Exchange Settlementβ
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/settlement?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
5. Check Requires Approvalβ
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/requires-approval?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
6. Commit Exchange (Customer Pays Difference)β
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/commit?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"settlementMethods": [
{
"paymentMethodId": "{{paymentMethodId}}",
"amount": 25.50,
"direction": "in",
"gatewayTransactionId": "txn_def456"
}
],
"approvedBy": "{{managerEmployeeId}}",
"approvalReason": "High value exchange",
"notes": "Customer paid difference in cash"
}'
7. Commit Exchange (Customer Receives Refund)β
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/commit?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"settlementMethods": [
{
"paymentMethodId": "{{paymentMethodId}}",
"amount": 15.00,
"direction": "out",
"gatewayTransactionId": "txn_ghi789"
}
],
"notes": "Customer received refund difference"
}'
8. Commit Exchange (Even Exchange)β
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/commit?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"settlementMethods": [],
"notes": "Even exchange - no payment needed"
}'
9. Get Exchange Detailsβ
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
10. Cancel Draft Exchangeβ
curl --location --request DELETE '{{BASE_URL}}/exchanges/{{exchangeId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
π Return Policiesβ
1. Create Return Policy (Business-Wide)β
curl --location '{{BASE_URL}}/return-policies' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"returnWindowDays": 30,
"allowNoReceiptReturns": false,
"requireApprovalAboveAmount": 500.00,
"maxCashRefundForCardPayment": 100.00,
"refundToOriginalMethodOnly": true,
"refundSplitProportionally": true,
"allowCrossLocationReturns": true,
"crossLocationRestockRule": "return_location",
"draftTimeoutHours": 24,
"autoStoreCreditOnRefundFailure": true,
"active": true,
"createdBy": "{{userId}}"
}'
2. Create Return Policy (Location-Specific)β
curl --location '{{BASE_URL}}/return-policies' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"locationId": "{{locationId}}",
"returnWindowDays": 14,
"allowNoReceiptReturns": true,
"requireApprovalAboveAmount": 200.00,
"maxCashRefundForCardPayment": 50.00,
"refundToOriginalMethodOnly": false,
"refundSplitProportionally": false,
"active": true,
"createdBy": "{{userId}}"
}'
3. Get All Return Policiesβ
curl --location '{{BASE_URL}}/return-policies?businessId={{businessId}}&page=1&size=10&orderBy=createdAt&order=desc' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
4. Get Applicable Policyβ
curl --location '{{BASE_URL}}/return-policies/applicable?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
5. Get Return Policy by IDβ
curl --location '{{BASE_URL}}/return-policies/{{returnPolicyId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
6. Update Return Policyβ
curl --location --request PATCH '{{BASE_URL}}/return-policies/{{returnPolicyId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"returnWindowDays": 45,
"requireApprovalAboveAmount": 750.00,
"active": true
}'
7. Delete Return Policy (Soft Delete)β
curl --location --request DELETE '{{BASE_URL}}/return-policies/{{returnPolicyId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
π·οΈ Return Reasonsβ
1. Create Return Reasonβ
curl --location '{{BASE_URL}}/return-reasons' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"code": "DEFECTIVE",
"label": "Defective Product",
"affectsRestocking": false,
"defaultCondition": "damaged",
"active": true
}'
2. Create Multiple Return Reasons (Common Set)β
Changed Mindβ
curl --location '{{BASE_URL}}/return-reasons' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"code": "CHANGED_MIND",
"label": "Customer Changed Mind",
"affectsRestocking": true,
"defaultCondition": "resellable",
"active": true
}'
Wrong Sizeβ
curl --location '{{BASE_URL}}/return-reasons' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"code": "WRONG_SIZE",
"label": "Wrong Size",
"affectsRestocking": true,
"defaultCondition": "resellable",
"active": true
}'
Damagedβ
curl --location '{{BASE_URL}}/return-reasons' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"code": "DAMAGED",
"label": "Damaged on Arrival",
"affectsRestocking": false,
"defaultCondition": "damaged",
"active": true
}'
Not as Describedβ
curl --location '{{BASE_URL}}/return-reasons' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"code": "NOT_AS_DESC",
"label": "Not as Described",
"affectsRestocking": true,
"defaultCondition": "resellable",
"active": true
}'
3. Get All Return Reasonsβ
curl --location '{{BASE_URL}}/return-reasons?businessId={{businessId}}&page=1&size=20&orderBy=label&order=asc' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
4. Search Return Reasonsβ
curl --location '{{BASE_URL}}/return-reasons?businessId={{businessId}}&search=defective&page=1&size=10' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
5. Get Return Reason by IDβ
curl --location '{{BASE_URL}}/return-reasons/{{returnReasonId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
6. Update Return Reasonβ
curl --location --request PATCH '{{BASE_URL}}/return-reasons/{{returnReasonId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"label": "Defective or Damaged Product",
"affectsRestocking": false,
"active": true
}'
7. Delete Return Reason (Soft Delete)β
curl --location --request DELETE '{{BASE_URL}}/return-reasons/{{returnReasonId}}?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
π§ Environment Variablesβ
Replace these placeholders with actual values from your system:
# Authentication
YOUR_FIREBASE_TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6...
# Business Context
BUSINESS_ID=550e8400-e29b-41d4-a716-446655440000
LOCATION_ID=660e8400-e29b-41d4-a716-446655440001
# Users & Employees
USER_ID=770e8400-e29b-41d4-a716-446655440002
EMPLOYEE_ID=880e8400-e29b-41d4-a716-446655440003
MANAGER_EMPLOYEE_ID=990e8400-e29b-41d4-a716-446655440004
# Transactions
RETURN_ID=aa0e8400-e29b-41d4-a716-446655440005
EXCHANGE_ID=bb0e8400-e29b-41d4-a716-446655440006
ORIGINAL_LINE_ID=cc0e8400-e29b-41d4-a716-446655440007
# Configuration
RETURN_REASON_ID=dd0e8400-e29b-41d4-a716-446655440008
POLICY_ID=ee0e8400-e29b-41d4-a716-446655440009
PAYMENT_METHOD_ID=ff0e8400-e29b-41d4-a716-446655440010
# Products & Customers
PRODUCT_ID=110e8400-e29b-41d4-a716-446655440011
NEW_PRODUCT_ID=120e8400-e29b-41d4-a716-446655440012
CUSTOMER_ID=130e8400-e29b-41d4-a716-446655440013
# Session
SESSION_ID=140e8400-e29b-41d4-a716-446655440014
π Complete Workflow Examplesβ
Example 1: Simple Return Flowβ
# Step 1: Lookup original sale
curl --location '{{BASE_URL}}/returns/sales/000123/lookup?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
# Step 2: Initiate return
curl --location '{{BASE_URL}}/returns' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"locationId": "{{locationId}}",
"returnType": "receipt_based",
"receiptNumber": "000123",
"createdBy": "{{userId}}"
}'
# Save RETURN_ID from response
# Step 3: Add return lines
curl --location '{{BASE_URL}}/returns/{{returnId}}/lines?businessId={{businessId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"originalSaleLineId": "{{originalLineId}}",
"quantity": 1,
"returnReasonId": "{{returnReasonId}}",
"condition": "resellable"
}'
# Step 4: Calculate refund
curl --location '{{BASE_URL}}/returns/{{returnId}}/refund-calculation?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
# Step 5: Check if approval needed
curl --location '{{BASE_URL}}/returns/{{returnId}}/requires-approval?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
# Step 6: Commit return
curl --location '{{BASE_URL}}/returns/{{returnId}}/commit?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"refundMethods": [
{
"paymentMethodId": "{{paymentMethodId}}",
"amount": 99.99
}
]
}'
Example 2: Exchange Flowβ
# Step 1: Initiate exchange
curl --location '{{BASE_URL}}/exchanges' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"businessId": "{{businessId}}",
"locationId": "{{locationId}}",
"receiptNumber": "000123",
"createdBy": "{{userId}}"
}'
# Save EXCHANGE_ID from response
# Step 2: Add return line (old item)
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/return-lines?businessId={{businessId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"originalSaleLineId": "{{originalLineId}}",
"quantity": 1,
"returnReasonId": "{{returnReasonId}}",
"condition": "resellable"
}'
# Step 3: Add sale line (new item)
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/sale-lines?businessId={{businessId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"productId": "{{newProductId}}",
"quantity": 1,
"unitPrice": 129.99
}'
# Step 4: Calculate settlement
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/settlement?businessId={{businessId}}' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN'
# Step 5: Commit exchange
curl --location '{{BASE_URL}}/exchanges/{{exchangeId}}/commit?businessId={{businessId}}&locationId={{locationId}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_FIREBASE_TOKEN' \
--data '{
"settlementMethods": [
{
"paymentMethodId": "{{paymentMethodId}}",
"amount": 30.00,
"direction": "in"
}
]
}'
π― Testing Tipsβ
- Setup Order: Create return policies and reasons first before processing returns
- Draft Transactions: Returns/exchanges start as drafts - you can cancel them before committing
- Approval Workflow: Test with amounts above and below the approval threshold
- Inventory Tracking: Check inventory levels before and after commits
- Concurrency: Test multiple cashiers processing returns on the same sale simultaneously
- Error Cases: Test with invalid receipt numbers, exceeded quantities, expired return windows
π Response Examplesβ
Successful Return Initiation Responseβ
{
"id": "aa0e8400-e29b-41d4-a716-446655440005",
"type": "return",
"status": "draft",
"parentSaleId": "original-sale-id",
"returnType": "receipt_based",
"receiptNumber": "000123",
"totalAmount": 0,
"saleDetail": {
"items": []
},
"createdAt": "2026-02-28T10:30:00Z"
}
Refund Calculation Responseβ
{
"subtotal": 89.99,
"tax": 10.80,
"total": 100.79,
"lineItems": [
{
"lineId": "line-1",
"productName": "Blue T-Shirt",
"quantity": 1,
"unitPrice": 89.99,
"lineTotal": 89.99,
"taxAmount": 10.80,
"refundAmount": 100.79
}
],
"refundMethods": [
{
"paymentMethodId": "card-visa",
"paymentMethodName": "Visa Card",
"amount": 100.79,
"constraint": {
"maxCashRefund": 100.00,
"requireOriginalMethod": true
}
}
]
}
Exchange Settlement Responseβ
{
"returnSubtotal": 89.99,
"returnTax": 10.80,
"returnTotal": 100.79,
"saleSubtotal": 129.99,
"saleTax": 15.60,
"saleTotal": 145.59,
"netDifference": 44.80,
"settlementType": "payment_due",
"returnLines": [...],
"saleLines": [...],
"settlementMethods": [
{
"paymentMethodId": "cash",
"paymentMethodName": "Cash",
"amount": 44.80,
"direction": "in"
}
]
}
π Import to Postmanβ
Method 1: Manual Importβ
- Copy any cURL command
- Open Postman
- Click "Import" β "Raw text"
- Paste the cURL command
- Click "Continue" β "Import"
Method 2: Create Collectionβ
- Create a new collection in Postman
- Set collection variables for all
{{VARIABLE}}placeholders - Add each endpoint manually using the cURL examples above
- Configure authorization at collection level
Method 3: Use Postman Collection Formatβ
Save this as retail-return-api.postman_collection.json and import directly into Postman.
Last Updated: 2026-02-28
API Version: 1.0
Feature: 014-exchange-return (Phase 7)