Flows
A flow is a "split → PWA" link with a weight: the higher the weight, the bigger share of the split's traffic this PWA gets. The methods below add a PWA to a split, change weights and remove a PWA from a split. For how weights divide traffic, see the "Splits and Flows" section.
| Method | Address | What it does |
|---|---|---|
| GET | /pwa_flows | List flows |
| GET | /pwa_flows/{id} | One flow |
| POST | /pwa_flows | Add a PWA to a split |
| PUT | /pwa_flows/{id} | Change the weight |
| DELETE | /pwa_flows/{id} | Remove a PWA from a split |
Replace YOUR_KEY and panel.example.com with your own.
List flows
GET/pwa_flows
Flows of your teams, recently changed first. Archived ones are not shown.
| Parameter | What it filters |
|---|---|
owner_split_id | Split - only this split's flows |
archived | 1 - only archived |
page, per_page | Page and its size (100 by default) |
Always pass owner_split_id
Without owner_split_id the list includes not only split flows but also the service flows of every PWA - one per app. They must not be changed or deleted.
curl "https://panel.example.com/api/pwa_flows?owner_split_id=WQby" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->get('pwa_flows', ['query' => ['owner_split_id' => 'WQby']]);
$flows = json_decode($response->getBody(), true)['data']['pwa_flows'];
foreach ($flows as $flow) {
echo "{$flow['pwa']['name']}: weight {$flow['weight']}, {$flow['stat_installs']} installs" . PHP_EOL;
}# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.get(f"{API}/pwa_flows", headers=HEADERS, params={"owner_split_id": "WQby"}, timeout=30)
flows = response.json()["data"]["pwa_flows"]
for flow in flows:
print(flow["pwa"]["name"], "weight", flow["weight"], flow["stat_installs"], "installs")// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/pwa_flows?owner_split_id=WQby`, { headers, signal: AbortSignal.timeout(30000) });
const { data } = await response.json();
for (const flow of data.pwa_flows) {
console.log(flow.pwa.name, 'weight', flow.weight, flow.stat_installs, 'installs');
}Response (shortened)
{
"data": {
"meta": { "current_page": 1, "per_page": 100, "last_page": 1, "total": 2 },
"pwa_flows": [
{
"id": "ymqy",
"weight": 70,
"archived": false,
"stat_uniques": 1100,
"stat_installs": 290,
"stat_registrations": 70,
"stat_deposits": 15,
"stat_repeat_deposits": 5,
"team": { "id": "WZAX", "name": "Team A" },
"pwa": { "id": "WZAX", "name": "Promo EN", "active": true, "archived": false },
"created_at": "05 Oct 2026 14:22",
"updated_at": "05 Oct 2026 14:22"
}
]
}
}The stat_* counters are all-time, only for traffic that came through this flow.
Only the common errors - see "API introduction".
One flow
GET/pwa_flows/{id}
A flow with its PWA and split (owner_split).
curl https://panel.example.com/api/pwa_flows/ymqy \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->get('pwa_flows/ymqy');
$flow = json_decode($response->getBody(), true)['data']['pwa_flow'];
echo "{$flow['pwa']['name']}: weight {$flow['weight']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.get(f"{API}/pwa_flows/ymqy", headers=HEADERS, timeout=30)
flow = response.json()["data"]["pwa_flow"]
print(flow["pwa"]["name"], "weight", flow["weight"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/pwa_flows/ymqy`, { headers, signal: AbortSignal.timeout(30000) });
const { data } = await response.json();
console.log(data.pwa_flow.pwa.name, 'weight', data.pwa_flow.weight);Response (shortened)
{
"data": {
"pwa_flow": {
"id": "ymqy",
"weight": 70,
"pwa": { "id": "WZAX", "name": "Promo EN", "active": true },
"owner_split": { "id": "WQby", "name": "US test", "status": "active" },
"team": { "id": "WZAX", "name": "Team A" }
}
}
}Errors
| Code | message | When |
|---|---|---|
404 | Not found | Wrong identifier |
403 | This action is unauthorized. | The object belongs to another team |
Adding a PWA to a split
POST/pwa_flows
| Field | Required | What it is |
|---|---|---|
owner_split_id | yes | The split you add the PWA to |
pwa_id | yes | The PWA that will get the traffic. Must be in the same team as the split |
weight | no | Weight, an integer from 0. 1 by default |
curl -X POST https://panel.example.com/api/pwa_flows \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"owner_split_id": "WQby", "pwa_id": "WZAX", "weight": 70}'<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->post('pwa_flows', ['json' => [
'owner_split_id' => 'WQby',
'pwa_id' => 'WZAX',
'weight' => 70,
]]);
$flow = json_decode($response->getBody(), true)['data']['pwa_flow'];
echo "Created flow {$flow['id']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.post(f"{API}/pwa_flows", headers=HEADERS, json={
"owner_split_id": "WQby",
"pwa_id": "WZAX",
"weight": 70,
}, timeout=30)
flow = response.json()["data"]["pwa_flow"]
print("Created flow", flow["id"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/pwa_flows`, {
method: 'POST',
headers,
body: JSON.stringify({
owner_split_id: 'WQby',
pwa_id: 'WZAX',
weight: 70,
}),
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log('Created flow', data.pwa_flow.id);The response is the created flow in data.pwa_flow. If weight was not passed, it is missing from this response - the weight is 1.
Errors
| Code | message | When |
|---|---|---|
422 | The owner split id field is required. | owner_split_id is missing or does not exist |
422 | The pwa id field is required. | pwa_id is missing or does not exist |
422 | You do not have access to this resource | A split or PWA you cannot see: a customer sees only their own |
422 | This resource belongs to another team | The PWA is in a different team than the split |
422 | This PWA is already in the split. | The split already has a flow with this PWA |
422 | The weight field must be at least 0. | weight is below zero |
422 | The weight field must be an integer. | weight is not an integer |
The flow's team comes from the split - no team_id in the body. A PWA goes into a split once: a repeated request answers 422. Once the old flow with this PWA is removed (archived), the PWA can be added again.
Only enabled PWAs get traffic: if a flow's PWA is stopped or archived, its share is divided among the other flows.
Changing the weight
PUT/pwa_flows/{id}
Changes only weight (and override_offer). Any other field - pwa_id, owner_split_id, team_id and so on - is ignored: a flow cannot be moved to another split or PWA. To change the PWA in a split, remove the flow and add a new one.
curl -X PUT https://panel.example.com/api/pwa_flows/ymqy \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"weight": 50}'<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->put('pwa_flows/ymqy', ['json' => ['weight' => 50]]);
$flow = json_decode($response->getBody(), true)['data']['pwa_flow'];
echo "New weight: {$flow['weight']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.put(f"{API}/pwa_flows/ymqy", headers=HEADERS, json={"weight": 50}, timeout=30)
flow = response.json()["data"]["pwa_flow"]
print("New weight:", flow["weight"])// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/pwa_flows/ymqy`, {
method: 'PUT',
headers,
body: JSON.stringify({ weight: 50 }),
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log('New weight:', data.pwa_flow.weight);The response is the flow after the change in data.pwa_flow. New weights reach the traffic within three minutes.
Errors
| Code | message | When |
|---|---|---|
404 | Not found | Wrong identifier |
403 | This action is unauthorized. | The object belongs to another team |
422 | The weight field must be an integer. | weight is not an integer |
422 | The weight field must be at least 0. | weight is below zero |
Weight 0 instead of removal
A flow with weight 0 stays in the split but gets no traffic. That's a handy way to take a PWA out of rotation for a while and then restore the weight.
Removing a PWA from a split
DELETE/pwa_flows/{id}
Archives the flow: the PWA stops getting this split's traffic, the flow's statistics are kept. Archived flows are shown in the list with archived=1.
curl -X DELETE https://panel.example.com/api/pwa_flows/ymqy \
-H "Authorization: Bearer YOUR_KEY" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer YOUR_KEY', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->delete('pwa_flows/ymqy');
echo $response->getStatusCode() === 200 ? 'PWA removed from the split' . PHP_EOL : 'Error' . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer YOUR_KEY", "Accept": "application/json"}
response = requests.delete(f"{API}/pwa_flows/ymqy", headers=HEADERS, timeout=30)
print("PWA removed from the split" if response.ok else "Error")// Node.js 18+, no dependencies; save as script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer YOUR_KEY', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/pwa_flows/ymqy`, { method: 'DELETE', headers, signal: AbortSignal.timeout(30000) });
console.log(response.ok ? 'PWA removed from the split' : 'Error');Response: {"data": true}.
Errors
| Code | message | When |
|---|---|---|
404 | Not found | Wrong identifier |
403 | This action is unauthorized. | The object belongs to another team |