GoRunner docs
Search or askSearch⌘K

Start a sign-in for a new connection

Since v2026.10.8

POST/api/connections/oauth2/start

Starts a sign-in to an app’s service. The connection is made only once the sign-in works.

Needs an API token with the Write permission for connections (connection:write).

Parameters

Body

From an API token: pieceId (or connectionId) and returnUrl.

pieceIdOptional · string

An app that signs in, such as gmail. A trigger app is made under its action app.

nameOptional · string

Up to 255 characters. Left blank: “Service · account” once the account is known. Not with connectionId.

connectionIdOptional · UUID

Sign a saved connection in again instead. It keeps its name and app. Not one the partner manages.

returnUrlOptional · URL

Where the browser comes back to: https (http only for localhost), on the installation’s own dashboard or one of its allowed websites. Up to 2,048 characters.

What are the rarely used parameters?
configOptional · object

Your own app instead of the installation’s: clientId, clientSecret, scope.

modeOptional · string

Leave it out, or send redirect. GoRunner’s own pages send popup or tab, which a token can’t.

returnToOptional · string

Ignored for a token. GoRunner’s own pages use it.

The sign-in

Send the person who should sign in to authorizeUrl: the service’s own consent page. The link works once, for 10 minutes. Give it to nobody else.

When the service sends the browser back, GoRunner makes the connection, then redirects to returnUrl with status=connected&connectionId=…&attemptId=…, or status=error&reason=…. No token is ever in that address, and your own query is kept.

A sign-in that is refused, fails or is abandoned makes nothing.

The return address

Its origin must be exactly the installation’s own dashboard, one of its allowed websites, or, for an embedded customer’s workspace, one of that account’s own. A wildcard entry allows none. On Cloud, the dashboard is the only choice. See Your customers’ connections.

It takes no Idempotency-Key: a retry starts a new sign-in, and an unused one runs out.

Responses

  • 200OK

    From a token: attemptId, authorizeUrl and expiresAt.

  • The app, the name, returnUrl or connectionId isn’t usable as written, or a name or config came with connectionId. details.field names it.

  • The token lacks the Write permission for connections.

  • An AI app signed in with OAuth. Signing in needs a person in a browser, or an API token.

  • The partner manages this connection through its own API, so it can’t be changed here.

  • This workspace may not use the app.

  • The workspace’s account is suspended. Nothing starts until it is resumed.

  • connectionId isn’t in this workspace.

  • The workspace’s account was deleted.

  • The app has nothing to sign in with, or its token address isn’t one this installation sends to.

  • returnUrl isn’t on an allowed website. details.origin names it.

  • Something failed on GoRunner’s side. Try again.

  • Sign-ins aren’t available on this server now, or the allowed websites couldn’t be read. Nothing started.

  • GoRunner couldn’t check which apps this workspace may use. Nothing changed. Try again.

  • The account’s state couldn’t be read. Nothing started. Try again.

Any call can also answer: 401 UNAUTHORIZED · 401 TOKEN_ENDED · 403 TOKEN_SURFACE_OFF · 403 FEATURE_OFF · 403 EMBED_NOT_ALLOWED · 403 EMBED_NOT_AVAILABLE · 413 PAYLOAD_TOO_LARGE · 415 INVALID_CONTENT_TYPE · 429 RATE_LIMITED · 503 BUSY · 503 PAUSED · 503 SERVICE_UNAVAILABLE. See Errors.

Request

GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…"  # needs Write for connections

curl -X POST "$GORUNNER/api/connections/oauth2/start" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "pieceId": "gmail",
    "name": "Gmail · Ada",
    "returnUrl": "https://app.bakery.example/integrations/done?user=u_123"
  }'

Response · 200 OK

json
{
  "data": {
    "attemptId": "Zq3XbN0iJm7dQe8TfR2wLg",
    "authorizeUrl": "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&client_id=…&code_challenge=…&code_challenge_method=S256&prompt=consent&redirect_uri=…&response_type=code&scope=…&state=…",
    "expiresAt": "2026-10-25T09:10:00Z"
  }
}
Every field

The body takes one of 2 forms.

Body, from GoRunner’s own pages

pieceIdRequired · string

An app that signs in (declares OAuth2, or the generic oauth2). A trigger’s app is made under its action app.

nameOptional · string

Optional. Blank: "{Service} · {account}" once the account is known.

configOptional · object

Only an own app’s clientId, clientSecret, scope (and the generic app’s authUrl, tokenUrl). Sealed into the attempt.

modeOptional · string

One of popup, tab. Default popup.

returnToOptional · string

Required with mode: tab: the dashboard path to come back to (one leading /, no host).

Body, from an API token

pieceIdOptional · string

Required for a new connection: an app that signs in (declares OAuth2, or the generic oauth2). A trigger’s app is made under its action app (gmailtrigger → gmail). With connectionId, optional and must be that connection’s.

nameOptional · string

A new connection’s name. Blank: "{Service} · {account}" once the account is known. Not with connectionId.

connectionIdOptional · UUID

Sign this existing connection (in the caller’s workspace) in again; it keeps its name and its app. Not a partner-managed one.

returnUrlRequired · URL

Where the browser comes back to: an absolute https address (http only for localhost), no user info or #fragment, whose origin is exactly an allowed website (or, with an API token on POST /api/connections/oauth2/start, this installation’s own app address). Its own query is kept; status, connectionId/reason and attemptId are set.

modeOptional · string

One of redirect. Default redirect.

configOptional · object

Your own app instead of the installation’s: clientId, clientSecret, scope (and the generic app’s authUrl, tokenUrl), and an app’s baseUrl. Sealed into the attempt. Not with connectionId.

Response · 200 OK, to GoRunner’s own pages

dataobject
attemptIdstring

Names this sign-in in the launch handshake and in the status route.

launchUrlURL

Point the popup opened on the click here. It is useless on its own: the page asks its opener for bindSecret and goes nowhere without it.

bindSecretstring

Single-use. Give it only to the popup, by postMessage to apiOrigin, when it asks. Never put it in a URL or storage.

apiOriginstring

The exact origin the launch and result pages are served on. Accept messages only from the popup AND this origin.

providerstring

The provider key of the app’s declared authorize URL (google, github, …); empty for the generic oauth2 app.

redirectUriURL

The callback this server is redirected back to ({PUBLIC_API_URL}/api/oauth2/callback). Register exactly this with the provider. It is a browser endpoint returning HTML; you never call it.

expiresAttime

When this attempt stops being usable (15 minutes after the press).

modestring

How the window asked to sign in: tab is opened with POST /api/connections/oauth2/attempts/{attemptId}/go.

One of popup, tab.

Response · 200 OK, to an API token

dataobject
attemptIdstring

Names this sign-in: read it with the same credential, and it comes back on returnUrl.

authorizeUrlURL

The provider’s consent page, complete: its state is single-use and bound to this attempt. Send the person who should sign in there, and nobody else.

expiresAttime

When the link stops working (10 minutes after the start).