{
  "openapi": "3.0.3",
  "info": {
    "title": "playit Premium API",
    "version": "0.2.0",
    "description": "Manage firewalls and tunnel firewall assignments with a playit Premium API key. Each operation needs an API key scope with the same name as its path. All requests change your live account."
  },
  "servers": [
    {
      "url": "https://api.playit.gg",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "paths": {
    "/firewalls/update": {
      "post": {
        "tags": [
          "Firewalls"
        ],
        "summary": "Replace all firewall rules",
        "operationId": "updateFirewall",
        "description": "Replaces all rules in a firewall. Rules that are not in the request are deleted. The change applies to every tunnel that uses the firewall.\n\nRequired scope: `/firewalls/update`.",
        "x-playit-scope": "/firewalls/update",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FirewallUpdateRequest"
              },
              "example": {
                "firewall_id": "11111111-1111-4111-8111-111111111111",
                "rules": "192.0.2.10/32 allow\n0.0.0.0/0 deny\n::/0 deny\n"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The operation completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmptySuccess"
                },
                "example": {
                  "status": "success",
                  "data": null
                }
              }
            }
          },
          "400": {
            "description": "The operation failed, or the request was not valid. Check `status` and `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FirewallUpdateFailure"
                    },
                    {
                      "$ref": "#/components/schemas/ApiError"
                    }
                  ]
                },
                "example": {
                  "status": "fail",
                  "data": {
                    "error": "InvalidRule",
                    "details": {
                      "line_number": 1,
                      "reason": "InvalidAction"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/AuthenticationError"
          },
          "403": {
            "$ref": "#/components/responses/OriginRejected"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/firewall/patch": {
      "post": {
        "tags": [
          "Firewalls"
        ],
        "summary": "Allow or deny networks",
        "operationId": "patchFirewall",
        "description": "Allows or denies specific networks and keeps all other rules. The API applies all changes together, or none of them. If two changes overlap but one allows and one denies, the request fails with `ConflictingChanges`.\n\nRequired scope: `/firewall/patch`.",
        "x-playit-scope": "/firewall/patch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FirewallPatchRequest"
              },
              "example": {
                "firewall_id": "11111111-1111-4111-8111-111111111111",
                "changes": [
                  {
                    "network": "192.0.2.10",
                    "allowed": true
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The operation completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FirewallPatchSuccess"
                },
                "example": {
                  "status": "success",
                  "data": {
                    "rules": "192.0.2.10/32 allow\n0.0.0.0/0 deny\n::/0 deny\n",
                    "rule_count": 1,
                    "changed": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "The operation failed, or the request was not valid. Check `status` and `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FirewallPatchFailure"
                    },
                    {
                      "$ref": "#/components/schemas/ApiError"
                    }
                  ]
                },
                "example": {
                  "status": "fail",
                  "data": "ConflictingChanges"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/AuthenticationError"
          },
          "403": {
            "$ref": "#/components/responses/OriginRejected"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/tunnels/firewall/assign": {
      "post": {
        "tags": [
          "Tunnels"
        ],
        "summary": "Assign or remove a tunnel firewall",
        "operationId": "assignTunnelFirewall",
        "description": "Assigns a firewall to a tunnel. To remove the firewall, set `firewall_id` to `null` or leave it out. If the firewall does not exist, the API can return HTTP 500.\n\nRequired scope: `/tunnels/firewall/assign`.",
        "x-playit-scope": "/tunnels/firewall/assign",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TunnelFirewallAssignRequest"
              },
              "example": {
                "tunnel_id": "22222222-2222-4222-8222-222222222222",
                "firewall_id": "11111111-1111-4111-8111-111111111111"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The operation completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmptySuccess"
                },
                "example": {
                  "status": "success",
                  "data": null
                }
              }
            }
          },
          "400": {
            "description": "The operation failed, or the request was not valid. Check `status` and `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/TunnelFirewallAssignFailure"
                    },
                    {
                      "$ref": "#/components/schemas/ApiError"
                    }
                  ]
                },
                "example": {
                  "status": "fail",
                  "data": "TunnelNotFound"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/AuthenticationError"
          },
          "403": {
            "$ref": "#/components/responses/OriginRejected"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Enter `Api-Key YOUR_API_KEY`. The key needs the scope for the operation."
      }
    },
    "schemas": {
      "FirewallUpdateRequest": {
        "type": "object",
        "properties": {
          "firewall_id": {
            "type": "string",
            "description": "The firewall to change.",
            "format": "uuid"
          },
          "rules": {
            "type": "string",
            "description": "All rules, one per line, in the format `TARGET ACTION`. The first matching rule decides the result. If no rule matches, the traffic is denied. IPv6 prefixes must be `/64` or shorter. Blank lines and lines that start with `#` are ignored."
          }
        },
        "required": [
          "firewall_id",
          "rules"
        ]
      },
      "FirewallChange": {
        "type": "object",
        "properties": {
          "network": {
            "type": "string",
            "description": "An IPv4 address, an IPv4 network (`/0` to `/32`), or an IPv6 network (`/0` to `/64`). Single IPv6 addresses, ASN targets, and country targets are not accepted."
          },
          "allowed": {
            "type": "boolean",
            "description": "`true` allows the network. `false` denies it."
          }
        },
        "required": [
          "network",
          "allowed"
        ]
      },
      "FirewallPatchRequest": {
        "type": "object",
        "properties": {
          "firewall_id": {
            "type": "string",
            "description": "The firewall to change.",
            "format": "uuid"
          },
          "changes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FirewallChange"
            },
            "description": "The networks to change. An empty array returns the current rules and changes nothing."
          }
        },
        "required": [
          "firewall_id",
          "changes"
        ]
      },
      "FirewallPatchResult": {
        "type": "object",
        "properties": {
          "rules": {
            "type": "string",
            "description": "All rules in the firewall after the change."
          },
          "rule_count": {
            "type": "integer",
            "minimum": 0,
            "maximum": 4294967295,
            "description": "The number of rules that count toward your plan limit. Default rules do not count. An ASN or country rule can count two times."
          },
          "changed": {
            "type": "boolean",
            "description": "`true` if the firewall changed."
          }
        },
        "required": [
          "rules",
          "rule_count",
          "changed"
        ]
      },
      "TunnelFirewallAssignRequest": {
        "type": "object",
        "properties": {
          "tunnel_id": {
            "type": "string",
            "description": "The tunnel to change.",
            "format": "uuid"
          },
          "firewall_id": {
            "type": "string",
            "description": "The firewall to assign. Set to `null`, or leave out, to remove the firewall.",
            "format": "uuid",
            "nullable": true
          }
        },
        "required": [
          "tunnel_id"
        ]
      },
      "EmptySuccess": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ]
          },
          "data": {
            "type": "object",
            "nullable": true,
            "enum": [
              null
            ],
            "description": "Always `null`."
          }
        },
        "required": [
          "status",
          "data"
        ]
      },
      "FirewallPatchSuccess": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/FirewallPatchResult"
          }
        },
        "required": [
          "status",
          "data"
        ]
      },
      "InvalidFirewallRule": {
        "type": "object",
        "properties": {
          "line_number": {
            "type": "integer",
            "minimum": 0,
            "maximum": 4294967295,
            "description": "The line with the error. Line numbers start at 1. 0 means the error is not on one specific line."
          },
          "reason": {
            "type": "string",
            "enum": [
              "InvalidNetwork",
              "InvalidNetworkPrefix",
              "InvalidNetworkIp",
              "InvalidAsn",
              "InvalidCountry",
              "InvalidAction",
              "Ipv6NetworkTooSpecific",
              "TooManyLines"
            ]
          }
        },
        "required": [
          "line_number",
          "reason"
        ]
      },
      "FirewallUpdateFailure": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "fail"
            ]
          },
          "data": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "error": {
                    "type": "string",
                    "enum": [
                      "TooManyRules",
                      "InvalidRules",
                      "FirewallNotFound"
                    ]
                  }
                },
                "required": [
                  "error"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "error": {
                    "type": "string",
                    "enum": [
                      "InvalidRule"
                    ]
                  },
                  "details": {
                    "$ref": "#/components/schemas/InvalidFirewallRule"
                  }
                },
                "required": [
                  "error",
                  "details"
                ]
              }
            ]
          }
        },
        "required": [
          "status",
          "data"
        ]
      },
      "FirewallPatchFailure": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "fail"
            ]
          },
          "data": {
            "type": "string",
            "enum": [
              "InvalidNetwork",
              "ConflictingChanges",
              "TooManyRules",
              "FirewallNotFound"
            ]
          }
        },
        "required": [
          "status",
          "data"
        ]
      },
      "TunnelFirewallAssignFailure": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "fail"
            ]
          },
          "data": {
            "type": "string",
            "enum": [
              "TunnelNotFound",
              "InvalidFirewallId"
            ]
          }
        },
        "required": [
          "status",
          "data"
        ]
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "data": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "auth"
                    ]
                  },
                  "message": {
                    "type": "string",
                    "description": "The authentication error: `AuthRequired`, `InvalidApiKey`, `ScopeNotAllowed`, or `NotAllowedWithReadOnly`."
                  }
                },
                "required": [
                  "type",
                  "message"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "validation"
                    ]
                  },
                  "message": {
                    "type": "string"
                  }
                },
                "required": [
                  "type",
                  "message"
                ]
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "internal"
                    ]
                  },
                  "message": {
                    "type": "object",
                    "properties": {
                      "trace_id": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "trace_id"
                    ]
                  }
                },
                "required": [
                  "type",
                  "message"
                ]
              }
            ]
          }
        },
        "required": [
          "status",
          "data"
        ]
      }
    },
    "responses": {
      "AuthenticationError": {
        "description": "The API key is missing, not valid, or does not have the scope for this operation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            },
            "example": {
              "status": "error",
              "data": {
                "type": "auth",
                "message": "ScopeNotAllowed"
              }
            }
          }
        }
      },
      "OriginRejected": {
        "description": "The API does not accept browser requests from this origin.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            },
            "example": {
              "status": "error",
              "data": {
                "type": "validation",
                "message": "origin not allowed"
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Server error. If you contact support, include the trace ID.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            },
            "example": {
              "status": "error",
              "data": {
                "type": "internal",
                "message": {
                  "trace_id": "example-trace-id"
                }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Firewalls",
      "description": "Replace firewall rules, or allow and deny networks."
    },
    {
      "name": "Tunnels",
      "description": "Assign a firewall to a tunnel, or remove it."
    }
  ]
}
