Documentation · For providers
API reference
The provider cabinet API for panels and bots: keys, domains, limited links, messages, devices and statistics, with curl examples.
For panels and bots: everything the cabinet has, except API keys and deleting the cabinet.
- Address:
https://api.flowlock.cc. - API key: cabinet → “API keys” → “Create a key”. The key (
flp_…) is shown once; keep it like a password. You can create up to 10 keys and revoke any of them. - The key goes only in the header:
Authorization: Bearer flp_…. A key in the URL is not accepted. - Bodies and responses are JSON, dates are RFC 3339 UTC. An error is an HTTP status plus
{"error": "…", "code": "…"}. A429comes with aRetry-Afterheader (seconds). - Lists come in pages:
?cursor=from the previous response'snext_cursor. - At most 120 requests per minute.
In the examples the key is in an environment variable:
export FLOWLOCK_KEY='flp_…'Provider
curl -s https://api.flowlock.cc/v1/provider -H "Authorization: Bearer $FLOWLOCK_KEY"{
"id": "k7m2x9q4bd",
"name": "My VPN",
"support_url": "https://t.me/myvpn_support",
"email": "help@myvpn.example",
"telegram": "myvpn_bot",
"status": "active",
"created_at": "2026-10-28T12:00:00Z",
"limits": { "domains": 50, "…": 0 }
}Change the contacts (an empty string removes a field, except name):
curl -s -X PATCH https://api.flowlock.cc/v1/provider -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{"support_url": "https://t.me/myvpn_support", "telegram": "@myvpn_bot"}'Fields: name: 1 to 40 characters; support_url: https://… or tg://…, up to 512; email; telegram: @name,
a t.me link or a name (5 to 32 characters of A–Z a–z 0–9 _).
Domains
# list
curl -s https://api.flowlock.cc/v1/provider/domains -H "Authorization: Bearer $FLOWLOCK_KEY"
# add: the response holds the record and the file for verification
curl -s -X POST https://api.flowlock.cc/v1/provider/domains -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{"domain": "sub.example.com"}'{
"id": "d1f2a3b4c5d6e7f8",
"domain": "sub.example.com",
"hash": "…64 hex…",
"status": "pending",
"created_at": "…",
"dns": {
"name": "_flowlock.sub.example.com",
"type": "TXT",
"value": "flowlock-provider=k7m2x9q4bd"
},
"file": {
"url": "https://sub.example.com/.well-known/flowlock-provider",
"body": "flowlock-provider=k7m2x9q4bd"
}
}# check now (up to 20 times an hour)
curl -s -X POST https://api.flowlock.cc/v1/provider/domains/d1f2a3b4c5d6e7f8/verify -H "Authorization: Bearer $FLOWLOCK_KEY"
# delete
curl -s -X DELETE https://api.flowlock.cc/v1/provider/domains/d1f2a3b4c5d6e7f8 -H "Authorization: Bearer $FLOWLOCK_KEY"Domain fields: status (pending | verified | failing | lost), method (dns | file: how it was
verified), verified_at, checked_at, error: the reason for the last failure: no_record (no record and no
file), wrong_id (the record exists, but with another ID), dns_error, http_status, fetch_error, not_public
(the domain points to a private address).
Links
# create a limited link for 3 devices
curl -s -X POST https://api.flowlock.cc/v1/provider/installs -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' \
-d '{"url": "https://sub.example.com/abc123", "limit": 3, "note": "user 1042"}'{
"id": "i8c7b6a5d4e3f2a1",
"code": "a7k2m9x4q8b3n6p5",
"link": "flowlock://install/a7k2m9x4q8b3n6p5",
"web_link": "https://flowlock.cc/i/a7k2m9x4q8b3n6p5",
"url": "https://sub.example.com/abc123",
"domain": "sub.example.com",
"limit": 3,
"count": 0,
"hwid": "",
"note": "user 1042",
"status": "active",
"created_at": "…",
"expires_at": null
}# HWID link: limit 1 and the device's HWID
curl -s -X POST https://api.flowlock.cc/v1/provider/installs -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' \
-d '{"url": "https://sub.example.com/abc123", "limit": 1, "hwid": "5f0c…"}'
# list; one link; change the limit, note, status (active | disabled), address, expiry
curl -s https://api.flowlock.cc/v1/provider/installs -H "Authorization: Bearer $FLOWLOCK_KEY"
curl -s https://api.flowlock.cc/v1/provider/installs/i8c7b6a5d4e3f2a1 -H "Authorization: Bearer $FLOWLOCK_KEY"
curl -s -X PATCH https://api.flowlock.cc/v1/provider/installs/i8c7b6a5d4e3f2a1 -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{"limit": 5}'
# the link's devices, and unlinking one
curl -s https://api.flowlock.cc/v1/provider/installs/i8c7b6a5d4e3f2a1/devices -H "Authorization: Bearer $FLOWLOCK_KEY"
curl -s -X DELETE https://api.flowlock.cc/v1/provider/installs/i8c7b6a5d4e3f2a1/devices/5f0c… \
-H "Authorization: Bearer $FLOWLOCK_KEY"
# delete the link
curl -s -X DELETE https://api.flowlock.cc/v1/provider/installs/i8c7b6a5d4e3f2a1 -H "Authorization: Bearer $FLOWLOCK_KEY"expires_at: RFC 3339 in the future, or null (no expiry). Errors: the address isn't on a verified domain:
422 domain_unverified; the limit is below the number of devices: 422 limit_below_count; with hwid the limit can
only be 1: 400 bad_request.
Messages
# notification to everyone
curl -s -X POST https://api.flowlock.cc/v1/provider/messages -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{
"kind": "notification", "mode": "optional",
"title": "Maintenance", "body": "Tonight 23:00 to 00:00 UTC service may be interrupted.",
"link": "https://myvpn.example/status",
"locales": {"ru": {"title": "Плановые работы", "body": "Сегодня с 02:00 до 03:00 МСК возможны перебои."}},
"target": {"all": true}, "expires_in": 86400}'{ "id": "m3k9q2x7a1b4c5d6", "kind": "notification", "status": "active", "…": "…" }# command: refresh the subscription on Windows and Linux on a schedule
curl -s -X POST https://api.flowlock.cc/v1/provider/messages -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{
"kind": "command", "action": "update-subscription",
"target": {"os": ["windows", "linux"]}, "send_at": "2026-11-01T09:00:00Z"}'
# command: move to a new domain (it must be verified)
curl -s -X POST https://api.flowlock.cc/v1/provider/messages -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{
"kind": "command", "action": "sub-change", "change_type": "domain", "value": "sub2.example.com",
"target": {"all": true}}'
# command: new settings for two devices
curl -s -X POST https://api.flowlock.cc/v1/provider/messages -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{
"kind": "command", "action": "set-settings",
"settings": {"ping-type": "tcp", "fragmentation-enable": true},
"target": {"devices": ["5f0c…", "9b1e…"]}}'
# command: offer an import (the user confirms)
curl -s -X POST https://api.flowlock.cc/v1/provider/messages -H "Authorization: Bearer $FLOWLOCK_KEY" \
-H 'Content-Type: application/json' -d '{
"kind": "command", "action": "import-data", "data": "https://sub.example.com/abc123",
"target": {"devices": ["5f0c…"]}}'
# list (with delivery results), one message, cancel
curl -s https://api.flowlock.cc/v1/provider/messages -H "Authorization: Bearer $FLOWLOCK_KEY"
curl -s https://api.flowlock.cc/v1/provider/messages/m3k9q2x7a1b4c5d6 -H "Authorization: Bearer $FLOWLOCK_KEY"
curl -s -X DELETE https://api.flowlock.cc/v1/provider/messages/m3k9q2x7a1b4c5d6 -H "Authorization: Bearer $FLOWLOCK_KEY"Message fields:
kind:notification|command;- notification:
title,body,link,locales(ru/en),mode(optional|force); - command:
actionand its fields:change_type+value(sub-change),data(import-data, up to 16 KB),settings(set-settings, keys from “App settings”); target:{"all": true},{"os": […]}or{"devices": […]};send_at: optional;expires_in: seconds fromsend_at(3600 to 2592000, 604800 by default).
In the list: status (scheduled | active | expired | cancelled) and counts: received, shown,
opened, done, declined, failed.
Errors: 400 bad_request (the field in field), 422 domain_unverified, 422 bad_setting (the key in key),
429 rate_limited (anti-spam, retry_after), 403 suspended.
Devices and statistics
# devices (for choosing recipients): filter by system and active in the last 30 days
curl -s 'https://api.flowlock.cc/v1/provider/devices?os=windows&active=30d' -H "Authorization: Bearer $FLOWLOCK_KEY"{
"devices": [
{
"device": "5f0c…",
"device_kind": "hwid",
"os": "windows",
"os_version": "10.0.26200",
"app": "0.4.0",
"first_seen": "…",
"last_seen": "…"
}
],
"next_cursor": "…"
}device_kind: hwid: the device's HWID (the same one your panel gets in x-hwid); alias: an alias, if the user
doesn't send the HWID. More on the “Statistics” page.
curl -s https://api.flowlock.cc/v1/provider/stats -H "Authorization: Bearer $FLOWLOCK_KEY"{
"devices": {
"total": 1520,
"active_30d": 1204,
"by_os": {
"windows": { "total": 900, "active_30d": 700 },
"macos": { "total": 420, "active_30d": 350 },
"linux": { "total": 200, "active_30d": 154 }
}
},
"months": [
{
"month": "2026-10",
"new": { "windows": 120, "macos": 40 },
"active": { "windows": 700, "macos": 350 }
}
]
}Reference data for forms
GET /v1/provider/settings-keys: the “App settings” keys with their types (bool, int, string, enum +
values), scope (app: the whole app, subscription: this subscription) and platforms.
Error codes
| code | HTTP | what it means |
|---|---|---|
unauthorized |
401 | no key, or the key is wrong or revoked |
not_provider |
404 | the account has no cabinet turned on |
feature_disabled |
403 | provider cabinets are temporarily switched off on the server |
suspended |
403 | the cabinet is suspended: messages and new links are not allowed |
bad_request |
400 | the body is malformed (field says which field) |
bad_domain |
400 | not a domain (an IP, no dot, invalid characters) |
conflict |
409 | already exists |
not_found |
404 | no such thing |
domain_unverified |
422 | the address isn't on your verified domain |
bad_setting |
422 | the key isn't in the settings list or the value doesn't fit (key) |
limit_below_count |
422 | the link's limit is below the number of its devices |
limit |
422 | a ceiling is reached: domains, links, keys, messages (upgrade: true: “Unlimited” lifts it) |
billing_off |
503 | payments for “Unlimited” are off on the server |
prepaid_max |
409 | “Unlimited” is already paid 12 months ahead |
payment_unavailable |
502 | the payment service did not answer, retry later |
rate_limited |
429 | too often, retry after retry_after |
Limits
| What | How many |
|---|---|
| API keys | 10 |
| domains | 50 |
| limited links | 5000 |
| limit of one link | 1000 devices |
| devices in statistics | 500,000 |
| messages to everyone / by system | 2 a day, at most once in 5 minutes |
| messages to selected devices | 200 an hour, up to 20 devices each |
| messages waiting for delivery | 500 |
| API requests | 120 a minute |
| manual domain checks | 20 an hour |
Need more? “Unlimited” or FlowLock support.
This page in Markdown · Every page is available as Markdown: add .md to its address without the trailing slash. The list is in llms.txt, everything at once in llms-full.txt.