# API overview

> The /v1 REST API: base URL, bearer authentication, the two kinds of API key, errors, and rate limits.

Source: https://docs.generalinput.com/api



Everything a script, a self-hosted instance, or an outside agent needs lives under one path prefix on the cloud API. The same routes back the `geni` CLI, so anything the CLI can do, a key with the matching scope can do too.

```
https://cloud.generalinput.com/v1
```

Requests and responses are JSON. Send `Content-Type: application/json` on any request with a body. Ids are opaque strings with a type prefix (`cred_…`, `wf_…`, `skl_…`), except operation ids, which are UUIDs.

## Authentication [#authentication]

Every request carries a bearer token:

```http
Authorization: Bearer gi_org_…
```

Three kinds of bearer reach `/v1`. The prefix tells them apart.

| Bearer           | Prefix     | Acts as                                 | Get one from                                                    |
| ---------------- | ---------- | --------------------------------------- | --------------------------------------------------------------- |
| Personal API key | `gi_sk_`   | You, in one workspace                   | Settings, API keys ([details](/api/api-keys))                   |
| Platform API key | `gi_org_`  | The workspace, for an instance or agent | Settings, API keys ([details](/api/api-keys))                   |
| CLI session      | `geni_rs_` | You, switchable between workspaces      | [`geni login`](/cli/auth/login). Never paste one into a script. |

A key is bound to one workspace when it is created. There is no header to switch workspaces; create a key per workspace instead. The MCP endpoint is the exception to all of this: it takes OAuth access tokens only, never a key. See [MCP](/mcp).

## Scopes [#scopes]

Each route is mounted behind one scope, and one route ([issue a token](/api/vault/issue-token)) adds a second on top of its mount. A key holds a set of scopes chosen at creation, and the two key kinds hold disjoint sets: a personal key can never hold `docs:read`, and a platform key can never hold `workflows:manage`. The [API keys](/api/api-keys) page has the full table. Every endpoint page states its scope in an **Access** line.

A CLI session is unscoped. It passes every scope gate because it is the interactive, revocable credential of a signed-in member.

## Errors [#errors]

Errors are a JSON object carrying an `error` string.

```json
{ "error": "Insufficient scope" }
```

Two cases add to that shape: a `426` also carries `minVersion` and `clientVersion`, and a hosted service's own error passes straight through with whatever shape that service uses.

| Status | Meaning                                                                                                                      |
| ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `400`  | The body or query failed validation. Most routes name the field; the vault routes answer with one canned message either way. |
| `401`  | Missing, unknown, or revoked bearer. The reason is never disclosed.                                                          |
| `402`  | The workspace has no credit balance. Hosted services only.                                                                   |
| `403`  | The key lacks the scope, the route needs a CLI session, or the workspace lacks a required feature.                           |
| `404`  | Not found, or not visible to the member the key acts as. The two are deliberately indistinguishable.                         |
| `409`  | The resource changed underneath you. Refetch and retry (skills).                                                             |
| `426`  | Only for `geni`. The CLI is below the minimum supported version. Other clients are never gated on version.                   |
| `429`  | Rate limited. Slow down and retry after a moment.                                                                            |
| `500`  | An unexpected server failure. A few hosted services also surface a rejected request this way, not as a `400`.                |
| `503`  | The authentication service could not be reached. Retry.                                                                      |

## Rate limits [#rate-limits]

Authenticated `/v1` traffic is limited per client IP:

| Traffic                                       | Limit         |
| --------------------------------------------- | ------------- |
| Authenticated requests                        | 1200 / minute |
| Device-code login endpoints (unauthenticated) | 600 / minute  |

The limit is sized for agent fan-out (a run posting to fifty channels in parallel fits comfortably) while still capping a runaway loop. Revoking a key cuts off a misbehaving client immediately, without waiting for the window.

The [MCP endpoint](/mcp) carries the same ceiling but is not counted per IP: a hosted connector calls from its own servers, so one address fronts every customer using it. Its bucket follows the caller's own credential instead.

## What is where [#what-is-where]

| Area                                      | Prefix                                                  | Scope                                        |
| ----------------------------------------- | ------------------------------------------------------- | -------------------------------------------- |
| [Integration docs](/api/integrations)     | `/v1/integrations`, `/v1/operations`                    | `docs:read`                                  |
| [Vault](/api/vault)                       | `/v1/credentials`, `/v1/connections`                    | `vault:read`, `vault:connect`, `vault:token` |
| [Hosted services](/api/platform)          | `/v1/platform/<service>`                                | `platform:use`                               |
| [Workflows and apps](/api/workflows)      | `/v1/workflows`                                         | `workflows:manage`                           |
| [Skills](/api/skills)                     | `/v1/skills`                                            | `skills:manage`                              |
| [Projects](/api/projects)                 | `/v1/projects`                                          | `projects:manage`                            |
| [Memory](/api/memory)                     | `/v1/memory`                                            | `memory:manage`                              |
| [Triggers](/api/triggers)                 | `/v1/triggers`                                          | `triggers:manage`                            |
| [CLI session endpoints](/api/cli-session) | `/v1/auth`, `/v1/workspaces`, `/v1/exec`, `/v1/browser` | CLI session only                             |

## A first request [#a-first-request]

Create a platform API key with the `docs:read` scope, then search the operation catalog:

```sh
curl -s "https://cloud.generalinput.com/v1/operations?q=send%20slack%20message" \
  -H "Authorization: Bearer $GI_API_KEY"
```

```json
{
  "results": [
    {
      "service": "slack",
      "serviceTitle": "Slack",
      "operations": [
        {
          "id": "4c21e1ee-4d54-4413-a4f2-80a80dff4c99",
          "title": "Send a Message",
          "description": "Sends a message to a channel, DM, or group conversation.",
          "score": 0.91
        }
      ]
    }
  ]
}
```

<Cards>
  <Card title="API keys" href="/api/api-keys">
    Personal versus platform keys, the scope table, and how to create and revoke
    them.
  </Card>

  <Card title="Vault" href="/api/vault">
    List credentials, connect new ones through a hosted link, and fetch a fresh
    token.
  </Card>

  <Card title="Hosted services" href="/api/platform">
    Search, scrape, geocode, email, generate images, and call a model.
  </Card>
</Cards>
