curl --request PATCH \
--url https://api.growi.io/api/public/v1/affiliate_sales/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"total": 123,
"subtotal": 123,
"occurred_at": "<string>",
"due_at": "<string>",
"customer_email": "jsmith@example.com",
"customer_name": "<string>",
"commission": 123,
"sub_id": "instagram"
}
'import requests
url = "https://api.growi.io/api/public/v1/affiliate_sales/{id}"
payload = {
"total": 123,
"subtotal": 123,
"occurred_at": "<string>",
"due_at": "<string>",
"customer_email": "jsmith@example.com",
"customer_name": "<string>",
"commission": 123,
"sub_id": "instagram"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
total: 123,
subtotal: 123,
occurred_at: '<string>',
due_at: '<string>',
customer_email: 'jsmith@example.com',
customer_name: '<string>',
commission: 123,
sub_id: 'instagram'
})
};
fetch('https://api.growi.io/api/public/v1/affiliate_sales/{id}', 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://api.growi.io/api/public/v1/affiliate_sales/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'total' => 123,
'subtotal' => 123,
'occurred_at' => '<string>',
'due_at' => '<string>',
'customer_email' => 'jsmith@example.com',
'customer_name' => '<string>',
'commission' => 123,
'sub_id' => 'instagram'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://api.growi.io/api/public/v1/affiliate_sales/{id}"
payload := strings.NewReader("{\n \"total\": 123,\n \"subtotal\": 123,\n \"occurred_at\": \"<string>\",\n \"due_at\": \"<string>\",\n \"customer_email\": \"jsmith@example.com\",\n \"customer_name\": \"<string>\",\n \"commission\": 123,\n \"sub_id\": \"instagram\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
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.patch("https://api.growi.io/api/public/v1/affiliate_sales/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"total\": 123,\n \"subtotal\": 123,\n \"occurred_at\": \"<string>\",\n \"due_at\": \"<string>\",\n \"customer_email\": \"jsmith@example.com\",\n \"customer_name\": \"<string>\",\n \"commission\": 123,\n \"sub_id\": \"instagram\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.growi.io/api/public/v1/affiliate_sales/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"total\": 123,\n \"subtotal\": 123,\n \"occurred_at\": \"<string>\",\n \"due_at\": \"<string>\",\n \"customer_email\": \"jsmith@example.com\",\n \"customer_name\": \"<string>\",\n \"commission\": 123,\n \"sub_id\": \"instagram\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 1947457,
"external_id": "ORD-12345",
"platform": "website",
"status": "unpaid",
"occurred_at": "2025-09-30T23:55:29.000Z",
"total": 9999,
"commission": 500,
"currency": "usd",
"sub_id": "instagram"
}
}{
"error": "<string>",
"message": "<string>"
}{
"error": "Sale not found"
}{
"error": "Only sales created via the public API (website platform) can be updated."
}Update Affiliate Sale
Updates an existing affiliate sale that was created via the Public API (website, ios, or android with manual attribution). Only sales created through POST /affiliate_sales can be updated. Commission is automatically recalculated if total is updated without specifying commission.
curl --request PATCH \
--url https://api.growi.io/api/public/v1/affiliate_sales/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"total": 123,
"subtotal": 123,
"occurred_at": "<string>",
"due_at": "<string>",
"customer_email": "jsmith@example.com",
"customer_name": "<string>",
"commission": 123,
"sub_id": "instagram"
}
'import requests
url = "https://api.growi.io/api/public/v1/affiliate_sales/{id}"
payload = {
"total": 123,
"subtotal": 123,
"occurred_at": "<string>",
"due_at": "<string>",
"customer_email": "jsmith@example.com",
"customer_name": "<string>",
"commission": 123,
"sub_id": "instagram"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
total: 123,
subtotal: 123,
occurred_at: '<string>',
due_at: '<string>',
customer_email: 'jsmith@example.com',
customer_name: '<string>',
commission: 123,
sub_id: 'instagram'
})
};
fetch('https://api.growi.io/api/public/v1/affiliate_sales/{id}', 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://api.growi.io/api/public/v1/affiliate_sales/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'total' => 123,
'subtotal' => 123,
'occurred_at' => '<string>',
'due_at' => '<string>',
'customer_email' => 'jsmith@example.com',
'customer_name' => '<string>',
'commission' => 123,
'sub_id' => 'instagram'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://api.growi.io/api/public/v1/affiliate_sales/{id}"
payload := strings.NewReader("{\n \"total\": 123,\n \"subtotal\": 123,\n \"occurred_at\": \"<string>\",\n \"due_at\": \"<string>\",\n \"customer_email\": \"jsmith@example.com\",\n \"customer_name\": \"<string>\",\n \"commission\": 123,\n \"sub_id\": \"instagram\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
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.patch("https://api.growi.io/api/public/v1/affiliate_sales/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"total\": 123,\n \"subtotal\": 123,\n \"occurred_at\": \"<string>\",\n \"due_at\": \"<string>\",\n \"customer_email\": \"jsmith@example.com\",\n \"customer_name\": \"<string>\",\n \"commission\": 123,\n \"sub_id\": \"instagram\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.growi.io/api/public/v1/affiliate_sales/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"total\": 123,\n \"subtotal\": 123,\n \"occurred_at\": \"<string>\",\n \"due_at\": \"<string>\",\n \"customer_email\": \"jsmith@example.com\",\n \"customer_name\": \"<string>\",\n \"commission\": 123,\n \"sub_id\": \"instagram\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": 1947457,
"external_id": "ORD-12345",
"platform": "website",
"status": "unpaid",
"occurred_at": "2025-09-30T23:55:29.000Z",
"total": 9999,
"commission": 500,
"currency": "usd",
"sub_id": "instagram"
}
}{
"error": "<string>",
"message": "<string>"
}{
"error": "Sale not found"
}{
"error": "Only sales created via the public API (website platform) can be updated."
}POST /affiliate_sales, including website, ios, and android with manual attribution) can be updated with this endpoint. Sales from other sources (e.g., Shopify, SDK cart attribution) cannot be modified via this API.
Important Behavior
- Commission recalculation: If you update
totalwithout providingcommission, the commission will be automatically recalculated based on campaign rules. - Audit logging: Every successful update is recorded in an internal audit log for support and debugging purposes.
- Cancelled sales: Sales with status
cancelledcannot be updated.
Path Parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
id | Integer | ID of the affiliate sale to update | Yes |
Request Body
All fields are optional. Only include the fields you want to update.| Field | Type | Description |
|---|---|---|
total | Integer | Total amount in cents. If provided without commission, commission is recalculated. |
subtotal | Integer | Subtotal in cents. |
currency | String | Currency code (e.g., usd, eur, gbp). |
occurred_at | String | When the sale occurred (ISO 8601 string or Unix timestamp). |
due_at | String | When the commission becomes due (ISO 8601 string or Unix timestamp). |
customer_email | String | Customer’s email address. Pass null to clear. |
customer_name | String | Customer’s name. Pass null to clear. |
commission | Integer | Commission amount in cents. Omit to auto-recalculate when total is updated. |
status | String | Sale status: unpaid, paid, paid_externally, or cancelled. |
sub_id | String | Custom source/sub-ID tag (e.g., instagram, youtube) for platform-level tracking. |
Request Example
Update the total and let commission recalculate:curl -X PATCH "https://api.growi.io/api/public/v1/affiliate_sales/1947458" \
-H "Authorization: Bearer YOUR_PUBLIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"total": 12999
}'
curl -X PATCH "https://api.growi.io/api/public/v1/affiliate_sales/1947458" \
-H "Authorization: Bearer YOUR_PUBLIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "paid"
}'
curl -X PATCH "https://api.growi.io/api/public/v1/affiliate_sales/1947458" \
-H "Authorization: Bearer YOUR_PUBLIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"total": 15000,
"commission": 750,
"customer_email": "updated@example.com"
}'
Response Example
200 OK - Sale updated successfully:{
"data": {
"id": 1947458,
"external_id": "ORD-12345",
"platform": "website",
"status": "unpaid",
"occurred_at": "2025-09-30T12:00:00.000Z",
"total": 12999,
"commission": 650,
"currency": "usd"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
data | Object | The updated affiliate sale object |
data.id | Integer | Unique Growi identifier for the sale |
data.external_id | String | Your order ID |
data.platform | String | Always website for sales updated via this endpoint |
data.status | String | Current status: unpaid, paid, paid_externally, cancelled, or refunded |
data.occurred_at | String | ISO 8601 timestamp when the sale occurred |
data.total | Integer | Total order amount in cents |
data.commission | Integer | Commission amount in cents |
data.currency | String | Currency code |
data.sub_id | String | Custom source/sub-ID tag captured on the sale |
Error Responses
404 Not Found - Sale does not exist or does not belong to your organization:{
"error": "Sale not found"
}
{
"error": "Only sales created via the public API (website platform) can be updated."
}
{
"error": "Cancelled sales cannot be updated."
}
Use Cases
This endpoint is useful for:- Order Adjustments: Update the total when an order is modified (e.g., partial refund, add-ons)
- Commission Corrections: Override the commission for special cases or promotions
- Status Management: Mark sales as paid or cancelled
- Data Corrections: Fix customer information or sale dates
- Sync with External Systems: Keep Growi in sync when orders are modified in your system
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
ID of the affiliate sale to update
Body
Total amount in cents. If provided without commission, commission is recalculated automatically.
Subtotal in cents
Currency code
usd, eur, gbp When the sale occurred (ISO 8601 string or Unix timestamp)
When the commission becomes due (ISO 8601 string or Unix timestamp)
Customer email address
Customer name
Commission amount in cents. Omit to auto-recalculate when total is updated.
Sale status
unpaid, paid, paid_externally, cancelled Custom source/sub-ID tag (e.g., instagram, youtube) for platform-level tracking
"instagram"
Response
Affiliate sale updated successfully
Show child attributes
Show child attributes