Skip to content
Coritan Docs

Let customers manage IPs, protection and domains

The portal API for a customer's Floating IPs, DDoS Shield, external servers and platform domain names.

View as Markdown

These portal routes let a customer order and attach Floating IPs, give a Cloud Compute instance its IPv6, filter the traffic that reaches them with DDoS Shield, point an external server at a game server they host elsewhere, and claim a free name under a domain your brand lends. Every route takes the customer's token as Authorization: Bearer $CUSTOMER_TOKEN and works only on services that customer bought. Sharing a server with another customer does not share its addresses.

  • Get a customer token as Sign customers in to your storefront describes.
  • Sell the plans in your catalogue: an IP product for addresses and an external server product for external servers. Set up products and pricing explains products and plans.
  • Use the right id. The IP and DDoS Shield routes take the address's service_id from GET /portal/ips. The external server routes take the service id from GET /portal/external-servers, which is the id GET /portal/services returns too.
  1. List the pools that sell what the customer wants:

    Shell
    curl "https://api.coritan.com/api/v1/orgs/acme/portal/ips/pools?version=ipv4&prefix_len=32" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN"
    

    Each pool in pools has its id, name, region, country_code and geo_label, and available says whether it has an address to sell. version is ipv4 (the default) or ipv6. For IPv4, prefix_len is 32 for one address, or 24–29 for a subnet; the list is empty for subnets while we do not rent them. IPv6 is sold as single addresses, so with version=ipv6 send prefix_len=128 or leave it out; any other size answers 422. region narrows the list to one location. IP pools and regions describes the pools.

  2. Find the id of your IP product and of its plan, as List products and plans shows.

  3. Create a key for this checkout in the browser, such as the value of crypto.randomUUID(), and place the order:

    Shell
    curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/ips/order" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"org_product_id": 14, "org_pricing_id": 40, "pool_id": 3, "prefix_len": 32, "version": "ipv4", "idempotency_key": "5b0f7c1e-ip-order"}'
    
    Field What it holds
    org_product_id, org_pricing_id Your IP product and the plan.
    pool_id The pool from step 1. The address comes from this pool only.
    prefix_len, version The size and family. They default to what the product sells, so leave them out for an IPv6 address. A value the product does not sell answers 422.
    idempotency_key Required. 1–64 letters, digits, dots, underscores, colons or hyphens. Keep it for the whole checkout, so a second click or a second tab returns the same order.
    attach_to_org_service_id Optional. The service id of one of the customer's servers that has no floating IP yet. We attach the address to it as soon as it is assigned. It works only for one IPv4 address, with prefix_len set to 32.
    hostname Optional. A name for the service.

    The answer has the fields of a storefront order, such as invoice_id, requires_payment and status. It adds intent, which follows the order, and hold_expires_at.

  4. When requires_payment is true, send the customer to pay, as Pay an invoice shows. We keep the address for the customer until hold_expires_at. An order that stays unpaid past then is cancelled, and the address goes back to the pool.

Route What it does
GET /portal/ips/orders The customer's open orders, so a checkout carries on after a reload or in another tab. Each has its idempotency_key and a status of started, held, invoiced or provisioning.
GET /portal/ips/orders/{idempotency_key} One order, with its invoice's invoice_number, total, currency, due_date and invoice_status.
POST /portal/ips/orders/{idempotency_key}/cancel Cancels an order whose address is not assigned yet, with its open invoice.

Sending an order again with the same key returns the first order, with replay: true, once it has an invoice. A key whose order failed, was cancelled or expired starts a new order.

GET /portal/ips returns items and total. Each item is one address or one subnet:

Field Meaning
service_id The id the other IP and DDoS Shield routes take.
kind standalone for one address, block for a subnet, or routed for the IPv6 /64 that comes with a Cloud Compute instance.
address, cidr, version The address, or the first address of a subnet or a /64, with its prefix.
service_status The service's status, such as active or suspended.
pool_name, region, geo_label Where the address comes from.
target_type, target_service_id, is_primary, attached_at What the address is attached to. They are empty when it is not attached.
host_state, host_error On a server, whether the address has reached it: pending, delivered, failed or removing, and why it failed.
ptr_hostname The reverse DNS name of a single address. A /64 has a name per address instead, as Set reverse DNS shows.

Show prices and due dates from the customer's service in GET /portal/services, which holds what the customer pays you.

GET /portal/ips/{service_id} returns one item. A subnet also has hosts_total, hosts_attached and hosts_available. GET /portal/ips/{service_id}/hosts lists each address in it, with attachable and what it is attached to; add include_reserved=false to leave out the addresses nobody can attach. How subnets work explains which addresses those are.

  1. List what the address can go on:

    Shell
    curl "https://api.coritan.com/api/v1/orgs/acme/portal/ips/attach-targets" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN"
    

    Each item is one of the customer's Cloud Compute instances (module_name is vps) or servers (container), with its service_id, hostname and has_floating_ip.

  2. Attach the address:

    Shell
    curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/ips/$IP_SERVICE_ID/attach" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"target_type": "vps", "target_service_id": 5120}'
    

    target_type is vps or container, and target_service_id is the target's service_id from step 1. For a subnet, host_address picks the address in it; leave it out to use the first free one. On an instance, the first IPv4 address you attach becomes the primary one; send is_primary: true to make a later address primary, or false to keep it secondary. A server takes one floating IP.

    An IPv6 address attaches to Cloud Compute instances only, so send target_type vps. It is never the instance's primary address: leave is_primary out.

  3. To take it off, send POST /portal/ips/{service_id}/detach. For a subnet with more than one address attached, send the one to take off as host_address.

An instance's IPv6 /64 (kind routed) stays on its instance. Attaching, detaching or moving it answers 409.

Attach and detach a floating IP explains what attaching does on each kind of target.

Shell
curl -X PATCH "https://api.coritan.com/api/v1/orgs/acme/portal/ips/$IP_SERVICE_ID/ptr" \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostname": "mail.example.com"}'

hostname must be a full domain name, up to 253 characters. For an address in a subnet, add the address as host_address. GET /portal/ips/{service_id}/ptr reads the current name, and hostname is null when there is none. Set reverse DNS for a floating IP covers reverse DNS in more detail.

For an instance's IPv6 /64, every request names one address inside it as host_address, such as 2001:db8:1000:5::1, the address the instance uses. Send it in the body of PATCH and as ?host_address= on GET. The /64 keeps up to 256 names.

A Cloud Compute instance in a location with IPv6 gets an IPv6 /64 of its own when it is created, at no charge, and uses the first address in it from its next start. Add IPv6 to an instance explains how the /64 works on the instance.

  • GET /portal/vps/{uuid} shows the /64 as ipv6_prefix and the address the instance uses as ipv6_address. Both are null until the instance has IPv6.
  • GET /portal/ips lists the /64 as an item with kind set to routed, attached to its instance.
  • When you sell an IP product, GET /portal/services also lists the /64 as one of the customer's services, at no charge.

To give IPv6 to an instance that has none, such as one created before its location had IPv6:

  1. Send the request, with the instance's uuid from resource.uuid on the customer's service. It takes no body:

    Shell
    curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/vps/$INSTANCE_UUID/ipv6" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN"
    
    JSON
    {"prefix": "2001:db8:1000:5::/64", "address": "2001:db8:1000:5::1", "reboot_required": true}
    
  2. When reboot_required is true, restart the instance, as Power an instance shows, so that it sets up the address. Asking again answers the same /64 with reboot_required set to false.

The request answers 409 when the location has no IPv6 to give or while the instance is migrating, 404 when the instance is not the customer's, and 429 with a Retry-After header when the customer asks too often. Any other failure answers 502, and the instance keeps no /64 from that request.

Filter traffic with DDoS Shield

Section titled Filter traffic with DDoS Shield

DDoS Shield filters traffic to every address with our standard profile. A customer can give an address a profile of their own, change its settings, add rules to it and block the senders they do not want.

Route What it does
GET /portal/shield/status Each of the customer's addresses in ips, with its ip_service_id, address, whether it is attached, and its protection: mode is platform_default or custom, with the profile's profile_name and protection_mode.
GET /portal/shield/events Attacks on the customer's addresses, with the ones still running first. Narrow them with ip_service_id and limit (default 50, at most 500).
GET /portal/shield/subjects/{ip_service_id}/profile The address's mode, profile and rules. editable is false on the standard profile, which the customer can read and cannot change.
PATCH /portal/shield/subjects/{ip_service_id}/profile Gives the address a profile of the customer's own when it has none, applies the settings in the body, and returns the profile.
POST /portal/shield/subjects/{ip_service_id}/rules Adds a rule, and gives the address its own profile first when it has none.
DELETE /portal/shield/subjects/{ip_service_id}/rules/{rule_id} Deletes a rule.
GET /portal/shield/subjects/{ip_service_id}/blocked-sources The senders blocked on the address in blocked_sources, newest first, and the most it can hold in max_blocks.
POST /portal/shield/subjects/{ip_service_id}/blocked-sources Blocks a sender or a range on the address.
DELETE /portal/shield/subjects/{ip_service_id}/blocked-sources/{block_id} Removes a block.

Each event has the address, the attack_type, the drop_reason, its size as pps, bps and unique_sources, the top_sources, started_at and ended_at, and active while it is still running. Read attack events explains what they mean.

Each PATCH, POST and DELETE counts towards a budget of 60 DDoS Shield changes per customer in ten minutes. Past it, a change answers 429 with rate_limited in detail.error, and the Retry-After header gives the seconds to wait.

Shell
curl -X PATCH "https://api.coritan.com/api/v1/orgs/acme/portal/shield/subjects/$IP_SERVICE_ID/profile" \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"default_action": "deny", "rate_limits": {"per_source_pps": 50000}}'
Field What it changes
protection_mode standard filters with our own checks and rate limits. custom does the same with the profile's rate limits in place of ours. passthrough skips our checks, the rate limits, the rules and the blocks.
default_action What happens to traffic that no rule matches: allow lets it through, and deny drops it.
established_passthrough With default_action set to deny, true lets through TCP packets of connections already open, sent to port 1024 or above.
rate_limits Any of per_source_pps (100–10,000,000), aggregate_pps (10,000–100,000,000), syn_pps_per_source (100–10,000,000) and icmp_pps_per_source (10–1,000,000). A limit you leave out keeps its value. The limits apply only in custom mode.

Every field is optional, and an empty body gives the address its own profile without changing anything else. The answer has the same shape as GET. A word outside its list answers 422 with protection_mode must be standard|custom|passthrough or default_action must be allow|deny, and a limit outside its range answers 422 with a list in detail. Change a floating IP's DDoS protection explains each setting.

A profile of the customer's own starts with our standard rate limits and no rules, and lets through traffic that no rule matches. Add a rule:

Shell
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/shield/subjects/$IP_SERVICE_ID/rules" \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Block a scanner", "action": "deny", "src_prefix": "203.0.113.0/24"}'
Field What it matches or does
action deny (the default), allow or rate_limit.
rate_limit_pps The packets per second a rate_limit rule lets through.
protocol The IP protocol number, such as 6 for TCP, 17 for UDP or 1 for ICMP.
src_prefix, dst_prefix A source or destination: one address, such as 203.0.113.7, or a range in CIDR form, such as 203.0.113.0/24. We store one address as its /32.
src_port_min, src_port_max, dst_port_min, dst_port_max Port ranges. A minimum with no maximum matches that one port.
tcp_flags_mask, tcp_flags_value The TCP flags to test, and the value they must have.
packet_len_min, packet_len_max, ttl_min, ttl_max Ranges of packet length and time to live.
icmp_type, icmp_code The ICMP type and code.
name, notes, enabled A label up to 191 characters, a note, and false to keep the rule without applying it.

Check each value in your storefront before you send it: protocol, TCP flag, time to live and ICMP values are whole numbers from 0 to 255, ports and packet lengths from 0 to 65535, and rate_limit_pps is a positive number. We check the prefixes, and one that is neither an address nor a range answers 422 with a list in detail.

Warning

A field you leave out matches every packet, and action defaults to deny. A rule sent with no match fields blocks all traffic to the address.

Change a floating IP's DDoS protection describes profiles and rules for Coritan customers.

A block drops all traffic from an address or a range to one of the customer's addresses, on the standard profile or their own, unless the profile is in passthrough mode:

Shell
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/shield/subjects/$IP_SERVICE_ID/blocked-sources" \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"address": "198.51.100.7", "reason": "Repeated SSH logins", "expires_minutes": 1440}'
Field What it holds
address Required. An IPv4 address, or a range from /8 to /32.
reason Optional, up to 255 characters.
expires_minutes Optional. The minutes until we remove the block, from 1 to 43,200 (30 days). Leave it out to keep the block until the customer removes it.

The answer, with status 201, is the block: its id, which DELETE takes, the cidr we stored, the reason, expires_at and created_at. An address holds up to 100 blocks. A block our team added has source set to admin and removable set to false, and the customer cannot remove it. Block a traffic source explains how a block is applied and lists every answer.

We email the customer when an attack on one of their addresses starts and when it ends, as Attack emails describes for Coritan accounts. The emails leave the way your other customer emails do (Choose how your email leaves) and link to your storefront's sign-in page. A customer cannot turn them off. Each address gets emails about at most six attacks in any 24 hours.

An external server gives a game server the customer hosts elsewhere a join address on your network. The customer orders it through Take an order, and these routes manage it once it exists. How external servers work explains the product.

GET /portal/external-servers lists the customer's external servers. Each has the id the routes take, name, billing_status, join_address, mode, upstream (the host and port players are sent to), region, and the last test in probe. state is online, offline, suspended or unknown. Until its first invoice is paid, an external server has no join address and its state is unknown. GET /portal/external-servers/{id} adds edge_addresses and custom_domain, and GET /portal/external-servers/regions lists the regions it can move to, with the default_region_id.

Change where players go:

Shell
curl -X PATCH "https://api.coritan.com/api/v1/orgs/acme/portal/external-servers/$SERVICE_ID" \
  -H "Authorization: Bearer $CUSTOMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"upstream_host": "203.0.113.25", "upstream_port": 25565}'
Field What it changes
upstream_host, upstream_port Where the server listens: a public IP address or a full hostname, and a port from 1 to 65535.
mode minecraft_java or minecraft_bedrock.
proxy_protocol true sends each player's address in a PROXY protocol header. Turn it on only when the server expects one. Bedrock servers cannot use it.
real_ip With PROXY protocol off, adds the player's address to the hostname in the handshake. It is on by default. Turn it off for a proxy such as Velocity or BungeeCord that cannot accept the longer hostname.
bedrock_bridge For a Java server that also runs Geyser, lets Bedrock players join at the same address.
location_id The region players connect through, from the regions list.
display_name The server's name, 1–100 characters.

We refuse an address in a private or reserved range, an address of our own proxies, another customer's server on our network, and a server already connected to another external server. The answer is the external server as it now stands. After an address change we test the server at once, so probe shows whether it answers. POST /portal/external-servers/{id}/test tests it again and returns the probe with a state of online or offline. Connect a server you host elsewhere covers the connection itself.

Change what players see in their server list with PATCH /portal/services/{service_id}/gameproxy, as Show the customer's services describes, and list the server in your directory with PUT /portal/services/{service_id}/community-listing, as Run a community with a server list, forum and guides describes.

Section titled Link a domain to an external server

The routes under /portal/external-servers/{id}/custom-domain work as Link a custom domain describes for a server. GET shows the link and the expected_cname_target, POST links a domain and subdomain, POST /verify checks the record now, PATCH changes include_in_list, and DELETE unlinks the name.

When the name is under a free name the customer claimed, as the next section describes, we write the CNAME record for them and the link is verified at once. Use a subdomain such as play there, since a CNAME record cannot sit at the root of a zone we host.

Offer free names under your domain

Section titled Offer free names under your domain

When your brand lends a domain, such as example.com, each customer can claim a name under it, such as survival.example.com, and use it for their servers. We set up the domains your brand lends; ask support to add one. The names resolve only when that domain's zone is on Coritan's nameservers, since we delegate each name from it.

  1. Read the overview:

    Shell
    curl "https://api.coritan.com/api/v1/orgs/acme/portal/platform-domains" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN"
    

    suffixes lists the domains on offer, and claims the customer's names. limit is how many names a customer may hold, and remaining how many more they can claim; remaining is null when there is no limit. rules has the min_length, max_length and pattern a name must fit, and the reserved names nobody can claim. Once the customer has a name, suggestions proposes a hostname under it for each product, such as play.survival.example.com for a game server.

  2. Check a name while the customer types it:

    Shell
    curl "https://api.coritan.com/api/v1/orgs/acme/portal/platform-domains/availability?label=survival&suffix=example.com" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN"
    

    The answer has available, the full name as fqdn, and the reason when it is not available. suffix defaults to the first domain on offer.

  3. Claim it:

    Shell
    curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/platform-domains/claims" \
      -H "Authorization: Bearer $CUSTOMER_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"label": "survival", "suffix": "example.com"}'
    

    The answer, with status 201, has the fqdn and zone_id. parent_delegated says whether we delegated the name from your domain's zone, dnssec whether we signed it, and certificate whether we requested a wildcard certificate for it, with the reason when we did not.

Each name is a DNS zone that we sign with DNSSEC. It appears on your organization's DNS tab, where your team can add and change its records, as Host DNS zones for your organization describes. The portal has no routes for customers to edit records.

DELETE /portal/platform-domains/claims/{claim_id}, with the claim's id from claims, releases a name and returns its fqdn.

Warning

Releasing a name deletes its zone with every record in it, and we stop renewing its certificates. Hostnames under it stop resolving, so move anything that uses them first.

Your storefront lets each customer order, attach and protect their addresses, run their external servers and claim names, and every route checks that the service belongs to the customer who sent it. Claims, releases, DDoS Shield changes and changes to external servers appear in your organization audit log with the customer as the actor.

Unsuspend the IP service before attaching
The address's service is suspended. Unsuspend it as Suspend or unsuspend a service describes.
Container already has a primary floating IP; detach first
A server takes one floating IP. Detach the other address first.
Subnet has multiple attached hosts; pass host_address to detach one
Send the address to take off as host_address.
host_address / host_inventory_id only apply to subnet block services
The address is a single one. Leave out host_address.
No free hosts available in this subnet
Every address in the subnet is attached. Detach one first.
An identical IP order is already in progress; retry in a moment
The first request with this key is still running. Retry with the same key.
This IP order can no longer be cancelled
The order is past payment, or has already ended. To give up an address the customer holds, cancel its service as Cancel a service shows.
Only a single IPv4 can be attached to a server from its Network tab
attach_to_org_service_id works only when prefix_len is 32. Order a subnet without it.
This server takes IPv4 floating IPs only for now. Attach this IPv6 address to a Cloud Compute instance.
The request sent an IPv6 address to a server, or named one in attach_to_org_service_id. Attach it to one of the customer's Cloud Compute instances.
An IPv6 address is never an instance's primary address. Leave is_primary out.
Send the attach again without is_primary.
This /64 comes with its instance and stays with it.
The address is an instance's IPv6 /64, which cannot be attached, detached or moved.
An IPv6 order takes prefix_len 128; got 64
IPv6 is sold as single addresses, for a pools list or an order. Send prefix_len as 128, or leave it out.
A PTR on a /64 needs host_address: the address inside the /64 that it names.
Add host_address. An address outside the /64 answers 422 with a message such as 2001:db8:2000::1 is not inside 2001:db8:1000:5::/64.
This /64 already has 256 PTR records, the most it can keep. Set a new hostname on one of its addresses instead.
Change the name of an address the /64 already has a name for.
This location has no IPv6 yet. or This location has no IPv6 left to give out. Contact support.
The instance's location has no /64 to give. Nothing changed on the instance.
We could not add IPv6 to this instance. Try again in a moment.
The instance did not get its /64. Send the request again in a moment.
PTR hostname must be a fully-qualified domain name
Send a full name, such as mail.example.com.
403 with Rule does not belong to this subject profile
The rule is on another address's profile. Delete it through that address.
403 with Our team added this block, so only they can remove it. Contact support to ask.
We added the block. Ask support to remove it.
409 with 198.51.100.7/32 is already blocked on this address.
The address has that block already.
409 with This address already has 100 blocked sources. Remove one before adding another.
Remove a block the customer no longer needs, or block one range that covers several senders.
422 with That range is too wide. Block a /8 or a narrower range. or This address is IPv4, so block an IPv4 source.
Send an IPv4 address, or a range from /8 to /32.
422 with Enter an IP address or a range in CIDR form, such as 203.0.113.7 or 203.0.113.0/24.
The block's address, or a rule's prefix, is neither an address nor a range. For a rule, the message is the msg of an item in detail.
409 with This external server is not connected yet; pay its invoice first
The external server has no join address until its first invoice is paid.
409 with This external server is suspended
A suspended external server cannot be changed. Unsuspend its service first.
422 with errors
We refused the new address or setting, and errors says why, such as an address in a private range or PROXY protocol on a Bedrock server.
A CNAME can't sit at the root of a zone hosted here. Use a subdomain such as play.
The domain is one of the customer's free names. Link a subdomain of it.
404 with This brand does not offer platform names
Your brand lends no domain yet. Ask support to add one.
409 with Already taken
Another customer holds the name, or a game server's join address uses it. Choose another.
429 with rate_limited
The customer repeated an action too quickly, or made 60 DDoS Shield changes in ten minutes. Wait, then try again.

API operations on this page

MethodPathWhat it does
GET/api/v1/orgs/{org_slug}/portal/ipsList my IPs
GET/api/v1/orgs/{org_slug}/portal/ips/attach-targetsList attach targets
POST/api/v1/orgs/{org_slug}/portal/ips/orderOrder a floating IP / subnet via the org storefront path
GET/api/v1/orgs/{org_slug}/portal/ips/ordersOpen IP order intents, so unpaid or in-flight orders resume after login or in a new tab
GET/api/v1/orgs/{org_slug}/portal/ips/orders/{idempotency_key}Get my IP order
POST/api/v1/orgs/{org_slug}/portal/ips/orders/{idempotency_key}/cancelCancel my IP order
GET/api/v1/orgs/{org_slug}/portal/ips/poolsThe locations you can order a floating IP or a subnet from
GET/api/v1/orgs/{org_slug}/portal/ips/{service_id}Get my IP
POST/api/v1/orgs/{org_slug}/portal/ips/{service_id}/attachAttach IP
POST/api/v1/orgs/{org_slug}/portal/ips/{service_id}/detachDetach IP
GET/api/v1/orgs/{org_slug}/portal/ips/{service_id}/hostsList my IP hosts
GET/api/v1/orgs/{org_slug}/portal/ips/{service_id}/ptrGet PTR
PATCH/api/v1/orgs/{org_slug}/portal/ips/{service_id}/ptrSet PTR
GET/api/v1/orgs/{org_slug}/portal/shield/eventsRecent attack events on the customer's IP services, live ones first
GET/api/v1/orgs/{org_slug}/portal/shield/statusThe protection on each of your floating IPs and subnets
GET/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/blocked-sourcesThe sources refused on this IP, newest first
POST/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/blocked-sourcesRefuse traffic from an address or a CIDR range to this IP only
DELETE/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/blocked-sources/{block_id}Remove a block from this IP
GET/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/profileGet subject profile
PATCH/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/profileEnsure custom profile
POST/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/rulesCreate subject rule
DELETE/api/v1/orgs/{org_slug}/portal/shield/subjects/{ip_service_id}/rules/{rule_id}Delete subject rule
GET/api/v1/orgs/{org_slug}/portal/external-serversList external servers
GET/api/v1/orgs/{org_slug}/portal/external-servers/regionsRegions an existing external server can be moved to
GET/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}Get external server
PATCH/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}Change where players are forwarded, how, and through which region
GET/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}/custom-domainGet custom domain
POST/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}/custom-domainLink custom domain
PATCH/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}/custom-domainShow the vanity hostname, or the hosted one, in the public server list
DELETE/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}/custom-domainUnlink custom domain
POST/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}/custom-domain/verifyCheck the CNAME now rather than waiting for the next maintenance pass
POST/api/v1/orgs/{org_slug}/portal/external-servers/{service_id}/testStatus-ping the backend now and record the answer
GET/api/v1/orgs/{org_slug}/portal/platform-domainsOverview
GET/api/v1/orgs/{org_slug}/portal/platform-domains/availabilityCheck availability
POST/api/v1/orgs/{org_slug}/portal/platform-domains/claimsCreate claim
DELETE/api/v1/orgs/{org_slug}/portal/platform-domains/claims/{claim_id}Delete claim