# Static IP gates

> Source: https://truto.one/docs/guides/integrations/static-ip-gates/

Some vendors only accept API calls from IP addresses on an allow-list. A **static gate** is a proxy with fixed IP addresses: point a connected account at one, add the gate's addresses to the vendor's allow-list, and the account's calls reach the vendor from those addresses.

:::skip-reading{cli="truto gates list -o json" skill="truto-cli"}
:::

## Who runs the gate

A gate record in Truto only tells Truto where to send an account's calls. The proxy behind it is a separate machine, and there are two ways to get one:

- **Ask Truto.** Truto runs a static gate with fixed IP addresses. Contact Truto support with the environments that need it: Truto adds the gate to them, and gives you its IP addresses for the vendor's allow-list. It then shows in `GET /static-gate` and in the connection link dialog, and you skip [step 1](#1-create-a-gate).
- **Run your own.** Host a proxy on a machine with fixed IP addresses that follows the [gate contract](#the-gate-contract), then create the record for it in [step 1](#1-create-a-gate).

## How a gate works

A static gate is a record in one environment with a `name` and a `domain`. The domain is the gate's host, and the fixed IP addresses belong to the machine behind it. An account uses a gate when its context has `truto_static_gate_id` set to the gate's ID.

For such an account, Truto sends the request to `https://<domain>` instead of the vendor, with two headers:

| Header | Value |
|---|---|
| `Truto-StaticGate-Token` | The gate's token, which proves the request came from Truto |
| `Truto-Target-URL` | The vendor URL the request is for |

The gate sends the request on to the target URL and returns the vendor's response. The request keeps its `Authorization` header, so the gate sees the account's credentials: only use a gate you or Truto run.

### The gate contract

A gate you host must, for every request it receives:

1. Check that `Truto-StaticGate-Token` equals the gate's token, and refuse the request otherwise. Without this check, anyone who finds the domain can use your IP addresses.
2. Send the request to the URL in `Truto-Target-URL`, with the same method and body and every header except the `Truto-*` ones.
3. Return the vendor's response as it is: status code, headers and body. Truto reads it as if the vendor had answered directly, so a changed status or body breaks pagination, error handling and token refreshes.

The gate's outbound traffic must leave from the fixed IP addresses you put on the vendor's allow-list.

These calls go through the gate:

- the account's Unified API and Proxy API calls, including those sync jobs make;
- OAuth token calls for the authorization code and client credentials grants, token refreshes, and JWT bearer exchanges.

OAuth 1.0a requests can't go through a gate.

A connection only uses a gate of its own team. If the gate is deleted, or the ID names another team's gate, API calls go straight to the vendor.

## 1. Create a gate

Skip this step for a gate Truto added for you. Gates have no screen in the dashboard. Create one with the API or the CLI, in the environment whose accounts will use it. Truto returns the gate's token in `api_token` in the create response only. Store it: the gate needs it to check `Truto-StaticGate-Token`, and no later response includes it. To choose the token yourself, send `api_token` in the request.

:::code-tabs

```bash label="cURL"
curl -X POST "https://api.truto.one/static-gate" \
  -H "Authorization: Bearer <API token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "us-east egress",
    "domain": "gate.example.com",
    "environment_id": "<environment_id>"
  }'
```

```bash label="CLI"
truto gates create -b '{"name":"us-east egress","domain":"gate.example.com","environment_id":"<environment_id>"}'
```

:::

`domain` is a host name without `https://`. You can change `name` and `domain` later with `PATCH /static-gate/{id}`. The token can't be changed: create a new gate instead.

## 2. Put a connection behind the gate

A connection gets its gate when it's created. A browser can't set or change `truto_static_gate_id` itself.

- **Connection link from the dashboard**: in the **Get connection link** dialog, open **Advanced settings** and pick the gate under **Static gate (optional)**. The picker shows only when the environment has a gate. A re-authorize link starts with the connection's current gate.
- **Link token from the API**: pass `truto_static_gate_id` to `POST /link-token`. The gate must belong to the link's environment, or the request returns `404`.
- **Account created through the API**: put `truto_static_gate_id` in the `context` of `POST /integrated-account`.

```bash
curl -X POST "https://api.truto.one/link-token" \
  -H "Authorization: Bearer <API token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "acme",
    "truto_static_gate_id": "<static_gate_id>"
  }'
```

Then add the gate's IP addresses to the vendor's allow-list for that customer.

## Methods that require a gate

An integration's connection method can set `requires_static_gate`. A connection with that method must have a gate: Truto refuses to connect it without one, and refuses a `PATCH` that would take the gate away. See [`requires_static_gate`](/docs/guides/integrations/generated-key-pairs#requires_static_gate).

Some integrations send every account's calls through Truto's own gate. That's part of the integration's base configuration, not something an environment or account override can turn on or off. An account with its own `truto_static_gate_id` uses its own gate instead.

## Manage gates

:::code-tabs

```bash label="cURL"
curl "https://api.truto.one/static-gate" \
  -H "Authorization: Bearer <API token>"

curl -X DELETE "https://api.truto.one/static-gate/<static_gate_id>" \
  -H "Authorization: Bearer <API token>"
```

```bash label="CLI"
truto gates list
truto gates delete <static_gate_id>
```

:::

Before you delete a gate, move its accounts to another one: an account whose gate is gone calls the vendor directly, and a vendor with an allow-list refuses it. See the [Static gates API reference](/docs/api-reference/admin/static-gates).
