curl --request GET \
--url https://api.privy.com/v1/reports/flows \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.privy.com/v1/reports/flows"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.privy.com/v1/reports/flows', 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.privy.com/v1/reports/flows",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.privy.com/v1/reports/flows"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.privy.com/v1/reports/flows")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.privy.com/v1/reports/flows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"flow_id": "Vq7L3mN9xR",
"title": "Abandoned Cart Recovery",
"status": "active",
"trigger_type": "cart_abandoned",
"channels": "multi",
"current_enrollment": {
"value": 86,
"unit": "count"
},
"stats": {
"sent": {
"value": 5200,
"unit": "count"
},
"delivered": {
"value": 5050,
"unit": "count"
},
"opened": {
"value": 2210,
"unit": "count"
},
"clicked": {
"value": 640,
"unit": "count"
},
"open_rate": {
"value": 51.4,
"unit": "percent"
},
"click_rate": {
"value": 12.7,
"unit": "percent"
},
"attributed_orders": {
"value": 210,
"unit": "count"
},
"attributed_revenue": {
"value": 9640.5,
"unit": "usd"
},
"order_rate": {
"value": 4.2,
"unit": "percent"
},
"average_order_value": {
"value": 45.91,
"unit": "usd"
},
"unsubscribed": {
"value": 34,
"unit": "count"
},
"bounced": {
"value": 61,
"unit": "count"
},
"sms_opted_out": {
"value": 12,
"unit": "count"
},
"sms_failed": {
"value": 9,
"unit": "count"
}
}
}
],
"period": {
"label": "last_30_days",
"start_date": "2026-06-21",
"end_date": "2026-07-20"
},
"pagination": {
"page": 1,
"per_page": 10,
"total_count": 4,
"total_pages": 1
}
}{
"error": {
"code": "unauthorized",
"message": "Bearer token is missing or invalid"
}
}{
"error": {
"code": "insufficient_scope",
"message": "Token does not have the required scope"
}
}{
"error": {
"code": "validation_failed",
"message": "One or more fields are invalid",
"details": [
{
"field": "email",
"message": "is required"
}
]
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded"
}
}List flow performance
Rank your flows by performance over a period — best performers first,
remaining flows by recency. Each row combines email and SMS stats
across the flow’s message steps. Use /v1/reports/flows/{flow_id}
for one flow’s report including the per-node message breakdown.
Every flow with the requested status is listed, quiet ones included —
unlike the display and campaign leaderboards nothing is dropped for
having no activity, so this doubles as a performance-annotated flow
inventory. A flow’s configuration is /v1/flows/{id}.
Required scope: reports_read
curl --request GET \
--url https://api.privy.com/v1/reports/flows \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.privy.com/v1/reports/flows"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.privy.com/v1/reports/flows', 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.privy.com/v1/reports/flows",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.privy.com/v1/reports/flows"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.privy.com/v1/reports/flows")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.privy.com/v1/reports/flows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"flow_id": "Vq7L3mN9xR",
"title": "Abandoned Cart Recovery",
"status": "active",
"trigger_type": "cart_abandoned",
"channels": "multi",
"current_enrollment": {
"value": 86,
"unit": "count"
},
"stats": {
"sent": {
"value": 5200,
"unit": "count"
},
"delivered": {
"value": 5050,
"unit": "count"
},
"opened": {
"value": 2210,
"unit": "count"
},
"clicked": {
"value": 640,
"unit": "count"
},
"open_rate": {
"value": 51.4,
"unit": "percent"
},
"click_rate": {
"value": 12.7,
"unit": "percent"
},
"attributed_orders": {
"value": 210,
"unit": "count"
},
"attributed_revenue": {
"value": 9640.5,
"unit": "usd"
},
"order_rate": {
"value": 4.2,
"unit": "percent"
},
"average_order_value": {
"value": 45.91,
"unit": "usd"
},
"unsubscribed": {
"value": 34,
"unit": "count"
},
"bounced": {
"value": 61,
"unit": "count"
},
"sms_opted_out": {
"value": 12,
"unit": "count"
},
"sms_failed": {
"value": 9,
"unit": "count"
}
}
}
],
"period": {
"label": "last_30_days",
"start_date": "2026-06-21",
"end_date": "2026-07-20"
},
"pagination": {
"page": 1,
"per_page": 10,
"total_count": 4,
"total_pages": 1
}
}{
"error": {
"code": "unauthorized",
"message": "Bearer token is missing or invalid"
}
}{
"error": {
"code": "insufficient_scope",
"message": "Token does not have the required scope"
}
}{
"error": {
"code": "validation_failed",
"message": "One or more fields are invalid",
"details": [
{
"field": "email",
"message": "is required"
}
]
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded"
}
}Authorizations
Send either an API token or an OAuth access token as
Authorization: Bearer <token>. See the Authentication page for details.
Headers
Target a subaccount instead of the main connected account.
Pass a subaccount id from get_account's subaccounts list. Only direct subaccounts
of the connected account are reachable — omit this for the main account itself.
Query Parameters
The date range to report over, resolved in the account's time zone. Pass custom together with start_date and end_date.
last_7_days, last_30_days, last_90_days, this_month, last_month, custom Inclusive start of a custom period (ISO 8601 date). Required when period=custom, ignored otherwise.
Inclusive end of a custom period (ISO 8601 date). Required when period=custom, ignored otherwise. Custom periods are capped at 366 days.
Only include flows with this status. Any other value is rejected.
draft, active, inactive Page number (starts at 1).
x >= 1Flows per page. Values above 25 are clamped to 25; omitted or invalid values default to 10.
1 <= x <= 25Response
Flows ranked by performance over the resolved period.
Show child attributes
Show child attributes
The resolved date range the report covers, inclusive, in the account's
time zone. Every label except last_month ends today, so the last day
of the range is still in progress: last_7_days is the six days before
today plus today, this_month runs from the 1st through today, and
last_month is the whole previous calendar month. Custom ranges cannot
exceed 366 days.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Was this page helpful?