# Create or update an account

**PUT** `/api/embed/v1/accounts/{accountId}`

Creates an account by your ID, or updates it. A field you leave out is left as it is.

Needs a partner key: `Authorization: Bearer gre_…`, from [Platform › Partner keys](https://docs.gorunner.io/embed/platform-screens#partner-keys). Call it from your server only.

Embed · Self-hosted with an Embed license

## Parameters

### Path

- `accountId` · Required · string: Your ID for the customer’s account: 1 to 255 bytes of UTF-8, case-sensitive. URL-encode it: it may contain `/`.

### Body

- `name` · Optional · string: Up to 200 characters. Needed to create.
- `turnOff` · Optional · array of strings or null: Switch IDs turned off for the whole account. Replaces the list; `null` clears it.
- `pieces` · Optional · array of strings or null: The apps the account may use, within the installation’s list. `null`: the installation’s list. `[]`: only branch and loop. Unknown IDs are refused by name.
- `limits.concurrentRuns` · Optional · integer or null: Runs at once: a number, 0 for no limit, `null` for the installation’s default. Runs over it wait in line.

**What are the rarely used parameters?**

- `assistant.monthlyTokens` · Optional · integer or null: Keel’s monthly allowance. `null`: the installation’s default.
- `aiSignIn` · Optional · object or null: The account’s own AI app sign-in address, `{"address": "https://…"}`. `null` clears it. Its website must already be allowed for this account. Needs v2026.10.24 or later: an older installation ignores it, so check that the answer has `aiSignIn`. See [Let customers sign in AI apps](https://docs.gorunner.io/embed/ai-app-sign-in).

## Safe to send again

It is keyed by your ID, so the same request twice leaves the same account. Every field is checked before anything is written.

To set `aiSignIn` on a new account: create it with a `name`, [allow its website](https://docs.gorunner.io/embed/api/websites/add-account-website), then set the address. A later change to the address doesn’t affect AI apps already connected.

## Responses

- 200 OK

  Updated.

- 201 Created

  Created, with its default workspace.

- 400 `VALIDATION_ERROR`

  A field isn’t valid, such as an unknown app in `pieces` (`details.unknown`) or one outside the installation’s list (`details.notAllowed`). Nothing is written.

- 403 `ORG_LIMIT`

  Creating the account would pass the license’s organization limit. Nothing is made.

- 403 `WORKSPACE_LIMIT`

  Creating the account’s default workspace would pass the license’s workspace limit. Nothing is made.

- 409 `ACCOUNT_DELETED`

  The account is deleted. Restore it first, within 30 days. Even clearing `aiSignIn` is refused.

- 409 `BUSY`

  Something else held what this call needs, and it gave up waiting. Nothing changed. Try again.

- 422 `SIGN_IN_ADDRESS_NOT_ALLOWED`

  `aiSignIn.address` isn’t on a website allowed for this account (`details.origin`). Add the website first. Nothing is written.

- 500 `INTERNAL_ERROR`

  Something failed on the installation’s side. Try again.

- 503 `PIECES_UNAVAILABLE`

  The installation’s app list couldn’t be read. Nothing was written.

Any call can also answer: [`401 UNAUTHORIZED`](https://docs.gorunner.io/errors#UNAUTHORIZED) · [`403 EMBED_NOT_LICENSED`](https://docs.gorunner.io/errors#EMBED_NOT_LICENSED) · [`403 IP_NOT_ALLOWED`](https://docs.gorunner.io/errors#IP_NOT_ALLOWED) · [`413 PAYLOAD_TOO_LARGE`](https://docs.gorunner.io/errors#PAYLOAD_TOO_LARGE) · [`415 INVALID_CONTENT_TYPE`](https://docs.gorunner.io/errors#INVALID_CONTENT_TYPE) · [`429 RATE_LIMITED`](https://docs.gorunner.io/errors#RATE_LIMITED) · [`503 BUSY`](https://docs.gorunner.io/errors#BUSY). See [Errors](https://docs.gorunner.io/embed/api#errors).

Request

**curl**

```bash
GORUNNER="https://automations.example.com"
GORUNNER_EMBED_KEY="YOUR_PARTNER_KEY"  # gre_…
ACCOUNT_ID="acct-bakery"

URL="$GORUNNER/api/embed/v1/accounts/$ACCOUNT_ID"
curl -X PUT "$URL" \
  -H "Authorization: Bearer $GORUNNER_EMBED_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Bakery Ltd",
    "limits": {
      "concurrentRuns": 5
    }
  }'
```

**JavaScript**

```js
const GORUNNER = "https://automations.example.com";
const KEY = process.env.GORUNNER_EMBED_KEY; // gre_…
const ACCOUNT_ID = encodeURIComponent("acct-bakery");

const res = await fetch(
  `${GORUNNER}/api/embed/v1/accounts/` +
    `${ACCOUNT_ID}`,
  {
    method: "PUT",
    headers: {
        Authorization: `Bearer ${KEY}`,
        "Content-Type": "application/json",
      },
    body: JSON.stringify({
        name: "Bakery Ltd",
        limits: { concurrentRuns: 5 },
      }),
  },
);
const { data } = await res.json();
```

**Python**

```python
import os, requests
from urllib.parse import quote

GORUNNER = "https://automations.example.com"
KEY = os.environ["GORUNNER_EMBED_KEY"]  # gre_…
ACCOUNT_ID = quote("acct-bakery", safe="")

res = requests.put(
    f"{GORUNNER}/api/embed/v1/accounts/"
    f"{ACCOUNT_ID}",
    headers={"Authorization": f"Bearer {KEY}"},
    json={
      "name": "Bakery Ltd",
      "limits": {"concurrentRuns": 5},
    },
)
data = res.json()["data"]
```

Response · 201 Created

```json
{
  "data": {
    "id": "acct-bakery",
    "name": "Bakery Ltd",
    "status": "active",
    "turnOff": [],
    "pieces": null,
    "assistant": {
      "monthlyTokens": null
    },
    "limits": {
      "concurrentRuns": 5
    },
    "workspaces": 1,
    "members": 0,
    "suspendedAt": null,
    "suspendedReason": null,
    "deletedAt": null,
    "purgeAfter": null,
    "createdAt": "2026-10-20T08:00:00Z",
    "updatedAt": "2026-10-25T09:00:00Z",
    "organizationId": "e9d2b6f1-3a7c-4d05-b8e4-1c6f2a9d7b30",
    "aiSignIn": {
      "address": null
    }
  }
}
```

**Every field**

### Body

- `name` · Optional · string: Required to create. Left out or null: left as it is.
- `turnOff` · Optional · array of strings or null: Switch ids off for this account, replacing the list; null or `[]` clears it. An id that does not exist is 400 `VALIDATION_ERROR` (`details.unknown`).
- `pieces` · Optional · array of strings or null: The account’s own app list: app ids, within the installation’s list; null inherits the installation’s. An id no app has is 400 `VALIDATION_ERROR` (`details.field` `pieces`, `details.unknown`), one the installation’s list leaves out the same with `details.notAllowed` — nothing is stored.
- `assistant` · Optional · object
  - `monthlyTokens` · Optional · integer or null
- `limits` · Optional · object
  - `concurrentRuns` · Optional · integer or null: The account’s runs at once: a number, 0 for no limit, null for the installation’s default. Left out, it is left as it is. The last change counts, from here or from Platform › Organizations.
- `aiSignIn` · Optional · object or null: This account’s own AI app sign-in address, for a partner whose people sign in on a domain of their own (a reseller’s site). Left out, it is left as it is; null, `{"address": null}` or `{"address": ""}` clears it, and the account’s people go to the installation’s address again. `{}` and an address of spaces are refused (400). On a deleted account the PUT is 409 `ACCOUNT_DELETED`, even to clear it. Changing or clearing the address never affects AI apps already connected. An older server ignores this field on an existing account: check that the answer carries `aiSignIn`.
  - `address` · Required · string or null: The page in your product where this account’s people sign in. Surrounding spaces are trimmed. The installation’s address’s rules apply, and a few more: https, or http on `localhost`, `127.0.0.1` or `[::1]`; no `#fragment`, no user name or password, at most 2,000 characters, not the installation’s own host; no invisible or direction-changing characters; a host name or an IP address, with no trailing dot, and a valid port; a query without `;`, where every `%` starts an escape, and with no parameter that starts with `gr_` in any letter case (`gr_request` is the installation’s). A problem with the address itself is 400 `VALIDATION_ERROR` (`details.field` `aiSignIn.address`). It is stored and answered in a standard form: the host in lower case, an international name in its `xn--` form, no default port. Your query parameters are kept, but may be reordered and re-encoded when `gr_request` is added. Its website (scheme, host and port) must already be allowed for THIS account: one of the installation’s allowed websites (a wildcard covers subdomains at any depth, never the bare domain) or exactly one of this account’s own websites, added with `POST /accounts/{accountId}/websites` (a PUT there replaces the whole list), never another account’s. Otherwise 422 `SIGN_IN_ADDRESS_NOT_ALLOWED` with `details.origin`. Either way nothing is stored, not even an account this PUT would have created: create the account with a `name`, add its website, then set the address. If that website is removed later, the stored address is kept but not used until the website is added again.

### Response · 200 OK, 201 Created

- `data` · object
  - `id` · string
  - `name` · string
  - `status` · string: One of `active`, `suspended`, `deleted`.
  - `turnOff` · array of strings
  - `pieces` · array of strings or null: Null inherits the installation’s list.
  - `assistant` · object
    - `monthlyTokens` · integer or null
  - `limits` · object
    - `concurrentRuns` · integer or null: How many runs this account may have executing at once. Null = the installation’s default (Platform › Settings › General), 0 = no limit. Runs over it wait in line and start in order; none is refused for it.
  - `workspaces` · integer
  - `members` · integer: Distinct people with access.
  - `suspendedAt` · time or null
  - `suspendedReason` · string or null
  - `deletedAt` · time or null
  - `purgeAfter` · time or null
  - `createdAt` · time
  - `updatedAt` · time
  - `organizationId` · UUID: The installation’s own id, for support.
  - `aiSignIn` · object
    - `address` · URL or null: This account’s own AI app sign-in address: where an AI app’s sign-in sends this account’s people instead of the installation’s address. Null: they go to the installation’s address (Platform › What customers get › Developers). It is used only while its website is still one of the installation’s allowed websites or one of this account’s own; otherwise the installation’s address is used, or, when the installation has none, no AI app can sign in to the account (`mcp.signInFrom` says which).
  - `mcp` · object or null: Only on GET /accounts/{accountId}: the account’s MCP addresses as its people find them on the MCP page. Null while Tokens or MCP is off for the account, or it is deleted. While an AI app can sign in to it (AI app sign-in on, and a sign-in address to send its people to: the account’s own `aiSignIn.address` or the installation’s), they are the account’s own addresses and `signIn` is true; otherwise the plain addresses tokens use.
    - `build` · URL or null: The address that builds and manages workflows; null while MCP building (`mcpBuild`) is off.
    - `tools` · URL: The address that calls the published workflows.
    - `signIn` · boolean: Whether an AI app can connect by signing in: through the account’s own `aiSignIn.address` while its website is allowed, or the installation’s AI app sign-in address.
    - `signInFrom` · string or null: Whose address a sign-in uses: `account` (its own `aiSignIn.address`) or `installation`; null when `signIn` is false. `installation` while `aiSignIn.address` is set means its website is no longer allowed for the account. One of `account`, `installation`, `null`.

Related: [Get an account](https://docs.gorunner.io/embed/api/accounts/get-account) · [Switches](https://docs.gorunner.io/embed/switches) · [Accounts and people](https://docs.gorunner.io/embed/accounts-and-people)

Source: https://docs.gorunner.io/embed/api/accounts/put-account
