# Create a join address

> Give a game server a name players can join with, such as survival-smp under Coritan's game domain.

Source: https://www.coritan.com/docs/proxies/join-addresses/create-a-join-address/

In the dashboard:

- /dashboard/proxies/game: https://www.coritan.com/dashboard/proxies/game

A join address gives one of your servers hosted here a name that players type in their game, such as `survival-smp.coritan.gg`, instead of an IP address and port. Create one when a server has no join address yet, or when you want another name for it ([How join addresses work](/docs/proxies/join-addresses/)). To give a name to a server you run elsewhere, [connect it as an external server](/docs/proxies/external-servers/connect-an-external-server/) instead.

## Before you begin

- You need a server hosted here ([Order a server](/docs/managed-containers/order-a-server/)).
- The join address forwards to the server's primary address and port, which the **Network** card on the server's **Settings** tab shows ([Server settings](/docs/managed-containers/settings/#network)). The game must listen on that port, so make the game's port primary first if it is not ([Make another port primary](/docs/managed-containers/ports/#make-another-port-primary)). A TCP service can forward to another of the server's ports when you create it with the API ([With the API](#with-the-api)).
- For a Java server with [Bedrock crossplay](/docs/managed-containers/software/bedrock-crossplay/), create a **Minecraft Java** join address. Bedrock players join at the same name, on port `19132`.

## Create the join address

1. In the dashboard, go to [Edge Proxy](https://www.coritan.com/dashboard/proxies/game) and select **New proxy…**.
2. Choose what the edge answers for: **Minecraft Java**, **Minecraft Bedrock** or **TCP service** ([Editions and ports](/docs/proxies/join-addresses/#editions-and-ports)).
3. Choose **A server hosted here**.
4. In **Join subdomain**, type the name, such as `survival-smp`. The field checks the name as you type, and shows `survival-smp.coritan.gg is available.` when it is free ([Name rules](#name-rules)).

    To pick a generated name instead, select **Suggest a name**, then select one of the five names it offers. Picking one also fills in **Display name** when it is empty.
5. Choose the **Server**. The list shows each server's name with its address and port.
6. Optionally, enter a **Display name** of up to 100 characters. The dashboard shows it, and you can change it later as the **Server name** on the **Server list** tab. Without one, the join address uses its subdomain.
7. Turn on **PROXY protocol** only when the server expects a PROXY protocol version 2 header on each connection ([What your server receives](/docs/proxies/join-addresses/#what-your-server-receives)). A Bedrock join address cannot use it.

    > [!IMPORTANT]
    > You cannot change **PROXY protocol** once the join address exists. A server that does not expect the header refuses every player.
8. Select **Create join address**.

For a **TCP service**, the form notes that the edge gives the join address a port of its own in the server's region. You see the port once the join address exists.

## Name rules

A join address's name is one label under the join domain, and must follow these rules:

- It has 3–28 characters.
- It uses lowercase letters, digits and hyphens, with no hyphen at either end.
- It has at least one letter, and does not start with `xn--`.
- No other join address, and no [coritan.gg name](/docs/proxies/coritan-gg-names/), already has it.
- It is not a reserved name.

The field changes what you type as you go. It turns capitals into lowercase letters, turns spaces, underscores and dots into hyphens, and drops every other character, so `Survival SMP` becomes `survival-smp`.

These names are reserved: `account`, `accounts`, `admin`, `analytics`, `api`, `app`, `assets`, `auth`, `billing`, `bitbucket`, `blog`, `cdn`, `checkout`, `console`, `control`, `dashboard`, `database`, `db`, `demo`, `dev`, `development`, `dns`, `docs`, `documentation`, `email`, `files`, `ftp`, `gameproxy`, `git`, `github`, `gitlab`, `help`, `images`, `imap`, `img`, `internal`, `invoice`, `localhost`, `login`, `mail`, `media`, `metrics`, `mongo`, `mongodb`, `monitor`, `monitoring`, `mx`, `mysql`, `nameserver`, `news`, `ns` and `ns1` to `ns8`, `oauth`, `panel`, `payment`, `payments`, `play`, `pop`, `portal`, `postgres`, `postgresql`, `private`, `proxy`, `public`, `rdp`, `redis`, `remote`, `root`, `sftp`, `shop`, `smtp`, `sql`, `ssh`, `sso`, `staging`, `static`, `stats`, `status`, `store`, `support`, `svn`, `test`, `testing`, `uploads`, `vpn`, `webmail`, `wiki` and `www`.

## Result

The dashboard shows `Join address survival-smp.coritan.gg created.` and opens the join address's page. Players join at `survival-smp.coritan.gg`. For a TCP service, they add the **Edge port** from the **Overview** tab after the name, such as `survival-smp.coritan.gg:61234`.

## Troubleshooting

`Subdomain is already in use`
: Another join address or a coritan.gg name has the name. Choose another, or select **Suggest a name**. The foot of the form reads `That subdomain is taken.` until you do.

`play is reserved; choose another.`
: The name is on the reserved list in [Name rules](#name-rules). Choose another.

`Use 3 to 28 characters.`
: The name is shorter than 3 or longer than 28 characters after the field has changed it.

`Use lowercase letters, numbers and hyphens, with no hyphen at either end.`
: The name starts or ends with a hyphen. Remove it.

`Include a letter; a subdomain cannot be only digits.`
: Add a letter to the name.

`Punycode names are not accepted.`
: The name starts with `xn--`. Choose another.

**A server hosted here** is greyed out
: The card reads `No servers on your account yet.` Order a server first ([Order a server](/docs/managed-containers/order-a-server/)), or connect a server you run elsewhere with **A server elsewhere**.

**A server elsewhere** is greyed out for a TCP service
: The card reads `TCP ports are available for servers hosted here only.` A TCP service join address forwards only to a server hosted here.

`Could not resolve upstream for service 1187`
: The server has no address yet, for example while Coritan is still setting it up. Wait until the server is running, then try again.

`Another allocation took the edge listen port at the same time. Try again.`
: Two TCP service join addresses asked for the same edge port at once. Select **Create join address** again.

Every player is refused after you create the join address
: **PROXY protocol** is on, and the server does not expect the header. Set the server to accept PROXY protocol version 2, or create another join address without it. For Paper, set `proxies.proxy-protocol: true` in `config/paper-global.yml`; for Velocity, set `haproxy-protocol = true` in `velocity.toml`.

## Related

- [How join addresses work](/docs/proxies/join-addresses/)
- [Customise the server list entry](/docs/proxies/join-addresses/server-list/)
- [Use your own domain for a join address](/docs/proxies/join-addresses/custom-domain/)
- [Connect a server you host elsewhere](/docs/proxies/external-servers/connect-an-external-server/)

## With the API

Check a name with `POST /api/v1/gameproxy/availability`. It needs no token, changes the name as the dashboard's field does, and checks it against the rules and every name in use:

```bash
curl -X POST https://api.coritan.com/api/v1/gameproxy/availability \
  -H "Content-Type: application/json" \
  -d '{"subdomain": "survival-smp"}'
```

```json
{"available": true, "subdomain": "survival-smp", "base_domain": "coritan.gg", "fqdn": "survival-smp.coritan.gg", "reason": null}
```

A name in use answers `"available": false` with the `reason` `Subdomain is already in use`. A name that breaks a rule answers with only `available`, `subdomain` and a `reason`, such as `{"available": false, "subdomain": "play", "reason": "Subdomain 'play' is reserved"}`. The `subdomain` you send must have 3–28 characters, or the call answers `422`.

Ask for generated names with `POST /api/v1/gameproxy/name-suggestions`. It needs no token, and takes a `count` from `1` to `20`, `5` by default:

```bash
curl -X POST https://api.coritan.com/api/v1/gameproxy/name-suggestions \
  -H "Content-Type: application/json" \
  -d '{"count": 3}'
```

```json
{
  "suggestions": [
    {"display_name": "FrostHaven", "subdomain": "frosthaven482"},
    {"display_name": "PixelForge", "subdomain": "pixelforge157"},
    {"display_name": "CopperIsles", "subdomain": "copperisles903"}
  ],
  "base_domain": "coritan.gg"
}
```

A suggestion leaves out names that other join addresses hold, but can still match a coritan.gg name. Check it with the availability call before you create the join address.

Create the join address with `POST /api/v1/gameproxy/routes`:

```bash
curl -X POST https://api.coritan.com/api/v1/gameproxy/routes \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "survival-smp",
    "mode": "minecraft_java",
    "upstream_service_id": 1187,
    "display_name": "Survival SMP"
  }'
```

```json
{
  "ok": true,
  "module_data": {
    "route_id": 42,
    "fqdn": "survival-smp.coritan.gg",
    "subdomain": "survival-smp",
    "base_domain": "coritan.gg",
    "mode": "minecraft_java",
    "listen_port": null,
    "upstream": "203.0.113.10:25565",
    "location_id": 3,
    "join_address": "survival-smp.coritan.gg",
    "ns_ready": true
  }
}
```

`join_address` is what players type, with the edge port after the name for a TCP service. `route_id` identifies the join address in the other join address calls. The call takes these fields:

| Field | Values | Default | What it sets |
|---|---|---|---|
| `subdomain` | 3–28 characters that follow the [name rules](#name-rules) | Required | The name under the join domain. |
| `mode` | `minecraft_java`, `minecraft_bedrock` or `tcp_port` | `minecraft_java` | The edition. `tcp_port` is a TCP service. |
| `upstream_service_id` | The `service_id` of one of your servers | Required | The server to forward to. `GET /api/v1/client/servers` returns each server's `service_id`. |
| `upstream_port` | A port on that server | The server's primary port | For `tcp_port`, the port on the server that the edge forwards to. Java and Bedrock join addresses follow the server's primary port, so leave it out for them. |
| `display_name` | Up to 100 characters | The subdomain | The join address's name in the dashboard. |
| `proxy_protocol` | `true` or `false` | `false` | A PROXY protocol version 2 header on each connection. Send `false` for Bedrock. |
| `real_ip` | `true` or `false` | `true`, or `false` when `proxy_protocol` is `true` | The player's address in the Java handshake ([What your server receives](/docs/proxies/join-addresses/#what-your-server-receives)). |

The call also takes the server list fields, but does not save them. Set the server list entry afterwards ([Customise the server list entry](/docs/proxies/join-addresses/server-list/#with-the-api)).

A name that another join address holds answers `400` with `Subdomain 'survival-smp' is already in use on coritan.gg`, and a name that breaks a rule answers `400` with the rule, such as `Subdomain cannot be only digits`. A body without `upstream_service_id` answers `400` with `upstream_service_id or upstream_host/port required`.

## API

- `POST /api/v1/gameproxy/availability`: Claim check on the write primary so create/delete races cannot lie (https://www.coritan.com/docs/api/reference/client/game-proxy/#op-post-api-v1-gameproxy-availability)
- `POST /api/v1/gameproxy/name-suggestions`: Name suggestions (https://www.coritan.com/docs/api/reference/client/game-proxy/#op-post-api-v1-gameproxy-name-suggestions)
- `POST /api/v1/gameproxy/routes`: Order path preferred; this creates a route bound to an existing service for power users (https://www.coritan.com/docs/api/reference/client/game-proxy/#op-post-api-v1-gameproxy-routes)
