GoRunner docs
Search or askSearch⌘K

Partner API

Provision your customers, sign them in, and run their workflows from your server.

Embed · Self-hosted with an Embed license

Keys

Every call from your server sends a partner key: Authorization: Bearer gre_…. An API token (grt_…) is refused. Make keys in Platform › Partner keys; you see each once, and up to 20 work at once.

A key can be limited to up to 20 addresses or ranges (203.0.113.0/24). That limits who may use the key, not which websites show the embed. Rotate a key with a grace period. Never send one to a browser: the page gets a login code instead.

Each call is checked in this order: the license (EMBED_NOT_LICENSED), the key (UNAUTHORIZED, IP_NOT_ALLOWED), then the limits.

The address

Every path starts with /api/embed/v1, on your installation’s own address: the one your people open, where /embed.js is. It is never on api.gorunner.io. These pages write it as https://automations.example.com.

/v1 is the Partner API’s version, gorunner-embed/1. See Versions and upgrades.

Bodies are JSON; a write with another Content-Type is refused (415 INVALID_CONTENT_TYPE). Times are in UTC.

bash
GORUNNER="https://automations.example.com"
GORUNNER_EMBED_KEY="YOUR_PARTNER_KEY"  # gre_…, from Platform › Partner keys

curl -X POST "$GORUNNER/api/embed/v1/login-codes" \
  -H "Authorization: Bearer $GORUNNER_EMBED_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user": { "id": "ada-123", "name": "Ada Lovelace" },
    "account": { "id": "acct-bakery", "name": "Bakery Ltd" },
    "role": "editor"
  }'

Answers

A success is {"data": …}; a list adds pagination. A failure is {"error": …}. Branch on code; message is for a person and may change.

details says more where there is more: field (the input that is wrong), unknown (IDs that don’t exist), reason (why a session ended) or switch (the switch that is off).

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "These switches don't exist: stepData.",
    "details": { "field": "turnOff", "unknown": ["stepData"] }
  }
}

The IDs are yours

Accounts, workspaces, people and managed connections are named by your own IDs: 1 to 255 bytes of UTF-8, no control characters, compared byte for byte. An ID may contain /, so URL-encode it in a path: /accounts/acct%2F42.

An email is only ever contact information: it never identifies a person or links two. A PUT creates or updates by your ID, so sending it twice leaves the same state.

Idempotency

These POSTs take an Idempotency-Key (up to 255 characters), so a retry after a dropped connection never acts twice: suspend, resume and restore an account; allow a website; install, enable and disable a workflow; cancel its waiting runs; cancel a run and run it again; disconnect an account’s or every AI app.

When you send the key againYou get
With the same request, within 24 hoursThe first answer again, with Idempotency-Replayed: true
With a different request422 VALIDATION_ERROR
While the first is still running409 CONFLICT: retry shortly
After the first failedA new try: only a success is kept

Keys belong to the partner key that sent them. A login code and a token take none: an unused code runs out in a minute, and a stored answer would keep a token for a day, so a retry makes a second one that you can revoke. Starting a connection sign-in and signing a person in for an AI app take none either.

Lists

Lists page with a cursor, each in the order its page states. Send limit (1 to 100, 50 by default) and, for the next page, the cursor the last page gave. nextCursor is null on the last page; a page that comes back full can carry a cursor with nothing after it, and the next page is then empty. Send a cursor back exactly as it came.

json
{ "data": [ … ], "pagination": { "limit": 50, "nextCursor": "…" } }

Limits

Counted forA minuteSetting
One partner key3,000 requestsEMBED_API_RATE_LIMIT
One customer account: the one in the path, or a login code’s300 requestsEMBED_ACCOUNT_RATE_LIMIT
One person of one account20 login codesEMBED_PERSON_RATE_LIMIT
One line of frame sessions4 renewalsEMBED_RENEW_RATE_LIMIT
AI app sign-in requests, per key600 requestsEMBED_OAUTH_REQUEST_RATE_LIMIT

The windows are fixed minutes, shared by every server of the installation. A request one limit refuses isn’t counted by the others, so one customer over its limit uses up nothing of your key’s. One address may also send at most four times API_RATE_LIMIT a minute to each server.

Over a limit, the answer is 429 RATE_LIMITED with Retry-After. X-RateLimit-Limit and X-RateLimit-Remaining describe the tightest window, and X-RateLimit-Scope names it: key, account, person or session.

Throttle your own login-code route for each person too. Every frame that opens, and every renewal through your page, is one login code; a frame renews by itself for up to 12 hours, so most renewals never reach your server.

The record

Every call with your key leaves a row in the audit log of the account it was about, except reading an AI app sign-in request: the route, the status, the request ID, the key, and whether it was a replay. Never a body. The changes themselves, such as an account created or a role changed, leave rows of their own.

Errors

CodeStatusWhen
VALIDATION_ERROR400, 422An input is wrong; details names it. 422 for an idempotency key used with another request.
INVALID_CONTENT_TYPE415A Content-Type other than application/json.
PAYLOAD_TOO_LARGE413A body over 5 MiB.
UNAUTHORIZED401No key, or one revoked, past its rotation or mistyped.
EMBED_NOT_LICENSED403The license doesn’t include Embed, or the installation has no valid license.
IP_NOT_ALLOWED403The key’s addresses don’t include yours.
FEATURE_OFF403A switch the call needs is off for the account; details.switch names it.
ORG_LIMIT, WORKSPACE_LIMIT403A new account or workspace would pass the license’s limit.
PIECE_NOT_ALLOWED403, 422An app the account may not use.
NOT_FOUND404No account, workspace or person with that ID.
CONFLICT409The change clashes with the current state; the message says how.
ACCOUNT_DELETED409The account is deleted. Restore it first, within 30 days.
ACCOUNT_NOT_DELETED, RESTORE_EXPIRED409Restoring an account that isn’t deleted, or whose 30 days are over.
ACCOUNT_SUSPENDED409Running a run again in a suspended account.
WORKFLOW_NOT_PUBLISHED409Running a run again whose workflow is off.
WEBSITE_EXISTS409The website is already on the list.
TOKEN_LIMIT409The workspace already has 25 tokens that work.
DEFAULT_WORKSPACE409An account’s default workspace can’t be deleted.
INVALID_ORIGIN, TOO_MANY_WEBSITES422A website can’t be allowed as written, or the list is full: 200 on yours, 20 for one customer.
CONNECTION_MISMATCH422A connection given for a template’s slot is for another service.
RATE_LIMITED429A limit is used up. See Retry-After.
INTERNAL_ERROR500Something failed on the installation. Try again.
PIECES_UNAVAILABLE503The apps allowed couldn’t be read. Nothing changed.

Each endpoint lists the codes it answers. For every code, see Error codes.

The frame’s own routes

Five routes take no partner key, and the SDK and the frame call them for you. Check the installation can be embedded is public. Start a session swaps a login code for a session (gri_…), which can renew itself, sign out or turn more switches off.

Every endpoint 59

By name, path or method, such as runs or POST.

Sign-in 6

Accounts 7

Tokens 3

Websites 7

Workspaces 4

People 6

Connections 6

Workflows 7

Runs 4

Usage 2

AI apps 7