> ## 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.

# Conventions

> Base URL, identifiers, timestamps, request and response shapes, and the absence of pagination.

These rules hold across the whole API. The [API Reference](/api-reference)
documents each endpoint's fields; this page is what those fields have in common.

## Base URL

```
https://api.autogrowth.farm/v1
```

Everything programmatic lives under `/v1`. Requests and responses are JSON, except
[`POST /content/upload`](/api-reference/content/upload-content), which takes
`multipart/form-data`.

## Identifiers

Rows are addressed by **UUID** — v7, so they sort roughly by creation time. Devices,
accounts, executions, content, collections, prompts, scripts and tags all use them.

Two things are not UUIDs:

* **Automation IDs** are slugs, like `warmup-instagram` or `post-to-instagram`.
  They come from [`GET /automations`](/api-reference/automations/list-automations)
  and are the same for every organization.
* **Input keys** inside `inputValues` are the `id` values of an automation's
  `inputs`, like `account` or `caption`.

<Info>
  A malformed UUID is never a server error. Sent as a path parameter it answers
  `404`, and sent as a filter it returns an empty list — the guard runs before the
  query, so a bad ID looks like a missing row rather than a `500`.
</Info>

## Timestamps

ISO 8601 in UTC, with milliseconds:

```
2026-09-01T12:00:00.000Z
```

Send the same shape. On [`GET /executions`](/api-reference/executions/list-executions),
an unparseable `from` or `to` is **ignored** rather than rejected, so a typo widens
your result set instead of failing.

## Absent means null

Most resources drop their null columns instead of sending them. On an execution, an
account, a content item, a script or a collection, a field you do not see is a field
that is not set:

```json theme={"dark"}
{
	"id": "0198f8a0-1111-7aaa-8ccc-3e5d7c9b1a24",
	"type": "automation",
	"automationId": "warmup-instagram",
	"status": "pending",
	"scheduledAt": "2026-09-01T14:00:00.000Z"
}
```

No `result`, no `logs`, no `workflowId` — because none are set, not because they
were withheld. Empty arrays survive: `tags: []` means an empty list, and that field
is always present.

**Devices are the exception.** They send explicit `null`s for unset fields such as
`udid`, `battery` and `stream`, so read them with a null check rather than a
presence check.

## References expand on reads, not on writes

The read endpoints replace a reference with an object **under the same key**. On
[`GET /executions`](/api-reference/executions/list-executions), `scriptId`,
`deviceId`, `accountId` and `containerId` come back as objects:

```json theme={"dark"}
{
	"deviceId": { "id": "0198f8a0-…", "name": "Rack 3 — iPhone 12" },
	"accountId": { "id": "0198f8a1-…", "username": "yourhandle", "platform": "instagram" }
}
```

The write endpoints — create, bulk create, cancel — do not expand. There the same
keys are plain UUID strings.

<Warning>
  A reference whose target row was deleted stays a **raw UUID string** on a read.
  An execution outlives the phone it ran on, so `deviceId` on an old run may be a
  string where a newer run has an object. Check the type before reading `.name`.
</Warning>

Accounts behave the same way: `deviceId`, `deviceIds` and `containerId` expand on
`GET /accounts`, and stay UUID strings on the pause and resume responses.

## No pagination

List endpoints return the **whole** set. There is no cursor, no page parameter and
no total count.

The only limiting parameter anywhere is `limit` on
[`GET /executions`](/api-reference/executions/list-executions). It defaults to 50,
must be a positive integer, and has no matching offset — so it caps the newest N
and cannot walk backwards through the rest. Narrow with `deviceId`, `status`,
`from` and `to` instead.

Plan for full result sets. A fleet with 40 phones and a busy library returns large
responses on `GET /devices` and `GET /content`.

## List ordering

| Endpoint                     | Order                       |
| ---------------------------- | --------------------------- |
| `GET /devices`               | Newest first                |
| `GET /accounts`              | Newest first                |
| `GET /accounts/{id}/history` | Oldest first                |
| `GET /executions`            | Newest scheduled first      |
| `GET /content`               | Newest first                |
| `GET /collections`           | Newest first                |
| `GET /collections/{id}`      | Its content, oldest first   |
| `GET /scripts`               | Most recently updated first |
| `GET /prompts`               | Most recently updated first |
| `GET /tags`                  | By name                     |
| `GET /automations`           | Fixed catalog order         |

## Status codes on success

| Status | When                                                                                 |
| ------ | ------------------------------------------------------------------------------------ |
| `200`  | A read, an update, or a delete — deletes answer `{"success": true}`                  |
| `201`  | Something was created: an execution, a bulk batch, a collection, a prompt, an upload |

A `201` from [`POST /executions/bulk`](/api-reference/executions/bulk-create-executions)
does **not** mean everything worked. Each item is validated on its own, and the
response splits them into `created` and `failed` — a batch where all 100 items
failed still answers `201` with an empty `created`.

## Rate limits

There are none today. No endpoint counts your requests, and no response carries a
rate-limit header.

That is a description of the current server, not a guarantee. Write clients that
back off on a `429` even though nothing returns one yet, and do not build a design
that only works if unlimited request volume stays free.
