Let customers manage IPs, protection and domains
The portal API for a customer's Floating IPs, DDoS Shield, external servers and platform domain names.
These portal routes let a customer order and attach Floating IPs, 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.
Before you begin
Section titled Before you begin- 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_idfromGET /portal/ips. The external server routes take the serviceidfromGET /portal/external-servers, which is the idGET /portal/servicesreturns too.
Order a floating IP
Section titled Order a floating IPList the pools that sell what the customer wants:
Shellcurl "https://api.coritan.com/api/v1/orgs/acme/portal/ips/pools?version=ipv4&prefix_len=32" \ -H "Authorization: Bearer $CUSTOMER_TOKEN"Each pool in
poolshas itsid,name,region,country_codeandgeo_label, andavailablesays whether it has an address to sell.versionisipv4oripv6.prefix_lenis32for one address, or 24–29 for a subnet; the list is empty for subnets while we do not rent them.regionnarrows the list to one location. IP pools and regions describes the pools.Find the
idof your IP product and of its plan, as List products and plans shows.Create a key for this checkout in the browser, such as the value of
crypto.randomUUID(), and place the order:Shellcurl -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 to32andipv4. | |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 serviceidof one of the customer's Container Apps servers that has no floating IP yet. We attach the address to it as soon as it is assigned. It works only whenprefix_lenis32. | |hostname| Optional. A name for the service. |The answer has the fields of a storefront order, such as
invoice_id,requires_paymentandstatus. It addsintent, which follows the order, andhold_expires_at.When
requires_paymentistrue, send the customer to pay, as Pay an invoice shows. We keep the address for the customer untilhold_expires_at. An order that stays unpaid past then is cancelled, and the address goes back to the pool.
Follow or cancel an order
Section titled Follow or cancel an order| 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.
Show a customer's addresses
Section titled Show a customer's addressesGET /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, or block for a subnet. |
address, cidr, version |
The address, or the subnet's first address, 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 Container Apps server, whether the address has reached the server: pending, delivered, failed or removing, and why it failed. |
ptr_hostname |
The reverse DNS name of a single address. |
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.
Attach and detach an address
Section titled Attach and detach an addressList what the address can go on:
Shellcurl "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_nameisvps) or Container Apps servers (container), with itsservice_id,hostnameandhas_floating_ip.Attach the address:
Shellcurl -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_typeisvpsorcontainer, andtarget_service_idis the target'sservice_idfrom step 1. For a subnet,host_addresspicks the address in it; leave it out to use the first free one. On an instance, the first address you attach becomes the primary one; sendis_primary: trueto make a later address primary, orfalseto keep it secondary. A server takes one floating IP.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 ashost_address.
Attach and detach a floating IP explains what attaching does on each kind of target.
Set reverse DNS
Section titled Set reverse DNScurl -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.
Filter traffic with DDoS Shield
Section titled Filter traffic with DDoS ShieldDDoS Shield filters traffic to every address with our standard profile. A customer can give an address a profile of their own and add rules to it.
| 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. |
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). |
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 and returns it. Sending it again changes nothing. |
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. |
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.
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:
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 range in CIDR form, such as 203.0.113.0/24. |
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.
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.
Run an external server
Section titled Run an external serverAn 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:
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.
Link a domain to an external server
Section titled Link a domain to an external serverThe routes under /portal/external-servers/{id}/custom-domain work as Link a custom domain describes for a Container Apps 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 domainWhen 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.
Read the overview:
Shellcurl "https://api.coritan.com/api/v1/orgs/acme/portal/platform-domains" \ -H "Authorization: Bearer $CUSTOMER_TOKEN"suffixeslists the domains on offer, andclaimsthe customer's names.limitis how many names a customer may hold, andremaininghow many more they can claim;remainingisnullwhen there is no limit.ruleshas themin_length,max_lengthandpatterna name must fit, and thereservednames nobody can claim. Once the customer has a name,suggestionsproposes a hostname under it for each product, such asplay.survival.example.comfor a game server.Check a name while the customer types it:
Shellcurl "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 asfqdn, and thereasonwhen it is not available.suffixdefaults to the first domain on offer.Claim it:
Shellcurl -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 thefqdnandzone_id.parent_delegatedsays whether we delegated the name from your domain's zone,dnssecwhether we signed it, andcertificatewhether we requested a wildcard certificate for it, with thereasonwhen 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.
Result
Section titled ResultYour 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 and changes to external servers appear in your organization audit log with the customer as the actor.
Troubleshooting
Section titled TroubleshootingUnsuspend 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 tabattach_to_org_service_idworks only whenprefix_lenis32. Order a subnet without it.PTR hostname must be a fully-qualified domain name- Send a full name, such as
mail.example.com. 403withRule does not belong to this subject profile- The rule is on another address's profile. Delete it through that address.
409withThis external server is not connected yet; pay its invoice first- The external server has no join address until its first invoice is paid.
409withThis external server is suspended- A suspended external server cannot be changed. Unsuspend its service first.
422witherrors- We refused the new address or setting, and
errorssays 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.
404withThis brand does not offer platform names- Your brand lends no domain yet. Ask support to add one.
409withAlready taken- Another customer holds the name, or a game server's join address uses it. Choose another.
429withrate_limited- The customer repeated an action too quickly. Wait, then try again.