# Connect Railway

An account of Railway is connected once, on the instance, by a person who then gives missions access to its resources; the agent never receives the credential. Deploy, redeploy, read logs and manage variables of Railway projects through the official Railway MCP server and GraphQL API.

One account is one Railway account reached through the company Railway app, or a workspace token (or an account token) pasted by hand; every tool runs either way. One resource is one project. Its owner labels each project with an environment (prod, staging, preview or dev); deploys and variable changes are held per label, and a project without a label can only be inspected.

Permissions, presets and tools of this service: [Railway plugin](/plugins/railway).

## How to connect

On the instance: Accounts → Connect an account, then pick the service. A company app, when the service needs one, is registered once by an administrator under Settings → Administration → Company apps, or set in the configuration (`providers.<id>.*`, see [configuration keys](/docs/configuration/keys)). Every instance registers its own app: this gateway never uses an app shared with another one.

### The usual way: through your company's OAuth app at the service (one click once the app is registered)

Scopes asked: `openid`, `offline_access`, `workspace:member`.

### Another way: with a workspace token pasted by hand

1. Sign in to Railway as the account the agents will act as; a shared service account that is a member of the workspace is fine.
2. Open Account settings, Tokens, and create a token with the workspace selected: a workspace token reaches that workspace only.
3. An account token, created with No workspace selected, works too but reaches every workspace of the account. A project token is not accepted: it reaches one environment of one project.
4. Railway tokens carry no permission choice: the token reaches what its workspace or account allows, and Vaubaan decides who may do what.
5. Copy the token and paste it here. Railway shows it once.

Where: [https://railway.com/account/tokens](https://railway.com/account/tokens)

What the connect page asks:

- Workspace token (secret): A workspace token is recommended; an account token works too. A project token is not accepted.

The service's documentation: [https://docs.railway.com/integrations/api](https://docs.railway.com/integrations/api)

## Disconnecting

In your Railway account settings, open Authorized Apps, select your company's app and choose Revoke Access: every token it holds stops working at once.

## Registration notes from the plugin

From the plugin's [README](https://github.com/vaubaan/gateway/blob/main/plugins/railway/README.md), written for the people who register the app or create the token.

### Credential — how the company registers its app

1. Railway → Workspace → **Developer → Auth apps → New app**
   (`https://railway.com/workspace/developer/apps/new`). Name it after your
   gateway instance. Leave **Device Flow off**.
2. Redirect URI: the instance callback the gateway shows in
   Settings → Administration → Company apps (Railway requires `https://`; for loopback use `https://127.0.0.1`,
   never `localhost`).
3. Copy `client_id` and `client_secret` into the instance configuration
   under the Railway provider app (Settings → Administration → Company apps, or the
   mounted config file). The host uses them for the authorization-code flow and
   for refresh; the plugin reads neither.

The gateway then runs the authorization-code flow declared by
`railwayCredentialSpec`: scopes `openid offline_access workspace:member`,
PKCE S256, `prompt=consent` (needed for a refresh token) and the RFC 8707
resource indicator `https://mcp.railway.com` on both the authorization and
token requests — the MCP server refuses tokens minted without it. Access
tokens last one hour; the host refreshes them with the standard
`refresh_token` grant declared by the spec (`refreshable: true`), including
after an `UpstreamAuthError`.

### Credential, the other way: a workspace token

Without a company OAuth app — or beside it — an account is connected with a
Railway token pasted by hand (`railwayTokenCredential`, `src/token.ts`),
created under railway.com/account/tokens. Every tool runs with it: discovery
and the four variable tools on the GraphQL API, the ten tools of Railway's MCP
server with the token sent as `Authorization: Bearer` to
`https://mcp.railway.com/`.

| Token                                  | Reach                          | Offered                     | MCP server                                                |
| -------------------------------------- | ------------------------------ | --------------------------- | --------------------------------------------------------- |
| **workspace token** (workspace chosen) | that workspace only            | yes, recommended            | accepted — checked against the real service on 2026-10-14 |
| account token ("No workspace")         | every workspace of the account | yes, second choice          | not checked; Railway documents the server for OAuth only  |
| project token                          | one environment of one project | no: a resource is a project | —                                                         |

A shared service account that is a member of the workspace is fine: Vaubaan
decides who may do what.

**Check at paste** (`probeRailwayToken`): `query { me { name email } }`
answers for an account token only (named by its email). Otherwise the
projects the token reaches answer for a workspace token, named by its
workspace (`the vaubaan workspace`) or, when the workspace name is not
exposed, by its projects. Neither answers → the token is refused.

**Discovery**: the projects the token reaches, then — account token only —
each workspace of `me.workspaces`. A workspace token has no `me`; its refusal
ends the list.

**When the MCP server refuses a pasted token** (401 or 403), the plugin asks
the API about the same token (`explainMcpRefusal`): refused there too → a
rejected credential (401, the account "Needs reconnecting"); still accepted →
the MCP server does not take this kind of token: a 403 naming it ("refused
this account token … reconnect with a workspace token"), and the connection
stays usable for the variable tools. `railway__whoami` may say little with a
workspace token, which has no user behind it.

Removing the account tells the person to delete the token under Account
settings → Tokens. ADR
[`token-connections-beside-the-integration-app`](https://github.com/vaubaan/gateway/blob/main/docs/decisions/token-connections-beside-the-integration-app.md)
(amendment of 2026-10-14).
