Skip to content

Firewalls API ​

All endpoints require a bearer token (a login JWT or an API key) and a permission, listed with each endpoint.

A firewall is a plain IP allow-list. Each rule is an IP or CIDR plus a description - that is the whole model. There are no ports, protocols, or directions. Only source addresses on the list can reach the pod; everyone else is blocked. Firewalls attach to a pod from the pod side (at pod creation, or via firewallId on update a pod).

The firewall object ​

json
{
  "id": "6a4d5b83b7006ccae6391b07",
  "name": "web-only",
  "rules": [
    { "cidr": "203.0.113.42", "description": "Home IP" },
    { "cidr": "198.51.100.0/24", "description": "Office range" }
  ],
  "accountId": "6a4d5b83b7006ccae639100a",
  "createdAt": "2026-09-01T12:00:00Z",
  "updatedAt": "2026-09-14T10:02:00Z"
}

A rule has exactly two fields:

FieldTypeNotes
cidrstringAn IP (203.0.113.42) or CIDR (198.51.100.0/24). A bare IP is stored exactly as you sent it - we do not rewrite it to /32.
descriptionstringFree text, trimmed and truncated to 255 characters.

List firewalls ​

http
GET /api/firewalls?page=1&limit=20&search=web

Needs firewalls:read. Paginated, newest first; search matches name.

bash
curl https://cloud-api.microapps.io/api/firewalls \
  -H "Authorization: Bearer $MICROAPPS_API_KEY"

Create a firewall ​

http
POST /api/firewalls

Needs firewalls:create, $3 of credit, and a free slot under your firewall limit.

bash
curl -X POST https://cloud-api.microapps.io/api/firewalls \
  -H "Authorization: Bearer $MICROAPPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web-only",
    "rules": [
      { "cidr": "203.0.113.42", "description": "Home IP" },
      { "cidr": "198.51.100.0/24", "description": "Office range" }
    ]
  }'

Response 201 with the firewall object. Rules follow a few rules of their own:

  • At least one rule is required (400 "At least one firewall rule is required").
  • At most 15 rules per firewall (400 "A maximum of 15 firewall rules are allowed per firewall"), counted after duplicates are dropped.
  • Duplicate CIDRs are silently dropped rather than rejected.
  • An address that is neither a valid IP nor a valid CIDR is 400 "Invalid IP address: ..." or 400 "Invalid CIDR notation: ...".
  • 0.0.0.0/0 (allow everyone) is stored as 0.0.0.0.
  • name is optional (we generate one if blank), 2 to 30 characters, and unique within your account - a clash is 409 "Firewall name ... is already taken".

Firewalls themselves are free, but the create call still needs at least $3 of account credit like any other create (402 otherwise).

Describe a firewall ​

http
GET /api/firewalls/:id

Needs firewalls:read. Returns the firewall object.

Firewall stats ​

http
GET /api/firewalls/:id/stats

Needs firewalls:read.

json
{
  "blocked": 1840,
  "allowed": 502100,
  "activeSessions": 12,
  "bytesIn": 44210000,
  "bytesOut": 981200000,
  "portCount": 3
}

blocked is the number of connections the allow-list rejected. portCount is how many public ports the firewall currently guards (one per pod service it fronts) - it does not mean rules have ports. Values are cumulative.

Update a firewall ​

http
PUT /api/firewalls/:id

Needs firewalls:update.

bash
curl -X PUT https://cloud-api.microapps.io/api/firewalls/6a4d5b83b7006ccae6391b07 \
  -H "Authorization: Bearer $MICROAPPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      { "cidr": "203.0.113.42", "description": "Home IP" },
      { "cidr": "198.51.100.0/24", "description": "Office range" },
      { "cidr": "192.0.2.17", "description": "CI runner" }
    ]
  }'

The rules array replaces the existing rules wholesale, so send the full list you want, not just the new entry. It only replaces when you send a non-empty array:

You sendResult
{"rules": [ ... ]}The existing rules are replaced by these.
{"name": "new-name"}Rename only; rules untouched.
{"rules": []}Nothing happens to the rules. This is not how you clear an allow-list - a firewall with no rules cannot exist.

Response 200 with the updated firewall. Changes take effect within seconds; the pod does not restart.

Delete a firewall ​

http
DELETE /api/firewalls/:id

Needs firewalls:delete. Returns 204. If a pod is still using the firewall it returns 400 "Firewall is in use by existing pods". Every pod has a firewall at all times, so point those pods at a different one first (firewallId on update a pod) rather than expecting to detach this one.

Built for the long tail.