# Evorxa HTTP API > The complete public API for Evorxa, a VPS and VDS host operated by ScaleBit Technologies, L.L.C.. > Everything documented here is callable with a personal access token from any > script or server. This file is generated from the same source as the human > reference at https://evorxa.com/docs. Base URL: https://api.evorxa.com/api Auth header: Authorization: Bearer Content type: application/json Human reference: https://evorxa.com/docs Contact: support@evorxa.com ## Conventions - Every request authenticates with a personal access token minted in the dashboard at /api-tokens. Tokens can be scoped to abilities (instances:read, instances:write, agent:read, agent:write, waf:read, waf:write, shield:read, shield:write, analytics:read, wallet:read) or granted full access with *. - The "Scope" line on an endpoint is the ability a token needs to call it. "all" means any valid token is accepted. - Paths shown with {braces} take a path parameter. - Errors return a JSON object with an "error" key and the status code listed under Responses. - Public catalog endpoints need no token; everything else does. ## Index - Authentication (5 endpoints) - Team (5 endpoints) - Instances (15 endpoints) - DDoS Protection (4 endpoints) - AI Agent (8 endpoints) - Shield · Firewall & Visibility (12 endpoints) - Web Application Firewall (21 endpoints) - Wallet & Billing (4 endpoints) - Announcements (1 endpoint) - Catalog (7 endpoints) - SSH Keys (3 endpoints) ## Authentication Every request authenticates with a personal access token in the Authorization header. Mint a token in your dashboard at /api-tokens. Each token can be scoped to specific abilities (instances:read, agent:write, waf:*, etc.) or granted full access. ### GET /me Get the authenticated user Returns the user record and the projects they belong to, including the caller's role in each project. next_renewal describes the next scheduled charge across the account: when it falls, what it will cost in cents, and how many instances are in it. It is null when nothing is due. Scope: all Responses: - 200: User and project list Example response: ```json { "user": { "id": 42, "email": "you@example.com", "name": "Jane Operator", "wallet_balance": 5240, "onboarded": true, "next_renewal": { "at": "2026-09-01T00:00:00+00:00", "amount_cents": 8600, "instances": 3 } }, "projects": [ { "id": 1, "name": "default", "type": "personal", "current_user_role": "owner" } ] } ``` ### PUT /me Update your profile Updates the authenticated user's personal details. All fields are optional; only the fields you send are changed. Scope: all Body: - first_name (string): Optional. First name. - last_name (string): Optional. Last name. - phone (string): Optional. Phone number in international format. Send null to clear. - country (string): Optional. Two-letter country code (ISO 3166-1 alpha-2). Responses: - 200: Updated user record. - 422: Validation failed. Example response: ```json { "user": { "id": 42, "name": "Jane Operator", "first_name": "Jane", "last_name": "Operator", "email": "you@example.com", "phone": "+15550001111", "country": "US" } } ``` ### GET /me/api-tokens List your personal API tokens Scope: all Responses: - 200: Token list (without the secret) Example response: ```json [ { "id": 7, "name": "CI deployer", "abilities": ["instances:read", "instances:write"], "last_used_at": "2026-05-14T09:12:03.000Z", "created_at": "2026-05-01T18:00:00.000Z" } ] ``` ### POST /me/api-tokens Mint a new API token Scope: all Body: - name (string, required): Human label for this token. - abilities (string[]): Optional. Defaults to ["*"] (full access). Use scoped abilities like "instances:read" to limit the token. Responses: - 201: Token created. The plaintext token is returned only once, so store it now. Example response: ```json { "id": 8, "name": "CI deployer", "abilities": ["instances:read", "instances:write"], "token": "8|aB9xKp2QrLm...", "created_at": "2026-05-14T19:42:18.000Z" } ``` ### DELETE /me/api-tokens/{id} Revoke an API token Scope: all Path parameters: - id (integer, required): Token ID. Responses: - 204: Revoked. ## Team Projects are shared workspaces. Each member holds a role. Owners can invite teammates, manage members, rename or delete the project, and take billing-impacting actions (create, upgrade, delete, or reactivate instances). Members get read and non-billing access. Invitations go out by email and expire after seven days. ### GET /projects/{project}/members List members and pending invitations Returns the project's members with their roles plus any pending invitations. Available to every member of the project. Scope: all Path parameters: - project (integer, required): Project ID. Responses: - 200: Members and pending invitations. - 404: You are not a member of this project. Example response: ```json { "project": { "id": 1, "name": "default", "type": "personal" }, "current_user_role": "owner", "members": [ { "id": 3, "role": "owner", "user": { "id": 42, "name": "Jane Operator", "email": "you@example.com" } } ], "invitations": [ { "id": 9, "email": "teammate@example.com", "role": "member", "expires_at": "2026-06-02T12:00:00.000Z", "inviter": { "id": 42, "name": "Jane Operator", "email": "you@example.com" } } ] } ``` ### POST /projects/{project}/invitations Invite a teammate by email Owner only. Sends an email invitation that the recipient accepts while signed in with the same address. The invitation expires after seven days. Scope: all Path parameters: - project (integer, required): Project ID. Body: - email (string, required): Recipient email address. - role (string): Optional. "owner" or "member". Defaults to "member". - locale (string): Optional. One of "en", "ar", "de", "nl", "it". Sets the language of the invitation link. Defaults to "en". Responses: - 201: Invitation created and emailed. - 403: Only project owners can invite teammates. - 409: An active invitation already exists for that email. - 422: That user is already a project member. Example response: ```json { "id": 9, "project_id": 1, "email": "teammate@example.com", "role": "member", "expires_at": "2026-06-02T12:00:00.000Z" } ``` ### DELETE /projects/{project}/members/{member} Remove a member Owner only. Removes a member from the project. A project must always keep at least one owner. Scope: all Path parameters: - project (integer, required): Project ID. - member (integer, required): Member record ID, from the members list. Responses: - 204: Member removed. - 403: Only project owners can remove members. - 422: Cannot remove the last remaining owner. ### PUT /projects/{project} Update project details Owner only. Updates the project's name and organization details. All fields are optional; only the fields you send are changed. Scope: all Path parameters: - project (integer, required): Project ID. Body: - name (string): Optional. Project / organization name. - tax_id (string): Optional. Tax or VAT registration number. Send null to clear. - address (string): Optional. Billing address. Send null to clear. Responses: - 200: Updated project. - 403: Only project owners can update project details. Example response: ```json { "id": 1, "name": "Acme Inc", "type": "business", "tax_id": "EG-123456789", "address": "1 Production Way, Cairo" } ``` ### DELETE /projects/{project}/invitations/{invite} Cancel a pending invitation Owner only. Cancels an invitation that has not been accepted yet. Scope: all Path parameters: - project (integer, required): Project ID. - invite (integer, required): Invitation ID. Responses: - 204: Invitation cancelled. - 403: Only project owners can cancel invitations. - 422: Accepted invitations cannot be cancelled. ## Instances Provision, list, inspect, power-cycle, reinstall, upgrade, and retire VPS instances. Pricing is debited from the wallet at creation and at upgrade. ### GET /instances List instances Scope: instances:read Query parameters: - project_id (integer): Filter to one project. Defaults to all projects you own. Responses: - 200: Array of instances. Example response: ```json [ { "id": "01H9...", "name": "api-prod-1", "hostname": "api-prod-1.server.evorxa.net", "plan": "scout-2", "status": "running", "main_ip": "203.0.113.42", "vcpu": 2, "ram": 4, "storage": 60, "billed_till": "2026-06-14T00:00:00.000Z", "ip_addresses": [...] } ] ``` ### POST /instances Provision a new instance Provisions a server on the Evorxa platform, builds the OS, debits the wallet, and returns the new record. The instance starts in 'provisioning' status and transitions to 'running' once the OS boots. Scope: instances:write Body: - name (string, required): Display name, 3-255 characters. Used in hostname derivation. - hostname (string, required): FQDN. Auto-managed reverse DNS. - project_id (integer, required): Owning project. - package_id (integer, required): Plan package ID. See /me/catalog/plans. - category (string, required): Either "standard" or "cpu-optimized". - os_template_id (integer, required): From /catalog/os-templates/{packageId}. Not required when app_slug is given, since the app selects its own base image. - os_name (string, required): Display label for the OS. Not required when app_slug is given. - billing_cycle (string, required): "monthly", "6months", or "yearly". - coupon (string): Optional coupon code. The discount is applied to the amount debited; an invalid, expired, or inapplicable code fails the request with 422 rather than charging full price. - app_slug (string): Optional. Install a one-click application on the new server. See /catalog/apps. Replaces os_template_id and os_name. - app_password (string): Optional, 8-128 characters. The sign-in password for the application. Omit it and a strong one is generated for you. Ignored by applications that create their own first account. - app_license_key (string): Required only for applications that cannot install without a licence you already own — those are marked license_required_upfront in /catalog/apps. Sent to the server during installation and never returned. - ssh_key_ids (integer[]): Optional. SSH keys to inject at build time. Responses: - 201: Instance created. Status is 'provisioning'. - 402: Insufficient wallet balance. - 422: Invalid plan, package, category, coupon, or app, the app does not fit the plan, or the plan is out of stock. ### GET /instances/{id}/app Get the application installed on an instance The one-click application installed on this server, with its sign-in details. Installation continues in the background after the instance is created, so poll this until status is 'ready'. Answers with a null app when the server was built without one. Scope: instances:read Path parameters: - id (string, required): Instance UUID. Responses: - 200: The application, or null. Example response: ```json { "app": { "slug": "wordpress", "name": "WordPress", "status": "ready", "username": "admin", "password": "generated-at-order-time", "login_url": "http://203.0.113.42/wp-admin", "installed_at": "2026-06-14T10:22:41.000Z" } } ``` ### GET /instances/{id} Get an instance Scope: instances:read Path parameters: - id (string, required): Instance UUID. Responses: - 200: Instance with live runtime state attached. - 404: Instance not found or deleted. Example response: ```json { "id": "01H9...", "name": "api-prod-1", "status": "running", "vf_state": "complete", "vf_remote_state": { "state": "running", "cpu": 12.4, "memory": { "memtotal": 4194304, "memfree": 1572864 } } } ``` ### DELETE /instances/{id} Delete an instance Scope: instances:write Path parameters: - id (string, required): Instance UUID. Body: - mode (string, required): "immediate" (delete now, prorated refund) or "end_of_cycle" (cancel renewal). Responses: - 200: Deleted, with refund amount in cents. ### POST /instances/{id}/power/{action} Power action Boot, shutdown, restart, or hard power-off. Local status moves to a transitional state and syncs from the hypervisor. Scope: instances:write Path parameters: - id (string, required): Instance UUID. - action (string, required): boot · shutdown · restart · poweroff Responses: - 200: Command dispatched. ### GET /instances/traffic Network traffic across every instance One request for the whole account. Readings are stored every five minutes and summed per bucket, so this is the cheap way to chart bandwidth without asking each instance in turn. Counters are differenced per instance before they are added together, and a reboot that resets a counter contributes nothing rather than a spike. Scope: analytics:read Query parameters: - period (string): "1h", "6h", "12h", "24h". Default "24h". Responses: - 200: Totals for the window and a continuous series. Example response: ```json { "period_minutes": 1440, "bucket_minutes": 30, "instances": 4, "totals": { "rx_bytes": 1363148599296, "tx_bytes": 559532441600 }, "points": [ { "t": "2026-08-18T00:00:00+00:00", "rx_bytes": 28374619136, "tx_bytes": 11653201920 } ] } ``` ### GET /instances/{id}/metrics CPU / RAM / disk / network Live snapshot from the hypervisor plus stored history for the requested period. Scope: analytics:read Path parameters: - id (string, required): Instance UUID. Query parameters: - period (string): "5m", "30m", "1h", "6h", "12h", "24h". Default "1h". Responses: - 200: Current snapshot + history points. Example response: ```json { "current": { "cpu_percent": 12.4, "memory_used_kb": 2621440, "memory_total_kb": 4194304, "disk_used_bytes": 12884901888, "disk_total_bytes": 64424509440, "network_rx_bytes": 938472847, "network_tx_bytes": 271938247 }, "history": [...] } ``` ### POST /instances/{id}/upgrade Upgrade plan Prorates the difference and debits the wallet. Optionally reboots immediately. Scope: instances:write Path parameters: - id (string, required): Instance UUID. Body: - package_id (integer, required): Target plan package. - reboot (boolean, required): Reboot immediately to apply. Responses: - 200: Upgraded with prorated charge. - 402: Insufficient wallet balance for the difference. ### POST /instances/{id}/reinstall Reinstall the OS or an application Wipes the disk and rebuilds it. Send either an operating system or an application slug — an application brings its own image, so the two are alternatives rather than a pair. The root password is reissued and returned by GET /instances/{id}. Scope: instances:write Path parameters: - id (string, required): Instance UUID. Body: - os_template_id (integer): Required unless app_slug is sent. From /instances/{id}/reinstall-options. - os_name (string): Required unless app_slug is sent. Display label. - app_slug (string): Required unless os_template_id is sent. From /catalog/apps. Refused with 422 if the plan is below the application's minimums. - app_password (string): Optional. Sign-in password for the application. Generated for you when omitted. - app_license_key (string): Optional. Required up front by applications that validate a licence during setup. Responses: - 200: Reinstall started. Status moves to 'provisioning'. - 422: Neither an operating system nor an application was sent, or the plan is too small for the application. ### GET /instances/{id}/reinstall-options List OS templates for reinstall Scope: instances:read Path parameters: - id (string, required): Instance UUID. Responses: - 200: OS template groups. ### POST /instances/{id}/reset-password Generate a new root/Administrator password Scope: instances:write Path parameters: - id (string, required): Instance UUID. Responses: - 200: New password returned in cleartext (once). ### POST /instances/{id}/cancel-deletion Undo a scheduled deletion Scope: instances:write Path parameters: - id (string, required): Instance UUID. Responses: - 200: Deletion cancelled, billing resumed. ### POST /instances/{id}/extend Extend the billing period early Debits one billing cycle from the wallet immediately and pushes billed_till forward by that cycle. Clears any scheduled end-of-cycle deletion. Not available while the instance is suspended. Scope: instances:write Path parameters: - id (string, required): Instance UUID. Responses: - 200: Extended; new billed_till returned. - 402: Insufficient wallet balance. - 422: Instance suspended or has no renewable cycle. ### POST /instances/{id}/reactivate Reactivate a suspended instance Debits the cycle cost from the wallet, extends billed_till, and unsuspends the instance. Scope: instances:write Path parameters: - id (string, required): Instance UUID. Responses: - 200: Reactivated. - 402: Insufficient wallet balance. ## DDoS Protection Network-layer DDoS protection is always on for supported locations: attacks are detected and scrubbed at the network edge before they reach your server. These endpoints expose the scrubbing analytics (dropped vs passed traffic), the attack history of an instance, and the per-instance attack notification settings. All of them return supported: false when the instance's IP is not on a protected prefix. ### GET /instances/{id}/ddos/overview Dropped vs passed traffic series Time series of scrubbed (dropped) and clean (passed) traffic for the instance's primary IPv4, normalized to bits per second and ordered oldest first. The live period returns roughly 12-second buckets covering the last 10 minutes; longer periods return coarser buckets. Scope: analytics:read Path parameters: - id (string, required): Instance UUID. Query parameters: - period (string): "live", "1h", "1d", "1w". Default "live". Responses: - 200: Traffic points for the period. - 502: Analytics temporarily unavailable upstream. Example response: ```json { "supported": true, "period": "live", "points": [ { "time": "2026-08-11T17:12:15Z", "drop_bps": 0, "pass_bps": 470 }, { "time": "2026-08-11T17:12:28Z", "drop_bps": 528000000, "pass_bps": 610 } ], "updated_at": "2026-08-11T17:12:28Z" } ``` ### GET /instances/{id}/ddos/incidents Attack history Past DDoS attacks against the instance's primary IPv4, newest first. Only attacks that happened after the instance was created are listed. Scope: analytics:read Path parameters: - id (string, required): Instance UUID. Query parameters: - page (integer): Page number, starting at 1. Responses: - 200: Attack list with pagination. - 502: Analytics temporarily unavailable upstream. Example response: ```json { "supported": true, "items": [ { "id": 106248, "started_at": "2026-07-12T11:15:28Z", "ended_at": "2026-07-12T11:16:13Z", "duration_seconds": 45, "vectors": ["UDP_FLOOD"], "peak": "771.49 Mbps" } ], "page": 1, "page_size": 10, "total_items": 1, "total_pages": 1, "has_next": false, "has_previous": false } ``` ### GET /instances/{id}/ddos/settings Get attack notification settings Attack notification preferences for this instance. Email alerts are enabled by default. The Discord webhook URL is write-only: reads expose only whether one is configured plus its last four characters. Scope: instances:read Path parameters: - id (string, required): Instance UUID. Responses: - 200: Current settings. Example response: ```json { "email_notifications": true, "discord_webhook_configured": true, "discord_webhook_hint": "a1b2", "affiliate_link_enabled": false, "is_affiliate": true } ``` ### PUT /instances/{id}/ddos/settings Update attack notification settings All fields are optional; only the fields you send are changed. The Discord alert is shareable by design: it shows attack details and a 'Server protected by Evorxa' link, but never your IP address. With affiliate_link_enabled (affiliates only) that link becomes your referral shortlink. Scope: instances:write Path parameters: - id (string, required): Instance UUID. Body: - email_notifications (boolean): Optional. Email the project owner when an attack is mitigated. - discord_webhook_url (string): Optional. A discord.com/api/webhooks/... URL. Send null to remove. - affiliate_link_enabled (boolean): Optional. Use your affiliate shortlink in the webhook embed. Responses: - 200: Updated settings (same shape as GET). - 422: Webhook URL is not a valid Discord webhook. ## AI Agent An LLM-driven operator with SSH into your box and a tool kit (shell, file edit, log tail). Open a chat to an instance and stream the agent's actions back. Every account includes a one-time free usage allowance; once it is spent, usage is billed to your wallet balance — but only after you explicitly enable billing. ### GET /agent/credits Get AI agent credit balance Scope: agent:read Responses: - 200: Balances in cents: the remaining free allowance, the wallet, their sum, and whether pay-as-you-go billing has been enabled. Example response: ```json { "ai_trial_cents": 500, "wallet_cents": 1240, "total_cents": 1740, "billing_enabled": false } ``` ### POST /agent/billing/enable Enable pay-as-you-go AI billing Explicit opt-in to wallet charging. Until this is called the agent only spends the free allowance and stops with a 402 (error `ai_billing_required`) when it runs out; afterwards, usage past the allowance debits the wallet. One-way: there is no disable endpoint. Idempotent. Scope: agent:write Responses: - 200: The updated balance breakdown. Example response: ```json { "ai_trial_cents": 0, "wallet_cents": 1240, "total_cents": 1240, "billing_enabled": true } ``` ### POST /instances/{id}/agent/chat Send a message to the agent Starts a new conversation or appends to an existing one. The agent may issue tool calls (shell, file edit) which run on your instance via SSH. Each tool call and token is metered. Scope: agent:write Path parameters: - id (string, required): Instance UUID. Body: - message (string, required): User prompt. - conversation_id (integer): Optional. Continue an existing conversation. Responses: - 200: Conversation record with appended assistant message + any tool transcripts. - 402: No spendable AI credit. `error` is `ai_billing_required` when the free allowance is gone and billing was never enabled (fix: POST /agent/billing/enable), or `insufficient_ai_credits` when billing is enabled but the wallet is empty (fix: top up). - 403: Instance is suspended. ### GET /instances/{id}/agent/conversations List conversations for an instance Scope: agent:read Path parameters: - id (string, required): Instance UUID. Responses: - 200: Conversations, newest first. Each carries `cost_cents`, the total that chat has consumed. ### GET /instances/{id}/agent/conversations/{convId} Get a conversation transcript Scope: agent:read Path parameters: - id (string, required): Instance UUID. - convId (integer, required): Conversation ID. Responses: - 200: Full message + tool call transcript, with the conversation's accumulated `cost_cents`. ### DELETE /instances/{id}/agent/conversations/{convId} Delete a conversation Scope: agent:write Path parameters: - id (string, required): Instance UUID. - convId (integer, required): Conversation ID. Responses: - 204: Deleted. ### GET /instances/{id}/agent/telegram Get Telegram link status Scope: agent:read Path parameters: - id (string, required): Instance UUID. Responses: - 200: Linked Telegram chat ID, or null. ### POST /instances/{id}/agent/telegram Link a Telegram chat to the agent Scope: agent:write Path parameters: - id (string, required): Instance UUID. Body: - telegram_chat_id (string, required): From the /start reply. Responses: - 200: Linked. ## Shield · Firewall & Visibility Per-IP firewall rules, RDNS, bandwidth caps, and ingress visibility (top IPs, top ASNs, country breakdown, port mix, PPS). Backed by in-house Shield nodes. ### GET /shield/ips List protected IPs Scope: shield:read Responses: - 200: IPs with rule counts, policy summary, current bandwidth. ### GET /shield/ips/{id} Get a protected IP Scope: shield:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Full IP profile: rules, policy, RDNS. ### POST /shield/ips/{id}/rules Create a firewall rule Scope: shield:write Path parameters: - id (integer, required): IP record ID. Body: - action (string, required): "allow" or "block". - protocol (string, required): "tcp", "udp", or "any". - src_cidr (string): Source CIDR. Defaults to 0.0.0.0/0. - dst_port (integer): Destination port. Null = any. Responses: - 201: Rule created and pushed to Shield nodes. ### PUT /shield/ips/{id}/rules/{ruleId} Update a firewall rule Scope: shield:write Path parameters: - id (integer, required): IP record ID. - ruleId (integer, required): Rule ID. Responses: - 200: Updated. ### DELETE /shield/ips/{id}/rules/{ruleId} Delete a firewall rule Scope: shield:write Path parameters: - id (integer, required): IP record ID. - ruleId (integer, required): Rule ID. Responses: - 204: Deleted. ### PUT /shield/ips/{id}/policy Update default policy Scope: shield:write Path parameters: - id (integer, required): IP record ID. Body: - default_in (string): "allow" or "block". - default_out (string): "allow" or "block". Responses: - 200: Policy updated. ### GET /shield/ips/{id}/bandwidth Bandwidth samples Scope: analytics:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Time-series of bps in/out. ### GET /shield/ips/{id}/visibility/top-ips Top source IPs Scope: analytics:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Ranked list of source IPs by bytes/packets. ### GET /shield/ips/{id}/visibility/top-asns Top source ASNs Scope: analytics:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Ranked ASN list. ### GET /shield/ips/{id}/visibility/countries Country breakdown Scope: analytics:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Country share of incoming traffic. ### GET /shield/ips/{id}/visibility/ports Port distribution Scope: analytics:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Most-hit destination ports. ### GET /shield/ips/{id}/visibility/pps Packets per second Scope: analytics:read Path parameters: - id (integer, required): IP record ID. Responses: - 200: Time-series PPS. ## Web Application Firewall Front an instance with a managed WAF + DDoS layer on the network edge. Each application gets its own protected hostname with an automatically issued certificate; point your own domains at it, tune enforcement, and read traffic, threat and live-event analytics. ### GET /shield/waf List WAF applications Scope: waf:read Responses: - 200: Applications with their hostnames, enforcement state and origin. ### POST /shield/waf Create a WAF application Scope: waf:write Body: - instance_id (string, required): The instance to protect (its IP becomes the origin). - name (string, required): Application label (max 100 chars). - template (string, required): http, https, or custom. - port (integer): Origin port, required when template is custom (1–65535). Responses: - 201: Application created with its protected hostname; the certificate is issued automatically. - 422: The instance is not provisioned yet, or it already has the maximum of 2 applications. Delete one to free a slot. ### GET /shield/waf/{id} Get a WAF application Scope: waf:read Path parameters: - id (integer, required): Application ID. Responses: - 200: Application with its hostnames and certificate status. ### DELETE /shield/waf/{id} Delete a WAF application Scope: waf:write Path parameters: - id (integer, required): Application ID. Responses: - 200: Deleted, along with its hostnames and certificates. ### POST /shield/waf/{id}/firewall Toggle WAF enforcement Scope: waf:write Path parameters: - id (integer, required): Application ID. Body: - enabled (boolean, required): true blocks malicious requests; false runs detect-only. Responses: - 200: Enforcement updated. ### POST /shield/waf/{id}/under-attack Toggle under-attack mode Scope: waf:write Path parameters: - id (integer, required): Application ID. Body: - enabled (boolean, required): Serve from a short-TTL edge cache during a surge so floods never reach the origin. Responses: - 200: Mode updated. ### PUT /shield/waf/{id}/settings Update protection settings Scope: waf:write Path parameters: - id (integer, required): Application ID. Body: - waf_mode (string): block, detect, or off. - challenge_enabled (boolean): Show a proof-of-work challenge under attack. - cache_enabled (boolean): Cache eligible responses at the edge. - attack_cache (boolean): Under-attack micro-cache. - rate_avg (integer): Sustained requests/sec per client. - rate_burst (integer): Burst allowance. Responses: - 200: Settings updated (they apply to every hostname of the application). ### GET /shield/waf/{id}/analytics Traffic & threat analytics Scope: waf:read Path parameters: - id (integer, required): Application ID. Query parameters: - period (string): 24h (default), 7d, or 30d. Responses: - 200: Totals, a time series, top paths/IPs/countries/ASNs, an activity heatmap and threat figures. - 502: Analytics temporarily unavailable. ### GET /shield/waf/{id}/events Live security events Scope: waf:read Path parameters: - id (integer, required): Application ID. Query parameters: - limit (integer): Max events (1–200, default 100). Responses: - 200: Recent blocked/challenged/inspected requests, newest first. ### POST /shield/waf/{id}/hostnames Add a hostname (verify DNS) Scope: waf:write Path parameters: - id (integer, required): Application ID. Body: - hostname (string, required): Domain to protect (max 255 chars). Responses: - 200: DNS is not pointing at us yet. Returns { verified: false, dns } with the CNAME record to create, and nothing is added. Create the record (DNS-only, no proxy) and call again. - 201: DNS verified; the hostname is added and its certificate is issued automatically. Returns { verified: true, hostname, dns }. ### POST /shield/waf/{id}/hostnames/{hostnameId}/retry-ssl Re-check DNS & issue certificate Scope: waf:write Path parameters: - id (integer, required): Application ID. - hostnameId (integer, required): Hostname ID. Responses: - 200: Hostname re-checked; certificate issued when DNS is in place. ### DELETE /shield/waf/{id}/hostnames/{hostnameId} Remove a hostname Scope: waf:write Path parameters: - id (integer, required): Application ID. - hostnameId (integer, required): Hostname ID. Responses: - 204: Removed. ### GET /shield/waf/{id}/live Live view (events, counters, map) Scope: waf:read Path parameters: - id (integer, required): Application ID. Query parameters: - since_id (integer): Return events after this id (cursor). Responses: - 200: Cursor events plus the current 5-minute window: live counters, per-country traffic (for the map), and top paths. ### GET /shield/waf/{id}/rules List custom rules Scope: waf:read Path parameters: - id (integer, required): Application ID. Responses: - 200: Your allow/block/challenge/log rules, in priority order. ### POST /shield/waf/{id}/rules Create a custom rule Scope: waf:write Path parameters: - id (integer, required): Application ID. Body: - name (string, required): Label (max 120 chars). - action (string, required): block | challenge | allow | log. - priority (integer): 0–100000, lower runs first (default 100). - enabled (boolean): Default true. - match (object, required): Conditions (all must match): path {op: prefix|exact|regex, value}, method [..], ip {op: in|not_in, cidrs: [..]}, header {name, op: exists|missing|equals, value?}, query {name, op, value?}. Responses: - 201: Rule created. ### PUT /shield/waf/{id}/rules/{ruleId} Update a custom rule Scope: waf:write Path parameters: - id (integer, required): Application ID. - ruleId (integer, required): Rule ID. Body: - (any create field) (object): Send only the fields to change. Responses: - 200: Rule updated. ### DELETE /shield/waf/{id}/rules/{ruleId} Delete a custom rule Scope: waf:write Path parameters: - id (integer, required): Application ID. - ruleId (integer, required): Rule ID. Responses: - 200: Removed. ### GET /shield/waf/{id}/exclusions List WAF exclusions Scope: waf:read Path parameters: - id (integer, required): Application ID. Responses: - 200: Rules you've excused for your own legitimate traffic. ### POST /shield/waf/{id}/exclusions Create a WAF exclusion Scope: waf:write Path parameters: - id (integer, required): Application ID. Body: - rule_id (string, required): The rule number to relax (digits only). - path_prefix (string): Optional. Limit to paths starting with this. - target (string): Optional. A single input to ignore (e.g. REQUEST_COOKIES); empty disables the whole rule. - note (string): Optional note (max 300 chars). - enabled (boolean): Default true. Responses: - 201: Exclusion created. ### PUT /shield/waf/{id}/exclusions/{xid} Update a WAF exclusion Scope: waf:write Path parameters: - id (integer, required): Application ID. - xid (integer, required): Exclusion ID. Body: - (any create field) (object): Send only the fields to change. Responses: - 200: Exclusion updated. ### DELETE /shield/waf/{id}/exclusions/{xid} Delete a WAF exclusion Scope: waf:write Path parameters: - id (integer, required): Application ID. - xid (integer, required): Exclusion ID. Responses: - 200: Removed. ## Wallet & Billing Balance, transactions, and invoices. Wallet is the money primitive: all paid actions (instance create/upgrade, reactivate) debit from it. Every server issues a renewal invoice 5 days before its due date; invoices are settled from wallet credit or paid directly by card or local payment. ### GET /wallet/balance Get wallet balance Scope: wallet:read Responses: - 200: Balance and a fresh exchange rate. Example response: ```json { "balance_cents": 5240, "balance_usd": 52.40 } ``` ### GET /wallet/transactions List transactions The wallet ledger, newest first. Every paid action appears here with the balance it left behind. Scope: wallet:read Query parameters: - page (integer): Page number. Default 1. - per_page (integer): Rows per page. Default 20, maximum 100. Responses: - 200: Paginated transaction ledger. ### GET /invoices List invoices Paginated invoice history, newest first. Renewal invoices are issued 5 days before an instance's due date and settle automatically from wallet credit when the balance covers them; wallet top-ups appear as already-paid invoices. Unpaid invoices past their due date carry is_overdue: true. Scope: wallet:read Query parameters: - status (string): Optional filter: "unpaid", "paid", or "cancelled". - page (integer): Page number, 20 invoices per page. Responses: - 200: Paginated invoices Example response: ```json { "data": [ { "id": 118, "number": "INV-000118", "type": "renewal", "status": "unpaid", "amount_cents": 699, "amount_formatted": "$6.99", "is_overdue": false, "due_date": "2026-08-16", "paid_at": null, "payment_method": null, "instance": { "id": "e2a6…", "name": "web-1" } } ], "current_page": 1, "last_page": 1, "total": 1 } ``` ### GET /invoices/{id} Get an invoice A single invoice with its line items and billing period. Scope: wallet:read Path parameters: - id (integer, required): Invoice id. Responses: - 200: Invoice with line items - 404: Invoice not found or not yours. Example response: ```json { "id": 118, "number": "INV-000118", "type": "renewal", "status": "unpaid", "amount_cents": 699, "due_date": "2026-08-16", "period_start": "2026-08-16T00:00:00Z", "period_end": "2026-09-16T00:00:00Z", "items": [ { "description": "Renewal: web-1 std.1 (monthly)", "item_type": "plan", "amount_cents": 599 }, { "description": "Extra IP (monthly)", "item_type": "addon", "amount_cents": 100 } ] } ``` ## Announcements Platform notices: new regions, planned maintenance, product changes. Read-only, and safe to poll on a schedule. ### GET /announcements List active announcements Returns up to 10 currently active announcements ordered by sort order, then newest first. Arabic fields are null when no translation was provided; body is null when there is no long-form text. Scope: all Responses: - 200: Active announcements Example response: ```json [ { "id": 3, "title": "New region online", "title_ar": "منطقة جديدة متاحة", "excerpt": "Deploy servers in our newest location starting today.", "excerpt_ar": "انشر خوادمك في أحدث مواقعنا ابتداءً من اليوم.", "body": "Full details about the new region...", "body_ar": null, "image_url": "https://cdn.example.com/announcements/region.png", "created_at": "2026-07-20T10:00:00Z" } ] ``` ## Catalog What you can buy. Public read-only catalog (no auth) plus an authenticated /me view. ### GET /catalog/plans Public plans (no auth) Scope: all Query parameters: - currency (string): Optional. USD (default), SAR, or EGP. Converts prices using the latest stored rate. Responses: - 200: Plan categories with per-cycle pricing in the requested currency. ### GET /catalog/regions Public regions (no auth) Scope: all Responses: - 200: Region codes, names, flags. ### GET /catalog/billing-cycles Public billing cycles (no auth) Scope: all Responses: - 200: Monthly, 6-month, yearly + their cycle discounts. ### GET /catalog/exchange-rates Public USD-based FX rates (no auth) Scope: all Responses: - 200: Rates for USD, SAR, EGP plus the fetched_at timestamp. Refreshed daily from public reference data. ### GET /me/catalog/plans Authenticated plans Scope: all Responses: - 200: Same shape as public, with personalised pricing applied. ### GET /catalog/os-templates/{packageId} OS templates for a package Scope: instances:read Path parameters: - packageId (integer, required): Plan package ID. Responses: - 200: OS groups with template variants. ### GET /catalog/apps Public one-click applications (no auth) Applications that can be installed on a new server at creation time. Pass a slug as app_slug to POST /instances, and read the resulting sign-in details from GET /instances/{id}/app. Check min_ram_mb and min_storage_gb against the plan you intend to buy, since an application larger than its plan is refused. Scope: all Responses: - 200: Available applications. Example response: ```json { "apps": [ { "slug": "wordpress", "name": "WordPress", "tagline": "The website and blogging platform behind most of the web", "category": "cms", "default_username": "admin", "generates_password": true, "setup_note": null, "license_required_upfront": false, "min_ram_mb": 2048, "min_storage_gb": 20 } ] } ``` ## SSH Keys Public SSH keys that can be injected at instance build time. Stored once, reused everywhere. ### GET /ssh-keys List SSH keys Scope: instances:read Responses: - 200: Your registered keys. ### POST /ssh-keys Add an SSH key Scope: instances:write Body: - name (string, required): Label. - public_key (string, required): OpenSSH-format public key. Responses: - 201: Created. ### DELETE /ssh-keys/{id} Delete an SSH key Scope: instances:write Path parameters: - id (integer, required): Key ID. Responses: - 204: Deleted.