{"environment":"prod-global","operations":[{"id":"account_post_382396ce4352","service":"account","method":"POST","path":"/account/auth/access","scope":"global","summary":"POST /account/auth/access","request":"Global sign up/sign in: body {email,password,lang?,referral_code?,captcha}. A verification-required result needs the email code, then login.","request_body":"json","authenticated":false},{"id":"account_get_c8048c35415f","service":"account","method":"GET","path":"/account/auth/captcha","scope":"global","summary":"GET /account/auth/captcha","request":"query: action=access|login|register|reset. Obtain and solve the one-use SHA-256 proof on the client; stdio handles this automatically.","request_body":"none","authenticated":false},{"id":"account_post_fd7dad414d18","service":"account","method":"POST","path":"/account/auth/login","scope":"global","summary":"POST /account/auth/login","request":"Body {email,password,captcha?} for global or {identifier,password,captcha} for china. MFA returns a short-lived mfa_token; complete mfa/verify.","request_body":"json","authenticated":false},{"id":"account_post_c7440cc70b22","service":"account","method":"POST","path":"/account/auth/logout","scope":"global","summary":"POST /account/auth/logout","request":"Body {refresh_token?}. Supply the refresh token to revoke its session family; Authorization alone does not revoke it. revoked=false means no active matching family. Stdio submits its retained refresh token and clears local credentials after the service acknowledges it. Existing access tokens expire normally.","request_body":"json","authenticated":false},{"id":"account_post_1f50d0b744b5","service":"account","method":"POST","path":"/account/auth/mfa/disable","scope":"global","summary":"POST /account/auth/mfa/disable","request":"No JSON body. Requires current ordinary user authentication and recent password/MFA proof. Ask the human before disabling MFA.","request_body":"none","authenticated":true},{"id":"account_post_ba8f8ea81fdd","service":"account","method":"POST","path":"/account/auth/mfa/enroll","scope":"global","summary":"POST /account/auth/mfa/enroll","request":"No JSON body. Requires a recently authenticated ordinary account. Returns otpauth_url, base32_secret and recovery_codes for private human enrollment; then verify a TOTP code.","request_body":"none","authenticated":true},{"id":"account_post_514a1dc65c76","service":"account","method":"POST","path":"/account/auth/mfa/recovery/regenerate","scope":"global","summary":"POST /account/auth/mfa/recovery/regenerate","request":"No JSON body. Requires recent authentication; invalidates old recovery codes. Deliver new recovery_codes privately to the human.","request_body":"none","authenticated":true},{"id":"account_post_dd0ba290f501","service":"account","method":"POST","path":"/account/auth/mfa/recovery/verify","scope":"global","summary":"POST /account/auth/mfa/recovery/verify","request":"Body {code,mfa_token}. Consume a recovery code supplied by the human.","request_body":"json","authenticated":false},{"id":"account_post_0b3bf926fd60","service":"account","method":"POST","path":"/account/auth/mfa/verify","scope":"global","summary":"POST /account/auth/mfa/verify","request":"Body {code,mfa_token?}. The human supplies their TOTP code. In stdio the pending login mfa_token is retained by the host.","request_body":"json","authenticated":false},{"id":"account_post_99d69df38847","service":"account","method":"POST","path":"/account/auth/password/verify","scope":"global","summary":"POST /account/auth/password/verify","request":"Body {password}. Refresh password authentication before protected security operations.","request_body":"json","authenticated":true},{"id":"account_post_262f38e312a1","service":"account","method":"POST","path":"/account/auth/refresh","scope":"global","summary":"POST /account/auth/refresh","request":"Body {refresh_token}. Rotate the ordinary session. Store both tokens securely in the MCP host; do not repeat them in chat.","request_body":"json","authenticated":false},{"id":"account_post_4dc2bd9d8d77","service":"account","method":"POST","path":"/account/auth/resend-verification","scope":"global","summary":"POST /account/auth/resend-verification","request":"Body {email,lang?}; request a new ordinary email verification message subject to existing cooldowns.","request_body":"json","authenticated":false},{"id":"account_post_66a470bb1eb5","service":"account","method":"POST","path":"/account/auth/reset-confirm","scope":"global","summary":"POST /account/auth/reset-confirm","request":"Body {email,code,new_password} for global, or {identifier,code,new_password} for china. Existing sessions are revoked; sign in again.","request_body":"json","authenticated":false},{"id":"account_post_14d316e10be1","service":"account","method":"POST","path":"/account/auth/reset-request","scope":"global","summary":"POST /account/auth/reset-request","request":"Body {email,captcha,lang?}; ask the human for the password-reset code sent by email.","request_body":"json","authenticated":false},{"id":"account_post_ba0183b8aaa6","service":"account","method":"POST","path":"/account/auth/verify-email","scope":"global","summary":"POST /account/auth/verify-email","request":"Body {email,code}. Use the code from the human, then login with the ordinary account password.","request_body":"json","authenticated":false},{"id":"account_post_4de7a964772c","service":"account","method":"POST","path":"/account/auth/verify-email-link","scope":"global","summary":"POST /account/auth/verify-email-link","request":"Body {token}. Consume the verification token from the email without opening the website.","request_body":"json","authenticated":false},{"id":"account_post_ef5ad830c3f2","service":"account","method":"POST","path":"/account/me/password","scope":"global","summary":"POST /account/me/password","request":"Body {current_password,new_password}. Change the ordinary account password; existing reauthentication and revocation rules apply.","request_body":"json","authenticated":true},{"id":"account_get_b347245dace6","service":"account","method":"GET","path":"/account/me/profile","scope":"global","summary":"GET /account/me/profile","request":"No body or query. Read the current ordinary account profile, verification and MFA status.","request_body":"none","authenticated":true},{"id":"account_post_b465c762b3fc","service":"account","method":"POST","path":"/account/me/reward-applications","scope":"global","summary":"POST /account/me/reward-applications","request":"Body {campaign_code,title,scenario,content,article_url?,previous_application_id?}. Supply an idempotency_key for one logical submission.","request_body":"json","authenticated":true},{"id":"account_get_c8c0ec0c9a7f","service":"account","method":"GET","path":"/account/me/reward-applications/{application_id}","scope":"global","summary":"GET /account/me/reward-applications/{application_id}","request":"Path {application_id}; no body. Read an application owned by the current account.","request_body":"none","authenticated":true},{"id":"account_post_e222d46eee5b","service":"account","method":"POST","path":"/account/me/reward-applications/{application_id}/withdraw","scope":"global","summary":"POST /account/me/reward-applications/{application_id}/withdraw","request":"Path {application_id}; no body. Withdraw an owned pending reward application after the human confirms.","request_body":"none","authenticated":true},{"id":"account_get_dbff9cccb0a3","service":"account","method":"GET","path":"/account/me/reward-records","scope":"global","summary":"GET /account/me/reward-records","request":"Query {cursor?,limit?}. Read the current account reward history.","request_body":"none","authenticated":true},{"id":"account_get_2b66e43458ad","service":"account","method":"GET","path":"/account/me/rewards","scope":"global","summary":"GET /account/me/rewards","request":"No body. Read active reward campaigns, referral information and eligibility from Account.","request_body":"none","authenticated":true},{"id":"account_get_e8b171bd189b","service":"account","method":"GET","path":"/account/public/capabilities","scope":"global","summary":"GET /account/public/capabilities","request":"No body or query. Read the selected site authentication mode and capabilities before registration.","request_body":"none","authenticated":false},{"id":"account_get_4e07102ed9ed","service":"account","method":"GET","path":"/account/public/email-domain-check","scope":"global","summary":"GET /account/public/email-domain-check","request":"Query {domain}. Check the registration email domain MX status.","request_body":"none","authenticated":false},{"id":"billing_get_545272293690","service":"billing","method":"GET","path":"/billing/me/credit-ledger","summary":"GET /billing/me/credit-ledger","request":"Query {kind?,cursor?,limit?}. Read posted credits ledger; follow the returned cursor.","request_body":"none","authenticated":true},{"id":"billing_get_20493b6ecb69","service":"billing","method":"GET","path":"/billing/me/credits","summary":"GET /billing/me/credits","request":"No body or query. Read authoritative posted credits and balance.","request_body":"none","authenticated":true},{"id":"billing_get_6ce37be3d985","service":"billing","method":"GET","path":"/billing/me/purchases","summary":"GET /billing/me/purchases","request":"Query {cursor?,limit?}. Read existing purchases; creating purchases and checkout is browser-only.","request_body":"none","authenticated":true},{"id":"billing_get_effa482ea9b2","service":"billing","method":"GET","path":"/billing/me/purchases/{purchase_id}","summary":"GET /billing/me/purchases/{purchase_id}","request":"Path {purchase_id}. Read an existing purchase owned by the current account.","request_body":"none","authenticated":true},{"id":"billing_get_e4ed01510b7f","service":"billing","method":"GET","path":"/billing/me/rates","summary":"GET /billing/me/rates","request":"No body or query. Read current rates for the selected site.","request_body":"none","authenticated":true},{"id":"billing_get_c5e8cbd986b4","service":"billing","method":"GET","path":"/billing/me/usage","summary":"GET /billing/me/usage","request":"Query {network_id?,path_id?,cursor?,limit?}. Read charged usage records.","request_body":"none","authenticated":true},{"id":"billing_get_9c0a05bfbc95","service":"billing","method":"GET","path":"/billing/me/usage/daily","summary":"GET /billing/me/usage/daily","request":"Query {from,to,tz?,network_id?}. from/to are Unix seconds; select a valid bounded reporting window.","request_body":"none","authenticated":true},{"id":"billing_get_dff507b7883b","service":"billing","method":"GET","path":"/billing/me/usage/links","summary":"GET /billing/me/usage/links","request":"Query {from,to,network_id?,cursor?,limit?}. from/to are Unix seconds.","request_body":"none","authenticated":true},{"id":"controller_get_093ecd917327","service":"controller","method":"GET","path":"/wwnet/app/networks","summary":"List networks owned by the current account.","request":"No request body.","request_body":"none","response":"200 OK: {\"data\":[{\"id\":\"\u003c12-character network_id\u003e\",\"name\":\"...\",\"cidr\":\"10.0.0.0/24\",\"role\":\"owner\",\"client_update_policy\":{\"mode\":\"auto\",\"window\":\"all_day\"}}],\"next_cursor\":\"\",\"has_more\":false}. Each network has one account owner; role is always owner.","authenticated":true},{"id":"controller_post_0e8322b6df12","service":"controller","method":"POST","path":"/wwnet/app/networks","summary":"Create a basic network owned by the caller.","request":"JSON body: name (required string, display name); cidr (required valid overlay CIDR). Invalid input returns 400 input.invalid_body.","request_body":"json","response":"200 OK: {\"data\":{\"id\":\"\u003c12-character network_id\u003e\",\"network_id\":\"\u003csame id\u003e\",\"name\":\"...\"}}. Also records the network.created audit event.","authenticated":true},{"id":"controller_delete_01b8c777d285","service":"controller","method":"DELETE","path":"/wwnet/app/networks/{network_id}","summary":"Delete a network and remove its devices. Audit history and existing financial records remain available.","request":"No request body.","request_body":"none","response":"200 OK: {\"data\":{\"id\":\"\u003c12-character network_id\u003e\",\"deleted\":true}}.","authenticated":true},{"id":"controller_get_b6eed5e4b2c1","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}","summary":"Read a network owned by the current account.","request":"No request body.","request_body":"none","response":"200 OK: {\"data\":{\"id\":\"\u003c12-character network_id\u003e\",\"name\":\"...\",\"cidr\":\"...\",\"status\":\"active\",\"role\":\"owner\",\"created_at\":0,\"updated_at\":0,\"cloud_relay_tuning\":{},\"client_update_policy\":{\"mode\":\"auto\",\"window\":\"all_day\"}}}. Each network has one account owner.","authenticated":true},{"id":"controller_post_a0d1b3ef3c24","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}","summary":"Update network name, Cloud Relay POP tuning or client update policy.","request":"JSON body with at least one field: name (optional string, display name); cloud_relay_tuning (optional object or null, POP probing and switching parameters; null restores defaults); client_update_policy (optional object: mode=immediate|auto|off, window=all_day|night|weekend_night, interpreted in UTC+0); reason (required when updating client_update_policy, trimmed length 1..500 bytes). Including cidr here returns 400 input.invalid_body; use the dedicated CIDR preview/apply workflow to change the subnet.","request_body":"json","response":"200 OK: the updated network in {\"data\":{...}}.","authenticated":true},{"id":"controller_get_d53ae2bbc94a","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/alerts","summary":"List network alert occurrences, distinguishing manual closure from actual recovery.","request":"Query: state=active|ended|all (default active), component=network|device|link|forward, q, from/to (Unix seconds; alerts overlapping the time window; omitted bounds are unrestricted). No pagination: returns the latest 500; has_more indicates additional omitted records.","request_body":"none","response":"200 OK: {data:{items:[{id:\"A-1\",kind,component,target_id,target_name,evidence,state,started_at,last_seen_at,ended_at,duration_seconds,closed_at,closed_display,closed_reason}],has_more}}. State follows the timestamps. Recovery time is when the condition was observed to end. Overview provides the active alert total.","authenticated":true},{"id":"controller_get_b6d85ff0ad29","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/alerts/{alert_id}","summary":"Read a network alert by ID, including recovered or manually closed alerts.","request":"Path alert_id is A-\u003csequence number\u003e; list filters and time windows do not apply.","request_body":"none","response":"200 OK: {data:{id,state,started_at,last_seen_at,ended_at,closed_at,closed_display,closed_reason,...}}, using the alert-list item fields.","authenticated":true},{"id":"controller_post_81d5aea3972a","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/alerts/{alert_id}/close","summary":"Manually close an alert. Closure stops pending-action notifications without setting ended_at or inventing recovery. Repeated requests are idempotent.","request":"JSON body: {\"reason\":\"required closure reason, 1..1000 bytes\"}.","request_body":"json","response":"200 OK: {data:{id,state,started_at,ended_at,closed_at,closed_display,closed_reason,...}}, the updated alert.","authenticated":true},{"id":"controller_get_b6815e12ca3a","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/audit","summary":"Read the latest 500 network audit events in descending order, without pagination; owner only.","request":"Query: range=live (180 seconds)|1h|12h|24h|7d; missing or invalid range defaults to 24h. Optional exact-match filters: event_kind, account_id (UUID), target_kind, target_id; q searches event_kind, target_kind, payload and actor display. limit and before are not accepted; has_more indicates omitted records and next_cursor is always empty. Only the network owner may query or filter; cross-network requests return 403 perm.forbidden.","request_body":"none","response":"200 OK: {\"data\":[{\"id\":\"\u003cuuid\u003e\",\"account_id\":\"\u003cuuid\u003e\",\"actor_display\":\"o***@example.com\",\"event_kind\":\"device.approved\",\"target_kind\":\"device\",\"target_id\":\"\u003cdevice_id\u003e\",\"target_name\":\"edge-router\",\"payload\":{...},\"created_at\":0}],\"next_cursor\":\"\",\"has_more\":false}.","authenticated":true},{"id":"controller_get_2e86a554eae5","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/audit/export","summary":"Stream network audit events as NDJSON or CSV, up to 10000 records; owner only.","request":"Query: format=json (default, NDJSON)|csv; range=live (180 seconds)|1h|12h|24h|7d (missing or invalid defaults to 24h); optional exact-match filters event_kind,account_id (UUID),target_kind,target_id; q searches event kind,target,payload and actor display; limit defaults to 5000, maximum 10000. Owner only. Maximum 6 exports per minute per client IP; excess returns 429 rate_limit.exceeded with Retry-After.","request_body":"none","response":"200 OK: format=json uses application/x-ndjson, one audit event per line with the audit-list event shape. format=csv uses text/csv and Content-Disposition: attachment; columns: id,network_id,account_id,actor_display,event_kind,target_kind,target_id,target_name,payload,created_at. Output is ordered by (created_at,id) ascending.","authenticated":true},{"id":"controller_get_0f54be72b36d","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/automation-executions/{execution_id}/output","summary":"Read or download captured stdout or stderr.","request":"Query: stream=stdout|stderr; optional download=true returns an attachment.","request_body":"none","response":"200 OK: raw output bytes, normally text/plain; charset=utf-8. download=true uses application/octet-stream and Content-Disposition. Response headers describe output range and truncation.","authenticated":true},{"id":"controller_get_3418a5d59c9d","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/automation-policies","summary":"List lifecycle scripts across all devices in the network.","request":"No request body. Use the documented path and query parameters; ordinary ownership and permissions still apply.","request_body":"none","response":"200 OK: canonical JSON data envelope.","authenticated":true},{"id":"controller_get_a7b58cb83d15","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/billing-rates","summary":"Read the three Cloud Relay rate tiers effective for this network, without the rate-card name.","request":"Path network_id.","request_body":"none","response":"200 OK: {data:{rates,bytes_per_gb}}.","authenticated":true},{"id":"controller_post_bc7ab5a0a32b","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/cidr/apply","summary":"Apply a confirmed overlay CIDR change and update affected device addresses.","request":"JSON body: cidr (required valid IPv4 CIDR); confirm (required true, otherwise 400); plan_digest (required unchanged value from the preview confirmed by the human). A stale or already-used preview returns 409 conflict.state with no changes. Preview again and obtain confirmation before retrying.","request_body":"json","response":"200 OK: {\"data\":{\"current_cidr\":\"10.1.0.0/24\",\"old_cidr\":\"10.0.0.0/24\",\"affected_count\":3,\"keep_count\":1,\"reassign_count\":2,\"devices\":[...]}}. Changes apply together or leave the configuration unchanged. An identical CIDR requiring no reassignment is a 200 no-op. Insufficient address capacity returns 400.","authenticated":true},{"id":"controller_post_defddf4d8231","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/cidr/preview","summary":"Preview planned device address changes for a new overlay CIDR without changing configuration.","request":"JSON body: cidr (required valid IPv4 CIDR). Insufficient address capacity returns 400.","request_body":"json","response":"200 OK: {\"data\":{\"affected_count\":3,\"keep_count\":1,\"reassign_count\":2,\"devices\":[{\"device_id\":\"\u003cuuid\u003e\",\"hostname\":\"t1\",\"current_ip\":\"10.0.0.10\",\"action\":\"reassign\",\"new_ip\":\"10.1.0.10\"}],\"plan_digest\":\"\u003copaque preview confirmation\u003e\"}}. action is keep or reassign. Pass the unchanged plan_digest to apply after human confirmation.","authenticated":true},{"id":"controller_get_555494791ce0","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/devices","summary":"List visible network devices in descending created_at order, excluding devices being removed or already removed.","request":"No request body.","request_body":"none","response":"200 OK: device-list envelope containing id,hostname,alias,platform,agent_version,overlay_ip,public_ip,ipv6_public_ip,status,online,is_peer_relay,direct_transports,last_seen_at. is_peer_relay indicates whether the device currently participates as a peer relay.","authenticated":true},{"id":"controller_delete_f61fcb08b862","service":"controller","method":"DELETE","path":"/wwnet/app/networks/{network_id}/devices/{device_id}","summary":"Remove a device. It disappears from the list immediately and disconnects when it receives the removal request.","request":"No request body.","request_body":"none","response":"200 OK: {\"data\":{\"id\":\"\u003cuuid\u003e\",\"removing\":true}}.","authenticated":true},{"id":"controller_post_a282ff2184e6","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}","summary":"Update device hostname, alias, overlay_ip or direct_transports in any combination.","request":"JSON body, at least one field: hostname (optional string, trimmed maximum 255 bytes; excess returns 400; empty clears); alias (optional display alias, maximum 64 characters; empty clears); overlay_ip (optional manually assigned IP, within the current overlay CIDR and unique across the network); direct_transports (optional nonempty subset of quic/tls; default [\"quic\",\"tls\"]).","request_body":"json","response":"200 OK: full device in {\"data\":{...}}, using the list-item shape with updated hostname,alias,overlay_ip,direct_transports.","authenticated":true},{"id":"controller_post_e762b3b0e770","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/approve","summary":"Approve a pending device and set its status to active.","request":"No parameters.","request_body":"none","response":"200 OK: full device in {\"data\":{...}}, using the list-item shape with status=active.","authenticated":true},{"id":"controller_get_e2376ff1c0c6","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/automation-executions","summary":"List device execution records, including snapshots retained after script deletion; filter by policy, stage or status.","request":"Optional query: policy_id, stage, status, limit (default 50, maximum 200).","request_body":"none","response":"200 OK: {data:[...],next_cursor:\"\",has_more:\u003cbool\u003e}. The server fetches limit+1 to determine whether further execution records exist.","authenticated":true},{"id":"controller_post_85992b2fc2ea","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/automation-policies","summary":"Create an inline script for a device lifecycle stage.","request":"JSON body: stage, order_index (target position; out-of-range appends), interpreter, content, timeout_mode, timeout_seconds, enabled. Device identity comes from the path.","request_body":"json","response":"200 OK: canonical JSON data envelope.","authenticated":true},{"id":"controller_post_01f0b563ee50","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/automation-policies/reorder","summary":"Atomically reorder scripts for one device lifecycle stage.","request":"JSON body: stage, policy_ids (the new order of every current script in that stage).","request_body":"json","response":"200 OK: canonical JSON data envelope.","authenticated":true},{"id":"controller_delete_81f81ab62ef5","service":"controller","method":"DELETE","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/automation-policies/{policy_id}","summary":"Delete a device script while preserving execution snapshots.","request":"No request body. Use the documented path and query parameters; ordinary ownership and permissions still apply.","request_body":"none","response":"200 OK: canonical JSON data envelope.","authenticated":true},{"id":"controller_post_a9737f4f3b48","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/automation-policies/{policy_id}","summary":"Update a device script interpreter, content, timeout or enabled state; device and lifecycle stage are immutable.","request":"JSON body: interpreter, content, timeout_mode, timeout_seconds, enabled. Use reorder to change ordering.","request_body":"json","response":"200 OK: canonical JSON data envelope.","authenticated":true},{"id":"controller_post_c4ef8a9d93e2","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/block","summary":"Reversibly block a device (status=blocked), disconnecting its network access until it is unblocked.","request":"Optional JSON body: {\"reason\":\"...\"}; reason is included in the audit event.","request_body":"json","response":"200 OK: full device in {\"data\":{...}} with status=blocked.","authenticated":true},{"id":"controller_get_157a5f8f28cf","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/details","summary":"Read the latest CPE management detail snapshot for a device.","request":"No request body.","request_body":"none","response":"200 OK: {\"data\":{\"device\":{...Device},\"snapshot\":{collected_at,reported_at,summary,forward_health}}}. forward_health.checked_at is server observation time; client_checked_at is only for diagnosing the device clock. snapshot is empty if the device has not reported.","authenticated":true},{"id":"controller_delete_3f575149e454","service":"controller","method":"DELETE","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/dynamic-domain","summary":"Unbind a dynamic domain and report its cleanup status.","request":"Availability check body: {zone,prefix}; PUT body: {zone,prefix,resolution_mode,expected_version}; DELETE body: {expected_version}. Bound names are fixed; unbind before renaming.","request_body":"json","response":"200: {data:{device_id,zone,prefix,fqdn,resolution_mode,version,sync_state,records,synced_at,last_error}}. Lists use the list envelope. Availability checks return {data:{available}}. last_error contains a service failure category, not provider response bodies.","authenticated":true},{"id":"controller_get_a9982df16b0d","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/dynamic-domain","summary":"Read a device dynamic domain; an unbound device returns unbound.","request":"Availability check body: {zone,prefix}; PUT body: {zone,prefix,resolution_mode,expected_version}; DELETE body: {expected_version}. Bound names are fixed; unbind before renaming.","request_body":"none","response":"200: {data:{device_id,zone,prefix,fqdn,resolution_mode,version,sync_state,records,synced_at,last_error}}. Lists use the list envelope. Availability checks return {data:{available}}. last_error contains a service failure category, not provider response bodies.","authenticated":true},{"id":"controller_put_b32791c65d00","service":"controller","method":"PUT","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/dynamic-domain","summary":"Bind a domain or save its resolution mode; DNS failures retain synchronization state.","request":"Availability check body: {zone,prefix}; PUT body: {zone,prefix,resolution_mode,expected_version}; DELETE body: {expected_version}. Bound names are fixed; unbind before renaming.","request_body":"json","response":"200: {data:{device_id,zone,prefix,fqdn,resolution_mode,version,sync_state,records,synced_at,last_error}}. Lists use the list envelope. Availability checks return {data:{available}}. last_error contains a service failure category, not provider response bodies.","authenticated":true},{"id":"controller_get_7d80b845ca86","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/forward-policy","summary":"Read a device forwarding policy; a device with no saved policy returns the default empty policy.","request":"No parameters.","request_body":"none","response":"200 OK: {\"data\":{device_id,policy_format,policy?,legacy_policy?,dns_hijack_enabled,content_hash,updated_at}}. Management model: {dns:{servers,default_server},l3:{include,exclude},l7:[{id,name?,match:{src_cidr?,dst_cidr?,dst_domain?},dns_server?,action}]}. dst_domain matches the union of connection and DNS-mapped domains; L3 name is only a management identifier. L3 dst_rule_set uses a self-contained HTTPS source URL (.srs or .mmdb#\u003ctwo-letter country code\u003e). policy_format=editor-v2 returns the management policy. Nonempty legacy DNS/domain policies return policy_format=execution-v1 and read-only legacy_policy; existing execution rules preserve AND conditions, AAAA, rule sets and credential bindings. L7 upstream_socks returns only {addr,username,password_set}; its password is write-only.","authenticated":true},{"id":"controller_put_54066c34f816","service":"controller","method":"PUT","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/forward-policy","summary":"Replace the complete device forwarding policy and recompute exit flags; records forward_policy.updated.","request":"JSON body: the complete ForwardPolicy, optionally with top-level expected_content_hash for optimistic concurrency (the last-read content_hash; empty if never saved; Website always supplies it). Management model: {dns:{servers,default_server},l3:{include,exclude},l7:[{id,name?,match:{src_cidr?,dst_cidr?,dst_domain?},dns_server?,action}]}. dst_domain matches the union of connection and DNS-mapped domains; L3 name is only a management identifier. L3 dst_rule_set uses a self-contained HTTPS source URL (.srs or .mmdb#\u003ctwo-letter country code\u003e). The current editor cannot replace legacy execution-v1 policies: 400 with issue code legacy_read_only prevents loss of conditions. For L7 exit upstream_socks.password, omission retains the value for the same rule ID; changes to addr,username or exit require reentry or return password_required. An explicit empty password clears it; empty addr removes the upstream and credentials. Returning password_set is accepted but ignored.","request_body":"json","response":"200 OK: the replacement document in the same shape as GET. Unchanged content returns the current document. A stale expected_content_hash returns 409 conflict.version with error.details.current_content_hash. Invalid input returns 400 input.invalid_body. Quota or configuration size limits return 429 quota.exceeded. error.details.issues is [{path,code,message}]; examples include exit_not_found,exit_platform_unsupported,password_required,payload_budget_exceeded. Size-limit errors also include error.details.affected_exit_device_ids.","authenticated":true},{"id":"controller_post_b4ae40639ab6","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/forward-policy/preview","summary":"Preview a forwarding policy without writes: validation and payload budget issues, unreachable rules, site-prefix overlaps, dangling exits, uncaptured DNS sources and unsupported platform features.","request":"JSON body: the same complete policy as replacement; expected_content_hash is ignored.","request_body":"json","response":"200 OK: {\"data\":{issues:[{path,code,message}],unreachable_rules:[{rule_id,reason}],site_route_overlaps:[{rule_id,dst_cidr,site_prefix}],dangling_exits:[{path,exit_device_id}],dns_src_not_captured:[{rule_id,src_cidr}],platform_unsupported:[{path,code,message}]}}. Nonempty issues prevent replacement; other findings are warnings.","authenticated":true},{"id":"controller_post_e9471ea30cf8","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/peer-relay/disable","summary":"Disable peer relay participation for the device.","request":"JSON body: {\"reason\":\"operation reason, 0..500 bytes\"}.","request_body":"json","response":"200 OK: full device in {\"data\":{...}}, using the list-item shape. Disabling makes is_peer_relay=false because is_peer_relay=enabled AND eligible.","authenticated":true},{"id":"controller_post_3788bdf118f2","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/peer-relay/enable","summary":"Enable peer relay participation if the device is eligible.","request":"JSON body: {\"reason\":\"operation reason, 0..500 bytes\"}.","request_body":"json","response":"200 OK: full device in {\"data\":{...}}, using the list-item shape. is_peer_relay=enabled AND eligible; it is true only when enabled and Controller determines the device is eligible.","authenticated":true},{"id":"controller_post_ba47404c3268","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/static-routes","summary":"Create an IPv4 static site route for an active Linux/OpenWrt device identified by the path.","request":"JSON body: canonical IPv4 prefix, IPv4 next_hop; optional advertise defaults to true.","request_body":"json","response":"200 OK: the new route. Advertisement remains ineffective until owner readiness succeeds.","authenticated":true},{"id":"controller_put_c11fd08bb7b2","service":"controller","method":"PUT","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/static-routes","summary":"Atomically replace all static site routes for a device.","request":"JSON body: expected_routes is the configuration list on which the draft is based (id,prefix,next_hop,advertise); routes is the complete desired list, omitting id for new entries. Both arrays are required and may be empty. Runtime fields do not participate in concurrency checks.","request_body":"json","response":"200 OK: the device route array. All changes apply together; conflicts or failures leave routes unchanged.","authenticated":true},{"id":"controller_delete_4ad9b8ac5f75","service":"controller","method":"DELETE","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/static-routes/{route_id}","summary":"Soft-delete a device static site route and immediately withdraw its effective advertisement.","request":"No parameters.","request_body":"none","response":"200 OK: deletion confirmation.","authenticated":true},{"id":"controller_post_df5578784dc3","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/static-routes/{route_id}","summary":"Update a device static site route and invalidate old readiness; the owning device is immutable.","request":"JSON body: at least one of prefix,next_hop,advertise.","request_body":"json","response":"200 OK: the updated route.","authenticated":true},{"id":"controller_post_15df027d7214","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/devices/{device_id}/unblock","summary":"Unblock a device (blocked to active), reversing block. Blocked devices do not consume device_limit; unblocking checks capacity and returns quota.exceeded if active capacity is full. Peer relay is not restored automatically and must be enabled separately. Records device.unblocked.","request":"No parameters.","request_body":"none","response":"200 OK: full device in {\"data\":{...}} with status=active.","authenticated":true},{"id":"controller_post_3b6018b33019","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/diagnostics/coverage","summary":"Summarize diagnostic report coverage. Missing reports do not imply health.","request":"The same time and filter parameters as diagnostics/query, without cursor.","request_body":"json","response":"rows,row_limit,truncated; actual report counts, peer-direction coverage, versions and missing-report counts.","authenticated":true},{"id":"controller_post_4d9167c2ce86","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/diagnostics/query","summary":"Query agent connectivity diagnostics with mandatory network/session/time scoping and stable cursor pagination.","request":"JSON body: session_id, from/to (Unix milliseconds, maximum 24 hours), optional device_id,peer_device_id,cursor.","request_body":"json","response":"At most 50 reports per page with has_more/next_cursor; payload uses the dedicated Snapshot JSON shape.","authenticated":true},{"id":"controller_get_d64c9d1f8e5d","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/diagnostics/sessions","summary":"List agent diagnostic sessions and the network mode, up to 100 sessions from the last 7 days.","request":"No request body.","request_body":"none","response":"Network mode, up to 100 sessions from the last 7 days, and current device inventory. Diagnostic protocol version null means unknown; 0 means an old client. inventory_truncated explicitly identifies incomplete inventory.","authenticated":true},{"id":"controller_get_80d6989dad34","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/dynamic-domains","summary":"List device dynamic domains.","request":"Availability check body: {zone,prefix}; PUT body: {zone,prefix,resolution_mode,expected_version}; DELETE body: {expected_version}. Bound names are fixed; unbind before renaming.","request_body":"none","response":"200: {data:{device_id,zone,prefix,fqdn,resolution_mode,version,sync_state,records,synced_at,last_error}}. Lists use the list envelope. Availability checks return {data:{available}}. last_error contains a service failure category, not provider response bodies.","authenticated":true},{"id":"controller_post_7faaf404635c","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/dynamic-domains/check","summary":"Check domain availability without reserving the name.","request":"Availability check body: {zone,prefix}; PUT body: {zone,prefix,resolution_mode,expected_version}; DELETE body: {expected_version}. Bound names are fixed; unbind before renaming.","request_body":"json","response":"200: {data:{device_id,zone,prefix,fqdn,resolution_mode,version,sync_state,records,synced_at,last_error}}. Lists use the list envelope. Availability checks return {data:{available}}. last_error contains a service failure category, not provider response bodies.","authenticated":true},{"id":"controller_get_85eca025e45e","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/events","summary":"Receive network change notifications over SSE; read the latest business snapshots through the corresponding HTTP operations.","request":"No body. Requires a user Bearer JWT and network ownership.","request_body":"none","response":"200 OK text/event-stream: baseline on connection, invalidate {resources:[...]} when network resources change, and heartbeats every 15 seconds. A bye arrives no later than access-token expiry or after 25-30 minutes; reconnect with valid credentials.","authenticated":true},{"id":"controller_get_59d715f10684","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/forward-policies","summary":"List per-device entry counts for saved forwarding policies, for the network forwarding summary.","request":"No parameters.","request_body":"none","response":"200 OK: {data:[{device_id,dns_servers,dns_rules,l3_include,l3_exclude,l7_rules,content_hash,updated_at}],next_cursor:\"\",has_more:false}. Devices without a saved policy are omitted and have an implicit all-zero default empty policy.","authenticated":true},{"id":"controller_get_3fd7627ce216","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/links","summary":"List network device links in descending (created_at,id) order, including endpoint hostnames, path policy and Credit admission state.","request":"No parameters.","request_body":"none","response":"200 OK: {\"data\":[{\"id\":\"\u003cuuid\u003e\",\"device_a_id\":\"\u003cuuid\u003e\",\"device_b_id\":\"\u003cuuid\u003e\",\"device_a_hostname\":\"...\",\"device_b_hostname\":\"...\",\"path_policy\":{...},\"cloud_relay_schedule\":null}],\"next_cursor\":\"\",\"has_more\":false}. Hostnames identify the endpoint devices. Links are managed automatically and this endpoint is read-only.","authenticated":true},{"id":"controller_get_427a01582b1d","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/links/{link_id}/path-policy","summary":"Read a link-specific transport policy, switching sensitivity and Cloud Relay availability schedule.","request":"No parameters.","request_body":"none","response":"200 OK: {\"data\":{\"policy\":{\"enabled_transports\":[\"direct\",\"peer_relay\",\"cloud_relay\"],\"strategy\":\"balanced\"},\"schedule\":null}}. schedule=null allows Cloud Relay all day. A missing link, a link outside this network or a non-UUID link_id returns 404.","authenticated":true},{"id":"controller_post_3e12bdac931f","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/links/{link_id}/path-policy","summary":"Atomically set a link-specific transport policy, switching sensitivity and Cloud Relay availability schedule.","request":"JSON body: policy (required object, at least one of direct/peer_relay/cloud_relay; strategy=conservative|balanced|aggressive); schedule (required object or null, allowed Cloud Relay time windows; null means all day).","request_body":"json","response":"200 OK: the complete updated policy. Invalid or duplicate transports, invalid strategy, timezone or time windows return 400. A missing link, a link outside this network or a non-UUID link_id returns 404.","authenticated":true},{"id":"controller_get_fcf8e224e76e","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/observe/quality/links/{link_id}","summary":"Read link quality trends with sampling state for both directions; RTT and loss use their own reliable sample counts.","request":"Path link_id must identify a Link UUID in the current network. Query from/to are Unix milliseconds; step is seconds; maximum window 7 days.","request_body":"none","response":"200 OK: {data:{trend:[{bucket_micros,src_device_id,dst_device_id,avg_rtt,avg_loss,samples,rtt_samples,loss_samples,last_observed_at_micros,last_rtt_at_micros}],directions:[{src_device_id,dst_device_id,samples,rtt_samples,loss_samples,last_observed_at_micros}],time_range:{from,to,step}}}. Both directions are described even when historical samples are absent. 100% loss can be shown when RTT is unavailable.","authenticated":true},{"id":"controller_get_a7ac218ff5b6","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/observe/quality/links/{link_id}/transitions","summary":"Read link path-transition events: aggregate markers for the whole window and the latest 100 events.","request":"Path link_id must identify a Link UUID in the current network. Query from/to are Unix milliseconds; step is aggregation seconds; maximum window 7 days.","request_body":"none","response":"200 OK: {data:{events:[...],markers:[...],time_range:{from,to,step},truncated:false,oldest_event_at_micros}}. Events are ordered by event time and transition_id descending; markers cover the entire time_range.","authenticated":true},{"id":"controller_get_95754efd49f6","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/observe/quality/overview","summary":"Read the quality-diagnostics link index with directional quality and path-transition counts.","request":"Query from/to are Unix milliseconds; maximum window 7 days. Omission defaults to the latest 24 hours.","request_body":"none","response":"200 OK: {data:{rows:[{link_id,src_device_id,dst_device_id,avg_rtt,avg_loss,max_loss,samples,last_observed_at_micros,transition_count}],truncated:false}}. Each row preserves its direction. When capacity is reached, returns the available prefix and explicitly sets truncated=true.","authenticated":true},{"id":"controller_post_6959df767fad","service":"controller","method":"POST","path":"/wwnet/app/networks/{network_id}/observe/traffic","summary":"Query traffic for a required device pair: directional one-minute bandwidth trends, top 10 flows and up to 100 five-tuple details. No DPI or application identification.","request":"JSON body: source (required device UUID for the first endpoint); destination (required device UUID for the second endpoint, different from source); from/to (optional int64 Unix milliseconds, both supplied together, maximum span 7 days).","request_body":"json","response":"200 OK: {\"data\":{\"bytes_top\":[{src_device_id,src_ip,dst_ip,dst_port,protocol,dst_device_id,path_type,transport,bytes,packets,flow_count}],\"trend\":[{bucket_micros,src_device_id,dst_device_id,bps}],\"detail\":[{src_device_id,src_ip,src_port,dst_ip,dst_port,protocol,dst_device_id,path_type,transport,bytes,packets,flow_count}],last_observed_at,time_range}}. bytes_top first combines paths by source device,source IP,destination IP,destination port,protocol, then selects the top 10; path_type/transport spanning multiple paths are mixed. trend.bps converts native one-minute traffic increments separately for each direction. detail combines both directions, sorts by traffic descending, returns at most 100 rows and does not paginate. Disabled or unreachable Observe returns 503 service.unavailable.","authenticated":true},{"id":"controller_get_9f411012ddc4","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/overview","summary":"Read a network overview at one observation time: control-plane summary, current link topology, 24-hour traffic and active alert count. Read alert details through the separate alert API.","request":"No body or query. Live data uses the latest sample within the last 6 minutes; historical range is not supported.","request_body":"none","response":"200 OK: {data:{source_states,network_status,summaries:{links,devices,channels,traffic_24h},topology:{devices,links,total_devices,total_links,returned_links,truncated,mode},alerts:{active_total}}}. Configuration read failures return 5xx. Monitoring failures retain 200, mark the corresponding source unavailable and still return configuration topology. Different paths in the two directions yield link path_type=mixed.","authenticated":true},{"id":"controller_get_f7b7ce85fbea","service":"controller","method":"GET","path":"/wwnet/app/networks/{network_id}/static-routes","summary":"List Linux/OpenWrt owner static site routes for the network.","request":"No parameters.","request_body":"none","response":"200 OK: route list including configuration values, effective_advertise/readiness reasons and owner routing_health (status,hash,reason,stale).","authenticated":true},{"id":"controller_get_cb0e9a8fc85a","service":"controller","method":"GET","path":"/wwnet/install/install.ps1","summary":"Return the Windows installation script.","request":"No parameters.","request_body":"none","response":"200 OK: script text; Cache-Control: no-store.","authenticated":false},{"id":"controller_get_2981d72093ac","service":"controller","method":"GET","path":"/wwnet/install/install.sh","summary":"Return the Linux installation script.","request":"No parameters.","request_body":"none","response":"200 OK: script text; Cache-Control: no-store.","authenticated":false},{"id":"controller_get_d9426d2322f8","service":"controller","method":"GET","path":"/wwnet/install/join/{artifact}","summary":"Generate a network-join one-liner for network_id.","request":"Path: /wwnet/install/join/{12-character-network_id}.{sh,ps1}. MCP path argument: artifact={network_id}.sh or {network_id}.ps1.","request_body":"none","response":"200 OK: script text. Invalid network_id format returns 404.","authenticated":false},{"id":"controller_get_56ef71627dd6","service":"controller","method":"GET","path":"/wwnet/install/uninstall.ps1","summary":"Return the Windows uninstall script.","request":"No parameters.","request_body":"none","response":"200 OK: script text; Cache-Control: no-store.","authenticated":false},{"id":"controller_get_0a474a275d58","service":"controller","method":"GET","path":"/wwnet/install/uninstall.sh","summary":"Return the Linux uninstall script.","request":"No parameters.","request_body":"none","response":"200 OK: script text; Cache-Control: no-store.","authenticated":false},{"id":"livechat_get_8502af3d6829","service":"livechat","method":"GET","path":"/livechat/me","summary":"GET /livechat/me","request":"No body or query. Read the current ordinary account support conversation and last_seq.","request_body":"none","authenticated":true},{"id":"livechat_post_b2e2028e0ff2","service":"livechat","method":"POST","path":"/livechat/me/attachments","summary":"POST /livechat/me/attachments","request":"Direct HTTP: multipart/form-data with a required binary file field named file. MCP call_operation: use file {name,data_base64}, no JSON body; maximum MCP attachment size is 8 MiB.","request_body":"multipart","authenticated":true},{"id":"livechat_get_c1ecbe29343c","service":"livechat","method":"GET","path":"/livechat/me/attachments/{attachment_id}","summary":"GET /livechat/me/attachments/{attachment_id}","request":"Path {attachment_id}. Download an attachment owned by the current conversation as data_base64.","request_body":"none","authenticated":true},{"id":"livechat_post_2ecdbc14e048","service":"livechat","method":"POST","path":"/livechat/me/bind","summary":"POST /livechat/me/bind","request":"No body. Bind a previously supplied visitor conversation to this ordinary account; existing service credential requirements apply.","request_body":"none","authenticated":true},{"id":"livechat_get_853cf3e9ff66","service":"livechat","method":"GET","path":"/livechat/me/events","summary":"GET /livechat/me/events","request":"Query {after_seq?}; optional last_event_id. Reads one SSE block, possibly a heartbeat; resume from the last actual event sequence.","request_body":"none","authenticated":true},{"id":"livechat_get_7c514b558130","service":"livechat","method":"GET","path":"/livechat/me/messages","summary":"GET /livechat/me/messages","request":"GET query {after_seq?,before_seq?,limit?}, choose only one cursor. POST body {client_msg_id,text?,attachment_id?}; preserve client_msg_id on retries.","request_body":"none","authenticated":true},{"id":"livechat_post_dd3779516071","service":"livechat","method":"POST","path":"/livechat/me/messages","summary":"POST /livechat/me/messages","request":"GET query {after_seq?,before_seq?,limit?}, choose only one cursor. POST body {client_msg_id,text?,attachment_id?}; preserve client_msg_id on retries.","request_body":"json","authenticated":true}]}
