Batches & ephemeral rules
Many rules per call, queued jobs, and rules that expire unless bumped, at /api/v2/firewall.
/api/v2/firewall takes the same organisation key as v1 and adds what v1 does
not do: many rules in one call with a result per item, work you can queue and
poll, and rules that remove themselves after a set time unless you keep them
alive. For one rule on one address, v1 is still the
simpler call.
https://one.terabit.io/api/v2/firewall
Authorization: Bearer tbk_...Needs the Firewall API flag on your organisation
(Activation), firewall.read for every GET
and firewall.manage for everything else.
What changes from v1
- The rule shape. An IP rule has a destination, an optional source, a
protocol and an action of
denyorallow, as in v1. Filters go by the names listed below, not v1's slugs. - One envelope. Every response is
{"data": ...}or{"error": {"code", "message", "details"}}. - Addresses are checked first. A request naming an address that is not
yours is refused whole with
403, anddetails.indexessays which items. A rule that already exists is reported asexists, not as a failure. - Its own rate limit of 500 requests a minute per source address, in
RateLimit-*headers. - Rules made here show under Protection → Firewall like any other, but carry no note and do not count towards the per-address limits v1 enforces.
- A short delay on key and address changes. A revoked key stops working here within thirty seconds; a newly assigned address becomes usable within five minutes. v1 sees both at once.
The rule
One shape; kind says which kind.
{
"kind": "ip",
"destination": { "ip": "203.0.113.10", "port": 27015 },
"source": { "ip": "0.0.0.0", "port": 0 },
"protocol": "udp",
"action": "deny"
}destination.port 0 means every port. source defaults to any address and any
port; protocol to any; action to deny.
{
"kind": "filter",
"filter": "mcje",
"destination": { "ip": "203.0.113.10", "port": 25565 }
}A filter rule attaches a stateful filter to a port. synproxy is a filter here
too, with an optional filter_id.
Every rule you read back carries a ref, which is how you address it
afterwards: ip:<id>, or filter:<name>:<ip>:<port> for a filter rule, which
has no id of its own.
Filter names
synproxy, dns, raknet, fivem, fivem-strict, openvpn, wireguard,
scpsl, generic-sip-udp, litenetlib, source-engine, arma3reforger,
palworld, conan-exiles, mcje, mcbe, unrealengine5, teamspeak3,
udpadaptive, monoamp, dayz, rdp, cs2, path-of-titans, rust,
vrising, a2s, zomboid, squad, ottwwi, arkse, tcp2, teraclassic,
bercon.
Older names work as aliases and read back under the canonical name: a2s_udp,
adaptive_udp, cs2_udp, fivem_enet, fivem_strict, openvpn_udp,
raknet_udp, sip_udp, ue5. tcp2 covers the whole address and ignores
destination.port.
This list is not narrowed per address the way v1's filters/available is. On an address assigned to a service, let what v1 offers there guide you.
One rule
curl -X POST https://one.terabit.io/api/v2/firewall/rules \
-H "Authorization: Bearer tbk_..." \
-H "Content-Type: application/json" \
-d '{"kind":"ip","destination":{"ip":"203.0.113.10","port":27015},"protocol":"udp"}'201 with the rule and its ref, or 200 with the existing rule when an
identical one was already there. Read it back with GET /rules/ip:42, delete
with DELETE /rules/ip:42.
Listing
GET /rules lists what you hold, narrowed with ip=, kind=ip|filter or
filter=<name>. It is paged: limit (default 100, at most 1,000) and
offset, with meta beside data.
{
"data": [ { "kind": "ip", "ref": "ip:42", "...": "..." } ],
"meta": { "total": 1362, "limit": 100, "offset": 0, "next_offset": 100 }
}next_offset is absent on the last page. The order is stable — IP rules by id,
then filters by name, address and port — so walking the pages sees each rule
once. GET /leases pages the same way.
Batches
Send rules instead of one rule. Items are applied in order and each reports its
own outcome; the call answers 201 if anything was created, 200 otherwise.
Two limits. A batch of more than 4 items must be sent with
"mode": "async" (below), or it is refused with 400 async_required; up to 4
may be applied synchronously. A request carries at most 1,000 items, which
is also the most one job holds — split larger sets across requests.
{
"atomic": false,
"rules": [
{ "kind": "ip", "destination": { "ip": "203.0.113.10", "port": 27015 }, "protocol": "udp" },
{ "kind": "filter", "filter": "mcje", "destination": { "ip": "203.0.113.10", "port": 25565 } }
]
}{
"data": {
"results": [
{ "index": 0, "op": "create", "status": "created", "ref": "ip:42", "rule": { "...": "..." } },
{ "index": 1, "op": "create", "status": "exists", "ref": "filter:mcje:203.0.113.10:25565", "rule": { "...": "..." } }
],
"summary": { "created": 1, "exists": 1, "deleted": 0, "absent": 0, "error": 0, "rolled_back": 0 }
}
}"atomic": true makes the batch all-or-nothing: one failure rolls every item
back, reported as rolled_back. Without it a failing item is error and the
rest still land.
Batch deletes go to POST /rules/delete with refs, or match (rule shapes,
deleting whatever matches each), or both, under the same two limits. A ref you
cannot see is reported absent.
Jobs
"mode": "async" on a batch, or ?mode=async on a single create or delete,
answers 202 at once. It is required for any batch of more than 4 items, and
worth using for anything you would not want to wait on:
{ "data": { "job": { "id": "3f9c...", "status": "queued" } } }Poll GET /jobs/{id}: queued, active, then completed or failed, with
the same results and summary a synchronous batch answers with.
DELETE /jobs/{id} cancels a job that has not started (409 not_cancellable
once it has). Jobs are kept for seven days; GET /jobs?status= lists yours.
Ephemeral rules
A ttl in seconds, on a batch or as ?ttl= on a single create, makes the rules
it creates temporary. Each read carries an expires_at; when it passes, the rule
is removed within about fifteen seconds.
# block a source for ten minutes
curl -X POST "https://one.terabit.io/api/v2/firewall/rules?ttl=600" \
-H "Authorization: Bearer tbk_..." \
-H "Content-Type: application/json" \
-d '{"kind":"ip","destination":{"ip":"203.0.113.10","port":0},"source":{"ip":"198.51.100.7"}}'POST /rules/{ref}/bump resets the clock to now plus the rule's ttl; a body of
{"ttl": 1800} sets a new interval that later bumps reuse.
DELETE /rules/{ref}/lease makes the rule permanent. GET /leases lists what is
due to expire, soonest first.
Re-creating a temporary rule without a ttl does not make it permanent — it is
reported as exists and its clock is untouched. Release the lease if you mean it
to stay.
Errors
| Code | Status | Meaning |
|---|---|---|
validation_failed | 400 | The body or query did not match the schema; details.issues lists the paths |
bad_request | 400 | An invalid address or an unknown filter |
async_required | 400 | A batch of more than 4 items without mode: async; details carries both limits |
unauthorized | 401 | No key, or a key that is not valid |
ability_required | 403 | The key lacks firewall.read or firewall.manage for this method |
forbidden | 403 | An address is not yours; details.indexes names the items |
not_found | 404 | No such rule, job or lease, including one that is not yours |
not_cancellable | 409 | The job has already started |
rate_limited | 429 | Over 500 requests in the minute; Retry-After says when |
auth_unavailable | 503 | Your key could not be verified right now; retry shortly |
Every endpoint, with its full schema and a playground, is under API Reference → Batches & ephemeral rules.
Poll a queued group operation GET
Every write that changes what a group applies answers with an operation_id. This is where you follow it.
List rules, narrowed by address, kind or filter GET
Paginated: limit (default 100, at most 1000) and offset, with meta.total and meta.next_offset in the answer. Ordered…