curl --request POST \
--url https://staging.api.payluk.ng/v1/escrow/milestone/create \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'customer-id: <customer-id>' \
--data '
{
"amount": 1000000,
"purpose": "Company website build",
"description": "3-phase delivery",
"whoPays": "buyer",
"maxDelivery": 30,
"deliveryTimeline": "days",
"totalQuantity": 1,
"categoryId": "665f1b2c9a1e4d0012ab3c30",
"milestones": [
{
"title": "Design",
"description": "Figma mockups",
"amount": 300000,
"dueDate": "2026-07-15",
"customerId": "665f1b2c9a1e4d0012ab3c40"
},
{
"title": "Development",
"description": "Frontend + backend",
"amount": 500000,
"dueDate": "2026-08-15"
},
{
"title": "Deployment",
"description": "Go-live + handover",
"amount": 200000
}
]
}
'import requests
url = "https://staging.api.payluk.ng/v1/escrow/milestone/create"
payload = {
"amount": 1000000,
"purpose": "Company website build",
"description": "3-phase delivery",
"whoPays": "buyer",
"maxDelivery": 30,
"deliveryTimeline": "days",
"totalQuantity": 1,
"categoryId": "665f1b2c9a1e4d0012ab3c30",
"milestones": [
{
"title": "Design",
"description": "Figma mockups",
"amount": 300000,
"dueDate": "2026-07-15",
"customerId": "665f1b2c9a1e4d0012ab3c40"
},
{
"title": "Development",
"description": "Frontend + backend",
"amount": 500000,
"dueDate": "2026-08-15"
},
{
"title": "Deployment",
"description": "Go-live + handover",
"amount": 200000
}
]
}
headers = {
"customer-id": "<customer-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'customer-id': '<customer-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 1000000,
purpose: 'Company website build',
description: '3-phase delivery',
whoPays: 'buyer',
maxDelivery: 30,
deliveryTimeline: 'days',
totalQuantity: 1,
categoryId: '665f1b2c9a1e4d0012ab3c30',
milestones: [
{
title: 'Design',
description: 'Figma mockups',
amount: 300000,
dueDate: '2026-07-15',
customerId: '665f1b2c9a1e4d0012ab3c40'
},
{
title: 'Development',
description: 'Frontend + backend',
amount: 500000,
dueDate: '2026-08-15'
},
{title: 'Deployment', description: 'Go-live + handover', amount: 200000}
]
})
};
fetch('https://staging.api.payluk.ng/v1/escrow/milestone/create', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://staging.api.payluk.ng/v1/escrow/milestone/create",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 1000000,
'purpose' => 'Company website build',
'description' => '3-phase delivery',
'whoPays' => 'buyer',
'maxDelivery' => 30,
'deliveryTimeline' => 'days',
'totalQuantity' => 1,
'categoryId' => '665f1b2c9a1e4d0012ab3c30',
'milestones' => [
[
'title' => 'Design',
'description' => 'Figma mockups',
'amount' => 300000,
'dueDate' => '2026-07-15',
'customerId' => '665f1b2c9a1e4d0012ab3c40'
],
[
'title' => 'Development',
'description' => 'Frontend + backend',
'amount' => 500000,
'dueDate' => '2026-08-15'
],
[
'title' => 'Deployment',
'description' => 'Go-live + handover',
'amount' => 200000
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"customer-id: <customer-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://staging.api.payluk.ng/v1/escrow/milestone/create"
payload := strings.NewReader("{\n \"amount\": 1000000,\n \"purpose\": \"Company website build\",\n \"description\": \"3-phase delivery\",\n \"whoPays\": \"buyer\",\n \"maxDelivery\": 30,\n \"deliveryTimeline\": \"days\",\n \"totalQuantity\": 1,\n \"categoryId\": \"665f1b2c9a1e4d0012ab3c30\",\n \"milestones\": [\n {\n \"title\": \"Design\",\n \"description\": \"Figma mockups\",\n \"amount\": 300000,\n \"dueDate\": \"2026-07-15\",\n \"customerId\": \"665f1b2c9a1e4d0012ab3c40\"\n },\n {\n \"title\": \"Development\",\n \"description\": \"Frontend + backend\",\n \"amount\": 500000,\n \"dueDate\": \"2026-08-15\"\n },\n {\n \"title\": \"Deployment\",\n \"description\": \"Go-live + handover\",\n \"amount\": 200000\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("customer-id", "<customer-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://staging.api.payluk.ng/v1/escrow/milestone/create")
.header("customer-id", "<customer-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 1000000,\n \"purpose\": \"Company website build\",\n \"description\": \"3-phase delivery\",\n \"whoPays\": \"buyer\",\n \"maxDelivery\": 30,\n \"deliveryTimeline\": \"days\",\n \"totalQuantity\": 1,\n \"categoryId\": \"665f1b2c9a1e4d0012ab3c30\",\n \"milestones\": [\n {\n \"title\": \"Design\",\n \"description\": \"Figma mockups\",\n \"amount\": 300000,\n \"dueDate\": \"2026-07-15\",\n \"customerId\": \"665f1b2c9a1e4d0012ab3c40\"\n },\n {\n \"title\": \"Development\",\n \"description\": \"Frontend + backend\",\n \"amount\": 500000,\n \"dueDate\": \"2026-08-15\"\n },\n {\n \"title\": \"Deployment\",\n \"description\": \"Go-live + handover\",\n \"amount\": 200000\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.api.payluk.ng/v1/escrow/milestone/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["customer-id"] = '<customer-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 1000000,\n \"purpose\": \"Company website build\",\n \"description\": \"3-phase delivery\",\n \"whoPays\": \"buyer\",\n \"maxDelivery\": 30,\n \"deliveryTimeline\": \"days\",\n \"totalQuantity\": 1,\n \"categoryId\": \"665f1b2c9a1e4d0012ab3c30\",\n \"milestones\": [\n {\n \"title\": \"Design\",\n \"description\": \"Figma mockups\",\n \"amount\": 300000,\n \"dueDate\": \"2026-07-15\",\n \"customerId\": \"665f1b2c9a1e4d0012ab3c40\"\n },\n {\n \"title\": \"Development\",\n \"description\": \"Frontend + backend\",\n \"amount\": 500000,\n \"dueDate\": \"2026-08-15\"\n },\n {\n \"title\": \"Deployment\",\n \"description\": \"Go-live + handover\",\n \"amount\": 200000\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Milestone escrow created successfully",
"data": {
"id": "665f1b2c9a1e4d0012ab3c10",
"amount": 1000000,
"purpose": "Company website build",
"whoPays": "buyer",
"fee": 25000,
"paymentToken": "PY_8AB12C9D3045",
"status": "PENDING",
"state": "AWAITING_PAYMENT",
"settlementType": "MILESTONE",
"milestones": [
{
"id": "665f1b2c9a1e4d0012ab3c21",
"title": "Design",
"description": "Figma mockups",
"amount": 300000,
"dueDate": "2026-07-15",
"status": "PENDING",
"releasedAt": null
},
{
"id": "665f1b2c9a1e4d0012ab3c22",
"title": "Development",
"description": "Frontend + backend",
"amount": 500000,
"dueDate": "2026-08-15",
"status": "PENDING",
"releasedAt": null
},
{
"id": "665f1b2c9a1e4d0012ab3c23",
"title": "Deployment",
"description": "Go-live + handover",
"amount": 200000,
"dueDate": null,
"status": "PENDING",
"releasedAt": null
}
],
"createdAt": "2026-06-22T10:15:00.000Z",
"updatedAt": "2026-06-22T10:15:00.000Z"
}
}Create milestone escrow
Creates a milestone escrow (JSON body, no file upload). Rules: at least 2 milestones, every milestone amount is naira with at most two decimal places (minimum 1), and the sum of milestone amounts must equal amount. Requires the customer-id header. maxDelivery (1 to 365) and deliveryTimeline are required, and each milestone’s dueDate, when given, must be a future date.
curl --request POST \
--url https://staging.api.payluk.ng/v1/escrow/milestone/create \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'customer-id: <customer-id>' \
--data '
{
"amount": 1000000,
"purpose": "Company website build",
"description": "3-phase delivery",
"whoPays": "buyer",
"maxDelivery": 30,
"deliveryTimeline": "days",
"totalQuantity": 1,
"categoryId": "665f1b2c9a1e4d0012ab3c30",
"milestones": [
{
"title": "Design",
"description": "Figma mockups",
"amount": 300000,
"dueDate": "2026-07-15",
"customerId": "665f1b2c9a1e4d0012ab3c40"
},
{
"title": "Development",
"description": "Frontend + backend",
"amount": 500000,
"dueDate": "2026-08-15"
},
{
"title": "Deployment",
"description": "Go-live + handover",
"amount": 200000
}
]
}
'import requests
url = "https://staging.api.payluk.ng/v1/escrow/milestone/create"
payload = {
"amount": 1000000,
"purpose": "Company website build",
"description": "3-phase delivery",
"whoPays": "buyer",
"maxDelivery": 30,
"deliveryTimeline": "days",
"totalQuantity": 1,
"categoryId": "665f1b2c9a1e4d0012ab3c30",
"milestones": [
{
"title": "Design",
"description": "Figma mockups",
"amount": 300000,
"dueDate": "2026-07-15",
"customerId": "665f1b2c9a1e4d0012ab3c40"
},
{
"title": "Development",
"description": "Frontend + backend",
"amount": 500000,
"dueDate": "2026-08-15"
},
{
"title": "Deployment",
"description": "Go-live + handover",
"amount": 200000
}
]
}
headers = {
"customer-id": "<customer-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'customer-id': '<customer-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 1000000,
purpose: 'Company website build',
description: '3-phase delivery',
whoPays: 'buyer',
maxDelivery: 30,
deliveryTimeline: 'days',
totalQuantity: 1,
categoryId: '665f1b2c9a1e4d0012ab3c30',
milestones: [
{
title: 'Design',
description: 'Figma mockups',
amount: 300000,
dueDate: '2026-07-15',
customerId: '665f1b2c9a1e4d0012ab3c40'
},
{
title: 'Development',
description: 'Frontend + backend',
amount: 500000,
dueDate: '2026-08-15'
},
{title: 'Deployment', description: 'Go-live + handover', amount: 200000}
]
})
};
fetch('https://staging.api.payluk.ng/v1/escrow/milestone/create', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://staging.api.payluk.ng/v1/escrow/milestone/create",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 1000000,
'purpose' => 'Company website build',
'description' => '3-phase delivery',
'whoPays' => 'buyer',
'maxDelivery' => 30,
'deliveryTimeline' => 'days',
'totalQuantity' => 1,
'categoryId' => '665f1b2c9a1e4d0012ab3c30',
'milestones' => [
[
'title' => 'Design',
'description' => 'Figma mockups',
'amount' => 300000,
'dueDate' => '2026-07-15',
'customerId' => '665f1b2c9a1e4d0012ab3c40'
],
[
'title' => 'Development',
'description' => 'Frontend + backend',
'amount' => 500000,
'dueDate' => '2026-08-15'
],
[
'title' => 'Deployment',
'description' => 'Go-live + handover',
'amount' => 200000
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"customer-id: <customer-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://staging.api.payluk.ng/v1/escrow/milestone/create"
payload := strings.NewReader("{\n \"amount\": 1000000,\n \"purpose\": \"Company website build\",\n \"description\": \"3-phase delivery\",\n \"whoPays\": \"buyer\",\n \"maxDelivery\": 30,\n \"deliveryTimeline\": \"days\",\n \"totalQuantity\": 1,\n \"categoryId\": \"665f1b2c9a1e4d0012ab3c30\",\n \"milestones\": [\n {\n \"title\": \"Design\",\n \"description\": \"Figma mockups\",\n \"amount\": 300000,\n \"dueDate\": \"2026-07-15\",\n \"customerId\": \"665f1b2c9a1e4d0012ab3c40\"\n },\n {\n \"title\": \"Development\",\n \"description\": \"Frontend + backend\",\n \"amount\": 500000,\n \"dueDate\": \"2026-08-15\"\n },\n {\n \"title\": \"Deployment\",\n \"description\": \"Go-live + handover\",\n \"amount\": 200000\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("customer-id", "<customer-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://staging.api.payluk.ng/v1/escrow/milestone/create")
.header("customer-id", "<customer-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 1000000,\n \"purpose\": \"Company website build\",\n \"description\": \"3-phase delivery\",\n \"whoPays\": \"buyer\",\n \"maxDelivery\": 30,\n \"deliveryTimeline\": \"days\",\n \"totalQuantity\": 1,\n \"categoryId\": \"665f1b2c9a1e4d0012ab3c30\",\n \"milestones\": [\n {\n \"title\": \"Design\",\n \"description\": \"Figma mockups\",\n \"amount\": 300000,\n \"dueDate\": \"2026-07-15\",\n \"customerId\": \"665f1b2c9a1e4d0012ab3c40\"\n },\n {\n \"title\": \"Development\",\n \"description\": \"Frontend + backend\",\n \"amount\": 500000,\n \"dueDate\": \"2026-08-15\"\n },\n {\n \"title\": \"Deployment\",\n \"description\": \"Go-live + handover\",\n \"amount\": 200000\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.api.payluk.ng/v1/escrow/milestone/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["customer-id"] = '<customer-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 1000000,\n \"purpose\": \"Company website build\",\n \"description\": \"3-phase delivery\",\n \"whoPays\": \"buyer\",\n \"maxDelivery\": 30,\n \"deliveryTimeline\": \"days\",\n \"totalQuantity\": 1,\n \"categoryId\": \"665f1b2c9a1e4d0012ab3c30\",\n \"milestones\": [\n {\n \"title\": \"Design\",\n \"description\": \"Figma mockups\",\n \"amount\": 300000,\n \"dueDate\": \"2026-07-15\",\n \"customerId\": \"665f1b2c9a1e4d0012ab3c40\"\n },\n {\n \"title\": \"Development\",\n \"description\": \"Frontend + backend\",\n \"amount\": 500000,\n \"dueDate\": \"2026-08-15\"\n },\n {\n \"title\": \"Deployment\",\n \"description\": \"Go-live + handover\",\n \"amount\": 200000\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Milestone escrow created successfully",
"data": {
"id": "665f1b2c9a1e4d0012ab3c10",
"amount": 1000000,
"purpose": "Company website build",
"whoPays": "buyer",
"fee": 25000,
"paymentToken": "PY_8AB12C9D3045",
"status": "PENDING",
"state": "AWAITING_PAYMENT",
"settlementType": "MILESTONE",
"milestones": [
{
"id": "665f1b2c9a1e4d0012ab3c21",
"title": "Design",
"description": "Figma mockups",
"amount": 300000,
"dueDate": "2026-07-15",
"status": "PENDING",
"releasedAt": null
},
{
"id": "665f1b2c9a1e4d0012ab3c22",
"title": "Development",
"description": "Frontend + backend",
"amount": 500000,
"dueDate": "2026-08-15",
"status": "PENDING",
"releasedAt": null
},
{
"id": "665f1b2c9a1e4d0012ab3c23",
"title": "Deployment",
"description": "Go-live + handover",
"amount": 200000,
"dueDate": null,
"status": "PENDING",
"releasedAt": null
}
],
"createdAt": "2026-06-22T10:15:00.000Z",
"updatedAt": "2026-06-22T10:15:00.000Z"
}
}Authorizations
Your secret key as a Bearer token. The key prefix selects the environment: sk_test_... (staging) or sk_live_... (production); a key on the wrong host is refused with 403 Unauthorized Access. Each key is limited to 10 requests per minute (429 beyond that) and, on production, to the IP addresses on your dashboard allowlist when one is configured.
Headers
The merchant customer this request acts on behalf of.
Body
Naira with at most two decimal places. Minimum 1000. Must equal the sum of the milestone amounts.
x >= 10001000000
"Company website build"
For milestone escrows the buyer always pays the Payluk fee; buyer is the only accepted value.
buyer "buyer"
Length of the delivery window, in deliveryTimeline units. Required. Between 1 and 365.
1 <= x <= 36530
Unit for maxDelivery. Required.
minutes, hours, days "days"
At least 2 milestones whose amounts sum to amount.
2Show child attributes
Show child attributes
"3-phase delivery"
1
"665f1b2c9a1e4d0012ab3c30"