Skip to content

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.

MethodAddressWhat it does
GET/pwa_flowsList flows
GET/pwa_flows/{id}One flow
POST/pwa_flowsAdd 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.

ParameterWhat it filters
owner_split_idSplit - only this split's flows
archived1 - only archived
page, per_pagePage 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.

bash
curl "https://panel.example.com/api/pwa_flows?owner_split_id=WQby" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Accept: application/json"
php
<?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;
}
python
# 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")
js
// 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)
json
{
  "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).

bash
curl https://panel.example.com/api/pwa_flows/ymqy \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Accept: application/json"
php
<?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;
python
# 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"])
js
// 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)
json
{
  "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
CodemessageWhen
404Not foundWrong identifier
403This action is unauthorized.The object belongs to another team

Adding a PWA to a split ​

POST/pwa_flows

FieldRequiredWhat it is
owner_split_idyesThe split you add the PWA to
pwa_idyesThe PWA that will get the traffic. Must be in the same team as the split
weightnoWeight, an integer from 0. 1 by default
bash
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
<?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;
python
# 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"])
js
// 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
CodemessageWhen
422The owner split id field is required.owner_split_id is missing or does not exist
422The pwa id field is required.pwa_id is missing or does not exist
422You do not have access to this resourceA split or PWA you cannot see: a customer sees only their own
422This resource belongs to another teamThe PWA is in a different team than the split
422This PWA is already in the split.The split already has a flow with this PWA
422The weight field must be at least 0.weight is below zero
422The 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.

bash
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
<?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;
python
# 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"])
js
// 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
CodemessageWhen
404Not foundWrong identifier
403This action is unauthorized.The object belongs to another team
422The weight field must be an integer.weight is not an integer
422The 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.

bash
curl -X DELETE https://panel.example.com/api/pwa_flows/ymqy \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Accept: application/json"
php
<?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;
python
# 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")
js
// 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
CodemessageWhen
404Not foundWrong identifier
403This action is unauthorized.The object belongs to another team