> ## Documentation Index
> Fetch the complete documentation index at: https://docs.autogrowth.farm/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> AutoGrowth runs social accounts on real phones. Everything a page describes happens on a physical device in the customer's own fleet, never through a social platform's API.
> The Companion is a macOS desktop app that runs on the customer's own Mac and manages the phones over USB — iPhones through WebDriverAgent, GrapheneOS phones through ADB and scrcpy. Nothing runs while the Companion is closed.
> The Companion builds, signs and installs WebDriverAgent on each iPhone itself. Never tell a customer to sign WebDriverAgent by hand in Xcode; Xcode is a prerequisite the Companion drives, not a step the customer performs.
> Autogrowth MAX is a one-time, invite-only lifetime license bought from the organization's credit balance, not a subscription tier. It is required to run any automation, workflow or Incubator program. Without a price set for that organization, the purchase is refused.
> An automation is one of AutoGrowth's built-in definitions; a script is a graph the customer builds in the Script Builder. The Script Builder is locked platform-wide, so never present building a script as something a customer can do today.
> An API key is a bare 64-character hex string with no prefix. It is shown once, at creation, is sent as `Authorization: Bearer <key>`, and can never be read back.
> An API key sees only the key owner's own accounts, content, collections, prompts, scripts and executions. Devices and tags are organization-scoped, and a non-owner sees only the devices assigned to them or carrying one of their tags. The automation catalog is global.
> There is no pagination. List endpoints return the whole set; the only limiting parameter anywhere is `limit` on `GET /v1/executions`, which defaults to 50 and has no offset to page with.
> Remote control and the live screen are dashboard-only, over WebSocket and MJPEG. Neither is part of the public API.
> The MCP server is hosted at https://mcp.autogrowth.farm and authenticates with OAuth. It is not an npm package and there is nothing to install locally. Its tools inherit exactly the scope of the API key behind them.
> Android support is partial and GrapheneOS-only: those phones appear in the fleet and can be watched and remote-controlled, but they cannot run scripts or automations.
> Never document Hi Katie or any persona feature. When a page and the code disagree, the code wins — check a claim against the source before repeating it.

# Authentication

> Create an API key, send it as a bearer token, and understand what a key is allowed to see.

Every `/v1` endpoint authenticates with a developer API key sent as a bearer token:

```http theme={"dark"}
Authorization: Bearer a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90
```

There is no other way in. No cookies, no query parameter, no unauthenticated
endpoint except the spec itself.

## Creating a key

<Steps>
  <Step title="Open Settings → Developer API">
    In the dashboard, go to **Settings**, then the **Developer API** section. You
    need the **Manage API keys** permission — organization owners have it
    automatically, other roles need it granted. See [Team](/guides/team).
  </Step>

  <Step title="Name the key and create it">
    The name is for you. It shows up in the key list with the key's last-used
    time, so name keys after what will use them.
  </Step>

  <Step title="Copy it now">
    The raw key is shown **once**, at creation. AutoGrowth stores only a SHA-256
    hash of it and cannot show it again. Lose it and your only option is to delete
    the key and create another.
  </Step>
</Steps>

## Key format

A key is 64 hexadecimal characters — 32 random bytes, hex-encoded — with **no
prefix** and no separator:

```
a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90
```

Treat it as a password. Anything holding it can do everything you can do to your
own rows, including scheduling runs that post to your accounts.

<Info>
  A key created by authorizing an [MCP client](/mcp/overview) looks different —
  it is generated by the MCP server rather than the dashboard, and you never see
  it. It behaves identically everywhere else.
</Info>

## Revoking a key

Delete it in **Settings → Developer API**. The next request with it answers `401`.
Deletion is the only revocation: keys do not expire, and they are not rotated for
you.

The list shows every key in your organization, so a teammate with the **Manage API
keys** permission can revoke yours, and you can revoke a stale one left behind by
someone who has moved on — including a key created by an MCP client.

## What a key can see

<Warning>
  A key is **not** an organization-wide credential. It inherits the role and the
  permissions of the user who created it, and most resources are then narrowed to
  that user's own rows.
</Warning>

This is the single most surprising thing about the API, so it is worth stating
flatly.

| Resource                                                     | What the key sees                                                                                                                     |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Accounts, content, collections, prompts, scripts, executions | **Only the key owner's own rows.** Not the organization's. An owner's key does not see a teammate's accounts.                         |
| Devices, tags                                                | Organization-scoped. An owner sees every phone; anyone else sees only phones assigned to them or carrying one of their assigned tags. |
| Automations                                                  | Global. The same catalog for every organization, minus admin-only entries.                                                            |
| Stats                                                        | Mixed, and documented per field on [`GET /stats/dashboard`](/api-reference/stats/get-dashboard-stats).                                |

Two consequences worth planning around:

* **A key cannot act on behalf of a team.** If three people each own accounts, you
  need three keys, or one user who owns everything.
* **The dashboard's "view as" is not available here.** The dashboard lets an owner
  look at a teammate's rows; a key always sees its own owner's.

The key's organization is resolved from its owner's **current** membership, not
from the organization the key was created in. A user who moves to another
organization takes their keys' access with them.

## Auth error responses

| Status | Body                                                                        | Cause                                                                        |
| ------ | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `401`  | `{"error": "Authorization required. Use: Authorization: Bearer <api-key>"}` | No `Authorization` header, or one that does not start with `Bearer `         |
| `401`  | `{"error": "Invalid API key"}`                                              | The key matches no row — wrong, mistyped, or deleted                         |
| `403`  | `{"error": "Account disabled or not found"}`                                | The key is valid but its owner's user is disabled or gone                    |
| `403`  | `{"error": "User not in any organization"}`                                 | The owner belongs to no organization                                         |
| `403`  | `{"error": "Organization not found"}`                                       | The owner's organization row is missing                                      |
| `403`  | `{"error": "Insufficient permissions"}`                                     | The key is fine; the owner's role lacks the permission the endpoint requires |

`401` means the credential is wrong — retrying with the same key will never work.
`403` means the credential is fine and the account behind it is not allowed to do
this; retrying will not help either, and it is not a reason to re-authenticate.

<Info>
  A key deleted mid-request loses. The middleware re-checks the key when it stamps
  `lastUsedAt` and answers `401` if the row went away, so a revocation cannot be
  outrun by a request already in flight.
</Info>

## Handling auth in your client

```javascript theme={"dark"}
async function autogrowthFetch(path, init = {}) {
	const res = await fetch(`https://api.autogrowth.farm/v1${path}`, {
		...init,
		headers: {
			...init.headers,
			Authorization: `Bearer ${process.env.AUTOGROWTH_API_KEY}`,
		},
	});

	if (res.status === 401) {
		// Wrong or deleted key. Retrying changes nothing.
		throw new Error('AutoGrowth API key is missing or invalid');
	}
	if (res.status === 403) {
		// Valid key, not allowed. Surface it to an operator.
		const { error, code } = await res.json();
		throw new Error(`AutoGrowth refused the request: ${code ?? error}`);
	}
	if (!res.ok) {
		const body = await res.json();
		throw new Error(body.error ?? `AutoGrowth request failed (${res.status})`);
	}

	return res;
}
```
